2019-12-30 12:16:05 +01:00
|
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
2020-01-01 19:44:41 +01:00
|
|
|
{-# LANGUAGE ConstraintKinds #-}
|
2020-01-01 17:27:39 +01:00
|
|
|
{-# LANGUAGE FlexibleContexts #-}
|
2020-05-17 16:30:56 +02:00
|
|
|
module SJW.Compiler (
|
2019-12-30 12:16:05 +01:00
|
|
|
main
|
|
|
|
) where
|
|
|
|
|
2020-05-17 16:30:56 +02:00
|
|
|
import SJW.Source (Source(..), HasSource, Path)
|
2020-01-01 19:44:41 +01:00
|
|
|
import Control.Monad.IO.Class (MonadIO(..))
|
2020-01-09 21:51:24 +01:00
|
|
|
import Control.Monad.RWS (ask, gets)
|
2020-01-01 17:27:39 +01:00
|
|
|
import Data.Map ((!))
|
2020-01-01 19:44:41 +01:00
|
|
|
import qualified Data.Map as Map (member)
|
2019-12-30 12:16:05 +01:00
|
|
|
import Data.Text (Text, cons)
|
2020-01-01 19:44:41 +01:00
|
|
|
import qualified Data.Text as Text (null, unlines)
|
2020-05-17 16:30:56 +02:00
|
|
|
import SJW.Dependencies as Dependencies (Failable, solve)
|
|
|
|
import SJW.Module (Environment, Log, Module(..), Modules(..))
|
|
|
|
import qualified SJW.Module as Module (parse, register)
|
|
|
|
import SJW.Module.File (File(..), variables)
|
|
|
|
import qualified SJW.Module.File as File (header, footer)
|
2020-01-01 19:44:41 +01:00
|
|
|
|
2020-05-17 16:30:56 +02:00
|
|
|
type Compiler m = (HasSource m, Log m, Environment m, MonadIO m, Failable m)
|
2019-12-30 12:16:05 +01:00
|
|
|
|
|
|
|
indent :: [Text] -> [Text]
|
2020-01-01 17:27:39 +01:00
|
|
|
indent = fmap indentLine
|
|
|
|
where
|
|
|
|
indentLine t
|
|
|
|
| Text.null t = t
|
|
|
|
| otherwise = cons '\t' t
|
|
|
|
|
2020-01-09 21:51:24 +01:00
|
|
|
include :: Environment m => Path -> m [Text]
|
|
|
|
include path = do
|
|
|
|
File {isMain, imports, payload} <- gets (file . (! path) . modules)
|
2020-01-01 17:27:39 +01:00
|
|
|
let (names, values) = unzip $ variables imports
|
2020-01-01 19:44:41 +01:00
|
|
|
return $ File.header isMain path names : indent payload ++ File.footer values
|
2020-01-01 17:27:39 +01:00
|
|
|
|
2020-01-09 21:51:24 +01:00
|
|
|
scan :: Compiler m => Bool -> Path -> m ()
|
|
|
|
scan isMain modulePath = do
|
|
|
|
alreadyLoaded <- gets (Map.member modulePath . modules)
|
2020-01-01 17:27:39 +01:00
|
|
|
if alreadyLoaded then return () else load
|
|
|
|
where
|
|
|
|
load :: Compiler m => m ()
|
|
|
|
load = do
|
2020-01-09 21:51:24 +01:00
|
|
|
newModule <- Module.parse isMain modulePath
|
|
|
|
Module.register modulePath newModule
|
|
|
|
mapM_ (scan False) $ dependencies newModule
|
2020-01-01 17:27:39 +01:00
|
|
|
|
|
|
|
body :: Compiler m => m [Text]
|
|
|
|
body = do
|
2020-01-09 21:51:24 +01:00
|
|
|
sortedPath <- Dependencies.solve =<< dependenciesGraph
|
2020-01-01 17:27:39 +01:00
|
|
|
includes <- concat <$> mapM include sortedPath
|
|
|
|
return $ "var modules = {};" : includes
|
2020-01-09 21:51:24 +01:00
|
|
|
where
|
|
|
|
dependenciesGraph = gets (fmap dependencies . modules)
|
2019-12-30 12:16:05 +01:00
|
|
|
|
2020-01-01 17:27:39 +01:00
|
|
|
main :: Compiler m => m Text
|
|
|
|
main = do
|
2020-05-17 16:30:56 +02:00
|
|
|
Source {mainModule} <- ask
|
2020-01-09 21:51:24 +01:00
|
|
|
scan True mainModule
|
2020-01-01 17:27:39 +01:00
|
|
|
codeBody <- body
|
|
|
|
return . Text.unlines $ openOnLoad : indent codeBody ++ [closeOnLoad]
|
2019-12-30 12:16:05 +01:00
|
|
|
where
|
2020-01-01 17:27:39 +01:00
|
|
|
openOnLoad = "window.addEventListener('load', function() {"
|
|
|
|
closeOnLoad = "});"
|