LslPlus-0.4.0: src/Language/Lsl/Internal/CompilationServer.hs
{-# OPTIONS_GHC -XFlexibleContexts -XNoMonomorphismRestriction -XTemplateHaskell #-}
module Language.Lsl.Internal.CompilationServer where
import Control.Monad
import Control.Monad.Instances
import Control.Monad.Error
import Data.Data
import Data.Either
import Data.Generics
import Data.Generics.Extras.Schemes
import qualified Data.Map as M
import Language.Lsl.Internal.Compiler
import Language.Lsl.Internal.Load(loadScript)
import Language.Lsl.Internal.Pragmas
import Language.Lsl.Syntax
import Language.Lsl.Parse
import Language.Lsl.Internal.Util
import Language.Lsl.Internal.DOMProcessing
import Language.Lsl.Internal.DOMSourceDescriptor
import qualified Language.Lsl.Internal.XmlCreate as E
import Text.XML.HaXml.Parse hiding (fst3,snd3,thd3)
import Language.Lsl.Internal.SerializationGenerator
import Language.Lsl.Internal.DOMCombinators
import Language.Lsl.Internal.SerializationInstances
import System.Directory
import System.FilePath(replaceExtension)
-- a module or a script
data CodeElement = CodeElement { codeElementName :: String, codeElementText :: String }
deriving (Show,Eq)
data CompilationCommand = Init (Bool,[(String,String)],[(String,String)]) -- compile all the modules and scripts (writing out .lsl files!)
| UpdateModule (String,String) -- update the given module, recompiling affected scripts (and writing out .lsl files!)
| UpdateScript (String,String) -- compile the given script (writing out the .lsl file)
| RemoveScript (String,String)
| RemoveModule (String,String)
| CheckModule CodeElement -- check the module
| CheckScript CodeElement -- check the script
deriving (Show,Eq)
data CompilationResponse = FullSourceValidation ([CompilationStatus],[CompilationStatus])
| ModuleResponse (LModule,[ErrInfo])
| ScriptResponse (LSLScript,[ErrInfo])
data EPKind = EPFunc | EPHandler deriving (Show,Eq)
data EPSummary = EPSummary { epKind :: EPKind, epName :: String, epType :: LSLType, epParams :: [(String,LSLType)] }
deriving (Show,Eq)
data GlobalSummary = GlobalSummary { globalName :: String, globalType :: LSLType }
deriving (Show,Eq)
data CompilationStatus = CompilationStatus { csName :: !String, csInfo :: !(Either [ErrInfo] ([GlobalSummary],[EPSummary])) }
deriving (Show,Eq)
data ErrInfo = ErrInfo (Maybe TextLocation) String deriving (Show,Eq)
toErrInfo (Nothing,msg) = ErrInfo Nothing msg
toErrInfo (Just srcCtx, msg) = ErrInfo (Just $ srcTextLocation srcCtx) msg
parseErrorToErrInfo pe =
let (x,y) = (sourcePosToTextLocation $ errorPos pe,
showErrorMessages "or" "unknown parse error"
"expecting" "unexpected" "end of input" (errorMessages pe))
in ErrInfo (Just x) y
sourcePosToTextLocation pos = (TextLocation line col line col name)
where line = sourceLine pos
col = sourceColumn pos
name = sourceName pos
gsummary :: Data a => a -> [Either GlobalSummary EPSummary]
gsummary = everythingBut (False `mkQ` string `extQ` srcContext) (++) [] ([] `mkQ` fsum `extQ` gsum)
where gsum (GDecl (Ctx _ (Var n t)) _) = [Left $ GlobalSummary n t]
fsum (FuncDec fnm t parms) = [Right $ EPSummary EPFunc (ctxItem fnm) t (map ((\ (Var n t) -> (n,t)) . ctxItem) parms)]
string :: String -> Bool
string _ = True
srcContext :: SourceContext -> Bool
srcContext _ = True
ssummary :: [Ctx State] -> [EPSummary]
ssummary = concatMap go
where go (Ctx _ (State nm hs)) = map (hsum (ctxItem nm)) hs
hsum snm (Ctx _ (Handler hnm parms _)) =
EPSummary EPHandler (snm ++ "." ++ ctxItem hnm) LLVoid (map ((\ (Var n t) -> (n,t)) . ctxItem) parms)
moduleSummary :: LModule -> ([GlobalSummary],[EPSummary])
moduleSummary (LModule gdefs _) = partitionEithers (gsummary gdefs)
scriptSummary :: CompiledLSLScript -> ([GlobalSummary],[EPSummary])
scriptSummary (CompiledLSLScript _ gs fs ss) = (map sumg gs,map sumf fs ++ ssummary ss)
where sumg (GDecl (Ctx _ (Var n t)) _) = GlobalSummary n t
sumf (Ctx _ (Func (FuncDec fnm t parms) _)) =
EPSummary EPFunc (ctxItem fnm) t (map ((\ (Var n t) -> (n,t)) . ctxItem) parms)
validationSummary :: (AugmentedLibrary,[(String,Validity CompiledLSLScript)]) -> ([CompilationStatus],[CompilationStatus])
validationSummary (ms,ss) = (msum,ssum)
where msum = map mkMSum ms
ssum = map mkSSum ss
mkMSum (nm, Left errs) = CompilationStatus nm $ Left (map toErrInfo errs)
mkMSum (nm, Right (lmodule,_)) = CompilationStatus nm $ Right (moduleSummary lmodule)
mkSSum (nm, Left errs) = CompilationStatus nm $ Left (map toErrInfo errs)
mkSSum (nm, Right cscript) = CompilationStatus nm $ Right (scriptSummary cscript)
data Tup3 a b c = Tup3 a b c
data Tst = Tst (Double,Int,Char)
data Tst1 = Tst1 (Tup3 Double Int Char)
$(deriveJavaRep ''TextLocation)
$(deriveJavaRep ''LSLType)
$(deriveJavaRep ''EPKind)
$(deriveJavaRep ''EPSummary)
$(deriveJavaRep ''GlobalSummary)
$(deriveJavaRep ''CodeElement)
$(deriveJavaRep ''CompilationCommand)
$(deriveJavaRep ''ErrInfo)
$(deriveJavaRep ''CompilationStatus)
$(deriveJavaRep ''State)
$(deriveJavaRep ''GlobDef)
$(deriveJavaRep ''Handler)
$(deriveJavaRep ''Ctx)
$(deriveJavaRep ''Expr)
$(deriveJavaRep ''SourceContext)
$(deriveJavaRep ''Pragma)
$(deriveJavaRep ''LSLScript)
$(deriveJavaRep ''Statement)
$(deriveJavaRep ''Component)
$(deriveJavaRep ''Func)
$(deriveJavaRep ''FuncDec)
$(deriveJavaRep ''Var)
$(deriveJavaRep ''LModule)
$(deriveJavaRep ''CompilationResponse)
$(deriveJavaRep ''Tup3)
$(deriveJavaRep ''Tst)
$(deriveJavaRep ''Tst1)
data CState = CState {
optimize :: Bool,
modulePaths :: M.Map String String,
scriptPaths :: M.Map String String,
modules :: M.Map String (Validity (LModule,ModuleInfo)),
scripts :: M.Map String (Validity CompiledLSLScript) }
emptyCState = CState { optimize = False, modulePaths = M.empty, scriptPaths = M.empty, modules = M.empty, scripts = M.empty }
mkCState (opt,mpaths,spaths) (lib,scripts) =
CState { optimize = opt, modulePaths = M.fromList mpaths, scriptPaths = M.fromList spaths, modules = M.fromList lib, scripts = M.fromList scripts }
toLib = libFromAugLib . M.toList
handler :: CState -> String -> IO (CState, String)
handler s0 input = case parse elemDescriptor input of
Left s -> return $ (s0, E.emit "error" [] [showString (E.xmlEscape s)] "")
Right Nothing -> return $ (s0, E.emit "error" [] [showString ("unexpected root element")] "")
Right (Just command) -> handleCommand s0 command
handleCommand _ (Init srcInfo) = do
compilationResult <- compileAndEmit srcInfo
return (mkCState srcInfo compilationResult, xmlSerialize Nothing (FullSourceValidation $ validationSummary compilationResult) "")
handleCommand cs (UpdateScript scriptInfo) = do
(id,result) <- loadScript (toLib $ modules cs) scriptInfo
let cs' = cs { scripts = M.insert id result (scripts cs) }
renderScriptsToFiles (optimize cs') [(id,result)] [scriptInfo]
return (cs',xmlSerialize Nothing (FullSourceValidation $ validationSummary (M.toList $ modules cs', M.toList $ scripts cs')) "")
handleCommand cs (UpdateModule minfo) = do -- this doesn't quite do what it says it does (yet)
let srcInfo = (optimize cs, M.toList (M.insert (fst minfo) (snd minfo) (modulePaths cs)), M.toList (scriptPaths cs))
handleCommand cs (Init srcInfo)
handleCommand cs (RemoveModule minfo) = do
let srcInfo = (optimize cs, M.toList (M.delete (fst minfo) (modulePaths cs)),M.toList (scriptPaths cs))
handleCommand cs (Init srcInfo)
handleCommand cs (RemoveScript scriptInfo) = do
let cs' = cs { scriptPaths = M.delete (fst scriptInfo) (scriptPaths cs), scripts = M.delete (fst scriptInfo) (scripts cs) }
let summary = FullSourceValidation $ validationSummary (M.toList (modules cs'), M.toList (scripts cs'))
let filePath = replaceExtension (snd scriptInfo) ".lsl"
exists <- doesFileExist filePath
when exists $ removeFile filePath
return (cs',xmlSerialize Nothing summary "")
handleCommand cs (CheckModule (CodeElement name text)) =
let (m, errs) = alternateModuleParser name text
lib1 = compileLibrary
(M.toList
(M.insert name m
(M.fromList
[ (n,m) | (n,Right m) <- libFromAugLib $ M.toList (modules cs)])))
errs' = map parseErrorToErrInfo errs ++ case lookup name lib1 of
Nothing -> []
Just (Right _) -> []
Just (Left errs) -> map toErrInfo errs
in return (cs,xmlSerialize Nothing (ModuleResponse (m,errs')) "")
handleCommand cs (CheckScript (CodeElement name text)) =
let (s, errs) = alternateScriptParser name text
lib = libFromAugLib $ M.toList (modules cs)
errs' = map parseErrorToErrInfo errs ++ case compileLSLScript' lib s of
Left errs -> map toErrInfo errs
_ -> []
in return (cs,xmlSerialize Nothing (ScriptResponse (s,errs')) "")
compilationServer :: IO ()
compilationServer = processLinesSIO emptyCState "quit" handler
codeGen loc pkg = do
setCurrentDirectory loc
saveReps pkg $ $(collectReps [''CompilationResponse,''CompilationCommand]) pkg