LslPlus-0.3.5: src/Language/Lsl/Syntax.hs
{-# OPTIONS_GHC -XDeriveDataTypeable -XTypeSynonymInstances -XFlexibleContexts -XGeneralizedNewtypeDeriving
-XTemplateHaskell -XNoMonomorphismRestriction -XFlexibleInstances #-}
-- | Defines the abstract syntax tree for LSL (and LSL Plus extensions).
module Language.Lsl.Syntax (
-- Types
Expr(..),
Var(..),
FuncDec (..),
Func(..),
LModule(..),
Component(..),
Statement(..),
LSLType(..),
Handler(..),
State(..),
GlobDef(..),
LSLScript(..),
Validity,
Global(..),
TextLocation(..),
SourceContext(..),
Ctx(..),
CompiledLSLScript(..),
Library,
AugmentedLibrary,
CodeErr,
CtxVar,
CtxName,
CtxExpr,
CtxStmt,
ModuleInfo,
Codebase(..),
-- Values
fromMCtx,
ctxItems,
nullCtx,
ctxVr2Vr,
findFunc,
moduleFromScript,
findState,
predefFuncs,
findFuncDec,
goodHandlers,
libFromAugLib,
isTextLocation,
isCastValid,
compileLSLScript,
compileLSLScript',
compileLibrary,
VState,
emptyValidationState,
rewriteCtxExpr,
rmCtx) where
import Language.Lsl.Internal.Type(Component(..),LSLType(..),lslTypeString)
import Language.Lsl.Internal.Constants(isConstant,findConstType)
import Language.Lsl.Internal.EventSigs(simpleLslEventDescriptors)
import Language.Lsl.Internal.FuncSigs(funcSigs)
import Language.Lsl.Internal.AccessGenerator(genAccessorsForType,genMAccessorsForType)
import Language.Lsl.Internal.Pragmas(Pragma(..))
import Data.Generics
import Data.Generics.Extras.Schemes
import Data.Data(Data,Typeable)
import Data.List(find,sort,sortBy,nub,foldl',nubBy,deleteFirstsBy)
import qualified Data.Map as M
import Data.Maybe(isJust,isNothing)
import Language.Lsl.Internal.Util(ctx,findM,lookupM,filtMap,throwStrError)
import Control.Monad(when,foldM,MonadPlus(..))
import Control.Monad.Error(MonadError(..),Error(..))
import qualified Control.Monad.State as S(State)
import Control.Monad.State hiding(State)
import Debug.Trace
--trace1 s v = trace (s ++ show v) v
data TextLocation = TextLocation { textLine0 :: Int, textColumn0 :: Int, textLine1 :: Int, textColumn1 :: Int, textName :: String }
deriving (Show,Typeable,Data)
data SourceContext = SourceContext { srcTextLocation :: TextLocation, srcPreText :: String, srcPostTxt :: String, srcPragmas :: [Pragma] }
deriving (Show,Typeable,Data)
isTextLocation (Just (TextLocation _ _ _ _ _)) = True
isTextLocation _ = False
rmCtx :: Data a => a -> a
rmCtx = everywhere (mkT doNullify)
where doNullify :: Maybe SourceContext -> Maybe SourceContext
doNullify _ = Nothing
-- | A wrapper that can associate a source code context with a value (e.g. a syntax value).
data Ctx a = Ctx { srcCtx :: Maybe SourceContext, ctxItem :: a } deriving (Show,Typeable,Data)
instance Functor Ctx where
fmap f (Ctx c v) = (Ctx c $ f v)
ctxItems = map ctxItem
fromMCtx :: Maybe (Ctx a) -> Maybe a
fromMCtx = fmap ctxItem
nullCtx :: a -> Ctx a
nullCtx = Ctx Nothing
funcNames = map (ctxItem.funcName)
ctxVr2Vr :: (Ctx a,b) -> (a,b)
ctxVr2Vr (Ctx _ name,c) = (name,c)
type CtxVar = Ctx Var
-- | An LSL variable (a name, and a type).
data Var = Var { varName :: String, varType :: LSLType } deriving (Show,Typeable,Data)
type CtxName = Ctx String
-- | An LSL function declaration (the function name and type information, without associated
-- statements.
data FuncDec = FuncDec { funcName :: CtxName, funcType :: LSLType, funcParms :: [CtxVar] }
deriving (Show,Typeable,Data)
type CtxStmt = Ctx Statement
type CtxFunc = Ctx Func
-- | An LSL function definition (return type, parameters and statements.
data Func = Func FuncDec [CtxStmt] deriving (Show,Typeable,Data)
-- | An LSL Plus module, which is a separately 'compiled' unit that can contain
-- both global variables and functions, but not states or handlers.
data LModule = LModule [GlobDef] [CtxVar]
deriving (Show,Typeable,Data)
type CtxExpr = Ctx Expr
-- | An LSL expression.
data Expr = IntLit Int
| FloatLit Double
| StringLit String
| ListExpr [CtxExpr]
| VecExpr CtxExpr CtxExpr CtxExpr
| RotExpr CtxExpr CtxExpr CtxExpr CtxExpr
| KeyLit String
| Call CtxName [CtxExpr]
| Add CtxExpr CtxExpr
| Sub CtxExpr CtxExpr
| Mul CtxExpr CtxExpr
| Div CtxExpr CtxExpr
| Mod CtxExpr CtxExpr
| Get (CtxName,Component)
| Set (CtxName,Component) CtxExpr
| BAnd CtxExpr CtxExpr
| BOr CtxExpr CtxExpr
| Xor CtxExpr CtxExpr
| ShiftL CtxExpr CtxExpr
| ShiftR CtxExpr CtxExpr
| And CtxExpr CtxExpr
| Or CtxExpr CtxExpr
| Equal CtxExpr CtxExpr
| NotEqual CtxExpr CtxExpr
| Lt CtxExpr CtxExpr
| Le CtxExpr CtxExpr
| Gt CtxExpr CtxExpr
| Ge CtxExpr CtxExpr
| IncBy (CtxName,Component) CtxExpr
| DecBy (CtxName,Component) CtxExpr
| MulBy (CtxName,Component) CtxExpr
| DivBy (CtxName,Component) CtxExpr
| ModBy (CtxName,Component) CtxExpr
| PostInc (CtxName,Component)
| PostDec (CtxName,Component)
| PreInc (CtxName,Component)
| PreDec (CtxName,Component)
| Not CtxExpr
| Neg CtxExpr
| Inv CtxExpr
| Cast LSLType CtxExpr
| AQString String
| AQInteger String
| AQKey String
| AQFloat String
deriving (Show,Typeable,Data)
-- | An LSL statement.
data Statement = Compound [CtxStmt]
| While CtxExpr Statement
| DoWhile Statement CtxExpr
| For ([CtxExpr]) (Maybe CtxExpr) ([CtxExpr]) Statement
| If CtxExpr Statement Statement
| Decl Var (Maybe CtxExpr)
| NullStmt
| Return (Maybe CtxExpr)
| StateChange String
| Do CtxExpr
| Label String
| Jump String
deriving (Show,Typeable,Data)
isLabel (Label _) = True
isLabel _ = False
-- | An LSL global variable (this is actually not a source level/syntactic entity -- the set of globals
-- for a script is derived after analyzing all included modules.
data Global = GDecl Var (Maybe Expr)
deriving (Show,Typeable,Data)
-- | A global definition (a function, a variable, or a module import statement).
data GlobDef = GV CtxVar (Maybe CtxExpr) | GF (Ctx Func) | GI CtxName [(String,String)] String
deriving (Show,Typeable,Data)
-- | An LSL event handler definition.
data Handler = Handler { handlerName :: CtxName, handlerParams :: [CtxVar], handlerStatements :: [CtxStmt] }
deriving (Show,Typeable,Data)
-- | The set of valid handlers supported by LSL.
goodHandlers :: [(String,[LSLType])]
goodHandlers = simpleLslEventDescriptors
-- | An LSL state definition.
data State = State CtxName [Handler]
deriving (Show,Typeable,Data)
-- | An LSL script.
data LSLScript = LSLScript [GlobDef] [State] deriving (Show,Typeable,Data)
type ModuleInfo = ([Global],[Ctx Func])
-- | A collection of modules.
type Library = [(String,Validity LModule)]
-- | A collection of mouldes, augmented with additional derived information.
type AugmentedLibrary = [(String,Validity (LModule,ModuleInfo))]
data Codebase = Codebase { codebaseLib :: AugmentedLibrary, codebaseScripts :: [(String,Validity CompiledLSLScript)] }
lslFunc (name,t,ts) =
FuncDec (nullCtx name) t (zipWith (\ x y -> nullCtx $ Var [y] x) ts ['a'..])
predefFuncs = map lslFunc funcSigs
findVar name = find (\(Var n _ ) -> n == name)
findType name =
let f Nothing = Nothing
f (Just (Var _ t )) = Just t in f . (findVar name)
findFuncDec name = ctx ("finding function " ++ name) . findM (\ fd -> ctxItem (funcName fd) == name)
findState name = ctx ("finding state " ++ name) . findM (\ (State n _) -> ctxItem n == name)
findFunc name = ctx ("finding function " ++ name) . findM (\ (Func fd _) -> ctxItem (funcName fd) == name)
lookupModule :: String -> Library -> Validity LModule
lookupModule name lib =
case lookup name lib of
Nothing -> throwError [(Nothing, "unknown module")]
Just (Left ((_,s):_)) -> throwError [(Nothing, "invalid library (" ++ s ++ ")")]
Just (Right m) -> return m
-- A description of an error and where to find it in the source.
type CodeErr = (Maybe SourceContext,String)
ctxFromCodeErr = fst
msgFromCodeErr = snd
-- | An error monad for representing validation errors with respect to LSL code.
type Validity a = Either [CodeErr] a
instance Error [CodeErr] where
noMsg = [(Nothing,"")]
strMsg s = [(Nothing,s)]
--------------------
matchTypes LLFloat LLInteger = True
matchTypes dest src = dest == src || (all (`elem` [LLKey,LLString]) [dest,src])
data CompiledLSLScript = CompiledLSLScript {
scriptGlobals :: ![Global],
scriptFuncs :: ![Ctx Func],
scriptStates :: ![State]}
deriving (Show)
data RefPos = RefPos { refPosName :: !String, refPosLine :: !Int, refPosCol :: !Int }
deriving (Show,Eq,Ord)
data ValidationState = ValidationState {
vsLib :: !Library,
vsGlobalRegistry :: !(M.Map String (Maybe SourceContext)),
vsLocalRegistry :: ![M.Map String (Maybe SourceContext)],
vsLocalVars :: ![[Var]],
vsRefs :: !(M.Map RefPos SourceContext),
vsLabels :: ![[String]],
vsModules :: ![String],
vsStates :: ![State],
vsGlobals :: ![Global],
vsFuncs :: ![Ctx Func],
vsErr :: ![CodeErr],
vsWarn :: ![CodeErr],
vsNamesUsed :: [String],
vsGVs :: ![Var],
vsGFs :: ![FuncDec],
vsStateNames :: ![String],
vsEntryPointInfo :: !(LSLType,Bool),
vsBranchReturns :: !Bool,
vsHandlersUsed :: ![String],
vsImports :: ![(String,[(String,String)],String)],
vsContext :: [Maybe SourceContext]
}
emptyValidationState = ValidationState {
vsLib = [],
vsGlobalRegistry = M.empty,
vsLocalRegistry = [],
vsLocalVars = [],
vsRefs = M.empty,
vsLabels = [],
vsModules = [],
vsStates = [],
vsGlobals = [],
vsFuncs = [],
vsErr = [],
vsWarn = [],
vsNamesUsed = [],
vsGVs = [],
vsGFs = [],
vsStateNames = [],
vsEntryPointInfo = (LLVoid,False),
vsBranchReturns = False,
vsHandlersUsed = [],
vsImports = [],
vsContext = []
}
-- generate set'xxx methods for all the record selectors in the above type...
-- warning: all types mentioned in the above declaration must be defined (lexically) prior to
-- the following splice
-- double warning: the 'set' functions generated can only be used in code that
-- lexically follows this splice
$(genAccessorsForType ''ValidationState) -- a Template Haskell splice...
-- this splice requires the above splice, and, because the author of the splice was too lazy to
-- generate type signatures, also requires -XNoMonomorphismRestriction
$(genMAccessorsForType ''ValidationState) -- a Template Haskell splice...
type VState a = S.State ValidationState a
--vsmRegisterGlobal :: Ctx Var -> VState ()
vsmRegisterGlobalName name ctx = get'vsGlobalRegistry >>= put'vsGlobalRegistry . (M.insert name ctx)
vsmRegisterGlobal (Ctx ctx (Var name t)) = vsmRegisterGlobalName name ctx
vsmRegisterFunc (Func (FuncDec (Ctx ctx name) _ _) _) = vsmRegisterGlobalName name ctx
vsmRegisterLocalName name ctx = do
lregistry <- get'vsLocalRegistry
case lregistry of
[] -> error "no local scope on stack"
(top:rest) -> put'vsLocalRegistry (M.insert name ctx top : rest)
vsmAddGlobalRef name ctx = do
registry <- get'vsGlobalRegistry
case M.lookup name registry of
Nothing -> return ()
Just Nothing -> return ()
Just (Just ctx) -> do
refs <- get'vsRefs
put'vsRefs (M.insert (RefPos name (textLine0 $ srcTextLocation ctx) (textColumn0 $ srcTextLocation ctx)) ctx refs)
vsmAddRef name ctx = do
lr <- get'vsLocalRegistry
go lr
where go [] = vsmAddGlobalRef name ctx
go (top:rest) = do
case M.lookup name top of
Nothing -> go rest
Just Nothing -> return ()
Just (Just ctx) -> do
refs <- get'vsRefs
put'vsRefs (M.insert (RefPos name (textLine0 $ srcTextLocation ctx) (textColumn0 $ srcTextLocation ctx)) ctx refs)
vsmAddGV :: Var -> VState ()
vsmAddGV var = get'vsGVs >>= put'vsGVs . (var:)
vsmAddGF :: FuncDec -> VState ()
vsmAddGF fd = get'vsGFs >>= put'vsGFs . (fd:)
vsmAddFunc :: Ctx Func -> VState ()
vsmAddFunc func = get'vsFuncs >>= put'vsFuncs . (func :)
vsmAddGlobal :: Global -> VState ()
vsmAddGlobal global = get'vsGlobals >>= put'vsGlobals . (global :)
vsmAddState :: State -> VState ()
vsmAddState state = get'vsStates >>= put'vsStates . (state :)
vsmAddLocal :: Maybe SourceContext -> Var -> VState ()
vsmAddLocal ctx v@(Var name _) = do
locals <- get'vsLocalVars
case locals of
[] -> error "internal error - no local scope"
(top:rest) ->
if (defined name $ concat locals) || (isConstant name)
then vsmAddErr (ctx, name ++ " is already defined")
else do
put'vsLocalVars ((v:top):rest)
vsmRegisterLocalName name ctx
vsmAddImport imp = get'vsImports >>= put'vsImports . (imp:)
vsmAddToNamesUsed :: String -> VState ()
vsmAddToNamesUsed name = get'vsNamesUsed >>= put'vsNamesUsed . (name :)
vsmWithNewScope :: VState a -> VState a
vsmWithNewScope action = do
get'vsLocalRegistry >>= put'vsLocalRegistry . (M.empty:)
get'vsLabels >>= put'vsLabels . ([]:)
get'vsLocalVars >>= put'vsLocalVars . ([]:)
val <- action
get'vsLocalRegistry >>= (\ (_:reg) -> put'vsLocalRegistry reg)
get'vsLabels >>= (\ (_:labels) -> put'vsLabels labels)
get'vsLocalVars >>= (\ (_:vars) -> put'vsLocalVars vars)
return val
vsmWithModule :: String -> VState () -> VState ()
vsmWithModule mname action = get'vsModules >>= put'vsModules . (mname:) >> action >> get'vsModules >>= (\ (_:ms) -> put'vsModules ms)
vsmAddErr :: CodeErr -> VState ()
vsmAddErr err = do
ctx <- get'vsContext >>= return . safeHead
errs <- get'vsErr
put'vsErr ((maybe (ctxFromCodeErr err) id ctx, msgFromCodeErr err) : errs)
vsmAddErrs :: [CodeErr] -> VState ()
vsmAddErrs = mapM_ vsmAddErr
vsmAddLabel :: String -> VState ()
vsmAddLabel label = do
labels <- get'vsLabels
case labels of
[] -> error "no local scope!"
(top:rest) -> put'vsLabels ((label:top):rest)
vsmAddHandler handlerName = get'vsHandlersUsed >>= put'vsHandlersUsed . (handlerName:)
vsmWithContext :: Maybe SourceContext -> VState a -> VState a
vsmWithContext srcCtx action = do
ctxs <- get'vsContext
put'vsContext (srcCtx:ctxs)
v <- action
put'vsContext ctxs
return v
vsmInEntryPoint :: LSLType -> Bool -> VState a -> VState a
vsmInEntryPoint t stateChangeAllow action = do
put'vsEntryPointInfo (t,stateChangeAllow)
action
vsmInBranch :: VState a -> VState a
vsmInBranch action = do
info <- get'vsBranchReturns
put'vsBranchReturns False
val <- action
put'vsBranchReturns info
return val
vsmWithinState action = do
put'vsHandlersUsed []
action
whenM mp action = do
p <- mp
when p action
whenJust mv action =
case mv of
Nothing -> return ()
Just v -> action v
whenIsJust mv p action =
case mv of
Nothing -> return ()
Just v | p v -> action
| otherwise -> return ()
safeHead [] = Nothing
safeHead (x:_) = Just x
compileLSLScript' :: Library -> LSLScript -> Validity CompiledLSLScript
compileLSLScript' library script = evalState (compileLSLScript script) (emptyValidationState { vsLib = library })
collectLabels :: Data a => a -> [(String,Maybe SourceContext)]
collectLabels = everythingBut (False `mkQ` string `extQ` sctx) (++) [] ([] `mkQ` lab)
where string :: String -> Bool
string = const True
sctx :: SourceContext -> Bool
sctx = const True
lab :: Ctx Statement -> [(String,Maybe SourceContext)]
lab (Ctx ctx (Label s)) = [(s,ctx)]
lab _ = []
warnLabelsMany :: Data a => [a] -> [CodeErr]
warnLabelsMany = concatMap warnLabels
warnLabelsStates = concatMap warnLabelsState
where warnLabelsState (State _ hs) = warnLabelsMany hs
warnLabels :: Data a => a -> [CodeErr]
warnLabels x = map (\ (name,ctx) -> (ctx,"label " ++ name ++ " is already defined (problem for LSL/Mono)")) problems
where problems = deleteFirstsBy fstEq labels uniques
labels = collectLabels x
uniques = nubBy fstEq labels
fstEq = flip ((==) . fst) . fst
compileLSLScript :: LSLScript -> VState (Validity CompiledLSLScript)
compileLSLScript (LSLScript globs states) = do
preprocessGlobDefs_ "" globs
preprocessStates states
mapM_ vsmAddGF predefFuncs
mapM_ compileGlob globs
mapM_ compileState states
err <- get'vsErr
case err of
[] -> do
globals <- get'vsGlobals
funcs <- get'vsFuncs
states <- get'vsStates
-- for now, error on label warnings since we have no warnings
return $ case warnLabelsStates states ++ warnLabelsMany funcs of
[] -> Right $ CompiledLSLScript (reverse globals) (reverse funcs) (reverse states)
errs -> Left errs
_ -> return $ Left $ reverse err
preprocessStates states = let snames = map (\ (State cn _) -> ctxItem cn) states in put'vsStateNames snames
preprocessGlobDefs :: String -> [GlobDef] -> VState ([Var],[FuncDec])
preprocessGlobDefs prefix globs = do
preprocessGlobDefs_ prefix globs
vars <- get'vsGVs
funcDecs <- get'vsGFs
return (vars,funcDecs)
preprocessGlobDefs_ :: String -> [GlobDef] -> VState ()
preprocessGlobDefs_ = mapM_ . preprocessGlobDef
preprocessGlobDef :: String -> GlobDef -> VState ()
preprocessGlobDef prefix (GV (Ctx ctx v@(Var name t)) _) = vsmAddGV (Var (prefix ++ name) t)
preprocessGlobDef prefix (GF (Ctx _ (Func (FuncDec name t params) _))) = vsmAddGF (FuncDec (fmap (prefix++) name) t params)
preprocessGlobDef prefix (GI moduleName _ prefix') = do
lib <- get'vsLib
case lookupModule (ctxItem moduleName) lib of
Left err -> vsmAddErrs err
Right (LModule globs _) -> mapM_ (preprocessGlobDef (prefix++prefix')) globs
compileGlob :: GlobDef -> VState ()
compileGlob (GV v mexpr) = do
let v' = ctxItem v
when (isConstant $ varName v') (vsmAddErr (srcCtx v, varName v' ++ " is a predefined constant"))
namesUsed <- get'vsNamesUsed
gvs <- get'vsGVs
when (varName v' `elem` namesUsed) (vsmAddErr (srcCtx v, varName v' ++ " is already defined"))
whenJust mexpr $ \ expr -> do
let (_,gvs') = break (\ var -> varName var == varName v') gvs
mt <- compileCtxSimple (drop 1 gvs') expr
whenJust mt $ \ t -> when (not (varType v' `matchTypes` t)) (vsmAddErr (srcCtx v, "expression not of the correct type"))
vsmRegisterGlobal v
vsmAddToNamesUsed (varName v')
vsmAddGlobal (GDecl v' (fmap ctxItem mexpr))
compileGlob (GF cf@(Ctx ctx f@(Func (FuncDec name t params) statements))) =
vsmWithNewScope $ do
compileParams params
vsmInEntryPoint t False $ do
whenM ((return elem) `ap` (return $ ctxItem name) `ap` get'vsNamesUsed) (vsmAddErr (srcCtx name, ctxItem name ++ " is already defined"))
returns <- compileStatements statements
when (not returns && t /= LLVoid) (vsmAddErr (srcCtx name, ctxItem name ++ ": not all code paths return a value"))
vsmRegisterFunc f
vsmAddToNamesUsed $ ctxItem name
vsmAddFunc cf
compileGlob (GI (Ctx ctx name) bindings prefix) =
vsmWithModule name $ do
let imp = (name, sort bindings, prefix)
imports <- get'vsImports
when (not (imp `elem` imports)) $ do
library <- get'vsLib
case lookupModule name library of
Left errs -> vsmAddErrs (map (\ (_,err) -> (ctx, "module " ++ name ++ ": " ++ err)) errs)
Right (LModule globs freevars) -> do
vars <- get'vsGVs
case validBindings vars freevars bindings of
Left err -> vsmAddErr (ctx, err)
Right () -> do
let (vars',funcDecs') = evalState (preprocessGlobDefs "" globs) (emptyValidationState { vsLib = library })
let renames = bindings ++ (map (\ x -> (x,prefix ++ x)) ((map varName vars') ++ (funcNames funcDecs')))
vsmAddImport imp
vsmWithContext ctx $ mapM_ (rewriteGlob' prefix renames (map ctxItem freevars ++ vars')) globs
rewriteGlob' prefix renames vars (GF (Ctx ctx (Func (FuncDec name t params) statements))) =
case lookup (ctxItem name) renames of
Nothing -> vsmAddErr (srcCtx name, "can't rename " ++ ctxItem name ++ ": not found")
Just name' -> do
namesUsed <- get'vsNamesUsed
if name' `elem` namesUsed
then vsmAddErr (srcCtx name, name' ++ " imported from module is already defined")
else let rewrittenFunc = (Func (FuncDec (Ctx (srcCtx name) name') t params) $ rewriteStatements 0 renames statements)
in do vsmAddToNamesUsed name'
vsmRegisterFunc rewrittenFunc
vsmAddFunc (Ctx ctx rewrittenFunc)
rewriteGlob' prefix renames vars (GV (Ctx ctx (Var name t)) mexpr) =
case lookup name renames of
Nothing -> vsmAddErr (ctx, "can't rename " ++ name ++ ": not found")
Just name' -> do
namesUsed <- get'vsNamesUsed
if name' `elem` namesUsed
then vsmAddErr (ctx, name' ++ " imported from module is already defined")
else let rewrittenGlobVar = GDecl (Var name' t) (fmap (ctxItem . (rewriteCtxExpr renames)) mexpr)
in do vsmAddToNamesUsed name'
vsmRegisterGlobal (Ctx ctx (Var name' t))
vsmAddGlobal rewrittenGlobVar
rewriteGlob' prefix0 renames vars (GI (Ctx ctx mName) bindings prefix) =
do lib <- get'vsLib
case lookupModule mName lib of
Left errs -> vsmAddErrs (map (\ (_,err) -> (ctx, err)) errs)
Right (LModule globs freevars) -> do
case validBindings vars freevars bindings of
Left err -> vsmAddErr (ctx, err)
Right _ -> do
bindings' <- mapM rewriteBinding bindings
let imp = (mName,sort bindings', prefix0 ++ prefix)
imports <- get'vsImports
when (not (imp `elem` imports)) $ do
let (vars',funcDecs') = evalState (preprocessGlobDefs "" globs) (emptyValidationState { vsLib = lib })
let renames = bindings' ++ map (\ x -> (x,prefix0 ++ prefix ++ x)) (map varName vars' ++ map (ctxItem . funcName) funcDecs')
vsmAddImport imp
mapM_ (rewriteGlob' (prefix0 ++ prefix) renames vars') globs
where rewriteBinding (fv,rn) = case lookup rn renames of
Nothing -> vsmAddErr (ctx, rn ++ ": not found") >> return (fv,rn)
Just rn' -> return (fv,rn')
compileState :: State -> VState ()
compileState state@(State nm handlers) =
vsmWithinState $ do
states <- get'vsStates
when (isJust (find (\ (State x _)-> ctxItem x == ctxItem nm) states)) $
vsmAddErr (srcCtx nm, ctxItem nm ++ " already defined")
mapM_ compileHandler handlers
vsmAddState state
compileHandler (Handler (Ctx ctx name) args stmts) =
vsmWithNewScope $ do
used <- get'vsHandlersUsed
if name `elem` used then vsmAddErr (ctx, name ++ " already used in this state")
else vsmAddHandler name
case lookup name goodHandlers of
Nothing -> vsmAddErr (ctx, name ++ " is not a valid handler name")
Just types -> when (types /= map varType (ctxItems args)) $ vsmAddErr (ctx, "invalid parameter types for handler " ++ name)
compileParams args
vsmInEntryPoint LLVoid True $ compileStatements stmts
compileCtxSimple :: [Var] -> CtxExpr -> VState (Maybe LSLType)
compileCtxSimple gvs (Ctx ctx expr) = do
t <- compileSimple gvs expr
case t of
Nothing -> vsmAddErr (ctx,"expression is not valid as an initializer for a global variable") >> return t
_ -> return t
compileSimple :: [Var] -> Expr -> VState (Maybe LSLType)
compileSimple _ (IntLit i) = return $ Just LLInteger
compileSimple _ (FloatLit i) = return $ Just LLFloat
compileSimple _ (StringLit s) = return $ Just LLString
compileSimple _ (KeyLit k) = return $ Just LLKey
compileSimple gvs (Get (Ctx ctx name, All)) = do
vsmAddGlobalRef name ctx
let var = find (\ v -> varName v == name) gvs
case var of
Just (Var _ t) -> return $ Just t
Nothing -> case findConstType name of
Nothing -> vsmAddErr (ctx,"variable " ++ name ++ " not found") >> return Nothing
Just t -> return $ Just t
compileSimple _ (Get (Ctx ctx name,_)) = do
vsmAddGlobalRef name ctx
vsmAddErr (ctx, "can't access vector/rotation component in global variable initialization") >> return Nothing
compileSimple _ (ListExpr []) = return $ Just LLList
compileSimple vars (ListExpr (e:es)) = do
mt <- compileCtxSimple vars e
case mt of
Nothing -> return Nothing
Just t -> do
when (t == LLList) (vsmAddErr (srcCtx e, "lists cannot contain other lists"))
compileSimple vars (ListExpr es)
compileSimple vars (VecExpr e1 e2 e3) = compileSimpleStructure vars LLVector [e1,e2,e3]
compileSimple vars (RotExpr e1 e2 e3 e4) = compileSimpleStructure vars LLRot [e1,e2,e3,e4]
compileSimple vars (Neg e) = do
mt <- compileCtxSimple vars e
case mt of
Nothing -> return Nothing
Just t ->
if (t `notElem` [LLFloat,LLInteger])
then vsmAddErr (srcCtx e, "operator only applicable to integers and floats in this context") >> return Nothing
else return (Just t)
compileSimple _ e = return Nothing
compileSimpleStructure :: [Var] -> LSLType -> [CtxExpr] -> VState (Maybe LSLType)
compileSimpleStructure _ t [] = return (Just t)
compileSimpleStructure vars t (e:es) = do
mt <- compileCtxSimple vars e
case mt of
Nothing -> return Nothing
Just t' ->
if (t' `notElem` [LLFloat,LLInteger])
then vsmAddErr (srcCtx e, "literal of type " ++ lslTypeString t' ++ " is not a valid element of " ++ lslTypeString t)
>> return Nothing
else compileSimpleStructure vars t es
compileStatement :: Ctx Statement -> VState Bool
compileStatement (Ctx ctx (Decl var@(Var name t) expr)) = do
vsmAddLocal ctx var
case expr of
Nothing -> return ()
Just expr' -> do mt' <- compileCtxExpr expr'
case mt' of
Nothing -> return ()
Just t' -> when (not $ matchTypes t t') $ vsmAddErr (srcCtx expr', "type of expression in declaration of " ++ name ++ " does not match " ++ lslTypeString t)
get'vsBranchReturns
compileStatement (Ctx ctx (While expr statement)) = do
t <- compileCtxExpr expr
vsmInBranch $ compileStatement (nullCtx statement)
get'vsBranchReturns
compileStatement (Ctx ctx(DoWhile statement expr)) =
do t <- compileCtxExpr expr
vsmInBranch $ compileStatement (nullCtx statement)
get'vsBranchReturns
compileStatement (Ctx ctx (For mexpr1 mexpr2 mexpr3 statement)) =
do compileExpressions mexpr1
compileExpressions mexpr3
t <- compileMExpression mexpr2
vsmInBranch $ compileStatement (nullCtx statement)
get'vsBranchReturns
compileStatement (Ctx ctx (If expr thenStmt elseStmt)) =
do t <- compileCtxExpr expr
ret1 <- vsmInBranch $ compileStatement (nullCtx thenStmt)
ret2 <- vsmInBranch $ compileStatement (nullCtx elseStmt)
returns <- get'vsBranchReturns
put'vsBranchReturns (returns || (ret1 && ret2))
get'vsBranchReturns
compileStatement (Ctx ctx NullStmt) = get'vsBranchReturns
compileStatement (Ctx ctx (Return Nothing)) =
do (rtype,_) <- get'vsEntryPointInfo
when (rtype /= LLVoid) (vsmAddErr (ctx,"function must return a value"))
put'vsBranchReturns True
return True
compileStatement (Ctx ctx (Return (Just expr))) =
do t <- compileCtxExpr expr
case t of
Nothing -> return ()
Just t' -> do
(rtype,_) <- get'vsEntryPointInfo
when (t' /= rtype && not (all (`elem` [LLString,LLKey]) [t',rtype])) (vsmAddErr (ctx,"inappropriate return type for function/handler"))
put'vsBranchReturns True
return True
compileStatement (Ctx ctx (StateChange name)) = do
(_,scallow) <- get'vsEntryPointInfo
snames <- get'vsStateNames
when (not scallow) $ vsmAddErr (ctx,"state changes not allowed from this context")
when (not (name `elem` snames)) $ vsmAddErr (ctx,name ++ " is not a valid state")
get'vsBranchReturns
compileStatement (Ctx ctx (Do expr)) = compileCtxExpr expr >> get'vsBranchReturns
compileStatement (Ctx ctx (Compound stmts)) = compileStatements stmts
compileStatement (Ctx ctx (Label _)) = put'vsBranchReturns False >> return False
compileStatement (Ctx ctx (Jump s)) =
do labels <- get'vsLabels
when (s `notElem` concat labels) $ vsmAddErr (ctx, "no such label to jump to: " ++ s)
get'vsBranchReturns
compileStatements :: [CtxStmt] -> VState Bool
compileStatements stmts =
do let newLabels = map (\ (Label s) -> s) $ filter isLabel (ctxItems stmts)
vsmWithNewScope $ do
mapM_ vsmAddLabel newLabels
mapM_ compileStatement stmts
get'vsBranchReturns
compileParams :: [CtxVar] -> VState ()
compileParams vs = mapM_ ( \(Ctx ctx v) -> vsmAddLocal ctx v) vs
isCastValid t t' = (t' == t) || (t,t') `elem` validCasts
where validCasts = [(LLInteger,LLFloat), (LLFloat,LLInteger),
(LLInteger,LLString),(LLString,LLInteger),
(LLFloat,LLString),(LLString,LLFloat),
(LLString,LLVector),(LLVector,LLString),
(LLString,LLKey),(LLKey,LLString),
(LLRot,LLString),(LLString,LLRot),
(LLList,LLString),(LLString,LLList)]
compileCtxExpr :: Ctx Expr -> VState (Maybe LSLType)
compileCtxExpr (Ctx ctx (Cast t expr)) =
do mt <- compileCtxExpr expr
case mt of
Nothing -> return ()
Just t' -> when (not $ isCastValid t' t) $ vsmAddErr (ctx,"cannot cast a value of type " ++ lslTypeString t' ++ " to type " ++ lslTypeString t)
return (Just t)
compileCtxExpr (Ctx ctx0 (Get ((Ctx ctx name),component))) = do
vsmAddRef name ctx
vars <- get'vsGVs
locals <- get'vsLocalVars
let varList = (concat locals ++ vars)
case (findType name varList `mplus` findConstType name,component) of
(Nothing,_) -> vsmAddErr (ctx, "undefined variable or constant: " ++ name) >> return Nothing
(Just LLRot,All) -> return $ Just LLRot
(Just LLRot,_) -> return $ Just LLFloat
(Just LLVector,All) -> return $ Just LLVector
(Just LLVector,S) -> vsmAddErr (ctx0,"s is not a valid component of a vector") >> (return $ Just LLFloat)
(Just LLVector,_) -> return $ Just LLFloat
(Just t,All) -> return $ Just t
(Just t,_) -> vsmAddErr (ctx,"only vectors and rotations have components") >> return Nothing
compileCtxExpr (Ctx ctx (Call name exprs)) = compileCall name exprs
compileCtxExpr (Ctx ctx (Not expr)) =
do mt <- compileCtxExpr expr
(mt `whenIsJust` (/=LLInteger)) $ vsmAddErr (ctx, "operator not applicable to non-integer type")
return $ Just LLInteger
compileCtxExpr (Ctx ctx (Neg expr)) =
do mt <- compileCtxExpr expr
(mt `whenIsJust` (`elem` [LLList,LLString,LLKey])) $ vsmAddErr (ctx, "operator not applicable to this type")
return mt
compileCtxExpr (Ctx ctx (Inv expr)) =
do mt <- compileCtxExpr expr
(mt `whenIsJust` (/=LLInteger)) $ vsmAddErr (srcCtx expr, "operator not applicable to non-integer type")
return mt
compileCtxExpr (Ctx ctx plus@(Add expr1 expr2)) =
do (mt1,mt2) <- compileEach (expr1,expr2)
case (mt1,mt2) of
(Nothing,Just t2) -> return (Just t2)
(Just t1,Nothing) -> return (Just t1)
(Nothing,Nothing) -> return Nothing
(Just LLInteger,Just LLInteger) -> return $ Just LLInteger
(Just LLInteger,Just LLFloat) -> return $ Just LLFloat
(Just LLFloat,Just LLInteger) -> return $ Just LLFloat
(Just LLFloat,Just LLFloat) -> return $ Just LLFloat
(Just LLVector,Just LLVector) -> return $ Just LLVector
(Just LLRot,Just LLRot) -> return $ Just LLRot
(Just LLString,Just LLString) -> return $ Just LLString
(Just LLList,Just LLList) -> return $ Just LLList
(Just t,Just LLList) -> return $ Just LLList
(Just LLList,Just t) -> return $ Just LLList
(Just t0,Just t1) -> reportIncompatibleOperands ctx t0 t1 >> return Nothing
compileCtxExpr (Ctx ctx minus@(Sub expr1 expr2)) =
do (mt1,mt2) <- compileEach (expr1,expr2)
case (mt1,mt2) of
(Nothing,Just t2) -> return (Just t2)
(Just t1,Nothing) -> return (Just t1)
(Nothing,Nothing) -> return Nothing
(Just LLInteger,Just LLInteger) -> return $ Just LLInteger
(Just LLInteger,Just LLFloat) -> return $ Just LLFloat
(Just LLFloat,Just LLInteger) -> return $ Just LLFloat
(Just LLFloat,Just LLFloat) -> return $ Just LLFloat
(Just LLVector,Just LLVector) -> return $ Just LLVector
(Just LLRot,Just LLRot) -> return $ Just LLRot
(Just t0,Just t1) -> reportIncompatibleOperands ctx t0 t1 >> return Nothing
compileCtxExpr (Ctx ctx expr@(Mul expr1 expr2)) =
do (mt1,mt2) <- compileEach (expr1,expr2)
case (mt1,mt2) of
(Nothing,Just t2) -> return (Just t2)
(Just t1,Nothing) -> return (Just t1)
(Nothing,Nothing) -> return Nothing
(Just LLInteger,Just LLInteger) -> return $ Just LLInteger
(Just LLInteger,Just LLFloat) -> return $ Just LLFloat
(Just LLFloat,Just LLInteger) -> return $ Just LLFloat
(Just LLFloat,Just LLFloat) -> return $ Just LLFloat
(Just LLVector,Just LLInteger) -> return $ Just LLVector
(Just LLVector,Just LLFloat) -> return $ Just LLVector
(Just LLFloat,Just LLVector) -> return $ Just LLVector
(Just LLInteger,Just LLVector) -> return $ Just LLVector
(Just LLVector,Just LLVector) -> return $ Just LLFloat
(Just LLVector,Just LLRot) -> return $ Just LLVector
(Just LLRot,Just LLRot) -> return $ Just LLRot
(Just t0,Just t1) -> reportIncompatibleOperands ctx t0 t1 >> return Nothing
compileCtxExpr (Ctx ctx expr@(Div expr1 expr2)) =
do (mt1,mt2) <- compileEach (expr1,expr2)
case (mt1,mt2) of
(Nothing,Just t2) -> return (Just t2)
(Just t1,Nothing) -> return (Just t1)
(Nothing,Nothing) -> return Nothing
(Just LLInteger,Just LLInteger) -> return $ Just LLInteger
(Just LLInteger,Just LLFloat) -> return $ Just LLFloat
(Just LLFloat,Just LLInteger) -> return $ Just LLFloat
(Just LLFloat,Just LLFloat) -> return $ Just LLFloat
(Just LLVector,Just LLInteger) -> return $ Just LLVector
(Just LLVector,Just LLFloat) -> return $ Just LLVector
(Just LLVector,Just LLRot) -> return $ Just LLVector
(Just LLRot,Just LLRot) -> return $ Just LLRot
(Just t0,Just t1) -> reportIncompatibleOperands ctx t0 t1 >> return Nothing
compileCtxExpr (Ctx ctx expr@(Mod expr1 expr2)) =
do (mt1,mt2) <- compileEach (expr1,expr2)
case (mt1,mt2) of
(Nothing,Just t2) -> return (Just t2)
(Just t1,Nothing) -> return (Just t1)
(Nothing,Nothing) -> return Nothing
(Just LLInteger,Just LLInteger) -> return $ Just LLInteger
(Just LLVector,Just LLVector) -> return $ Just LLVector
(Just t1,Just t2) -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx e@(Equal expr1 expr2)) =
do (mt1,mt2) <- compileEach (expr1,expr2)
case (mt1,mt2) of
(Nothing,Just t2) -> return (Just t2)
(Just t1,Nothing) -> return (Just t1)
(Nothing,Nothing) -> return Nothing
(Just LLInteger,Just LLFloat) -> return $ Just LLInteger
(Just LLFloat,Just LLInteger) -> return $ Just LLInteger
(Just LLString,Just LLKey) -> return $ Just LLInteger
(Just LLKey,Just LLString) -> return $ Just LLInteger
(Just t1,Just t2) | (t1 == t2) -> return $ Just LLInteger
| otherwise -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx e@(NotEqual expr1 expr2)) =
do (mt1,mt2) <- compileEach (expr1,expr2)
case (mt1,mt2) of
(Nothing,Just t2) -> return (Just t2)
(Just t1,Nothing) -> return (Just t1)
(Nothing,Nothing) -> return Nothing
(Just LLInteger,Just LLFloat) -> return $ Just LLInteger
(Just LLFloat,Just LLInteger) -> return $ Just LLInteger
(Just LLString,Just LLKey) -> return $ Just LLInteger
(Just LLKey,Just LLString) -> return $ Just LLInteger
(Just t1,Just t2) | (t1 == t2) -> return $ Just LLInteger
| otherwise -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx e@(BAnd expr1 expr2)) = compileBothInteger ctx (expr1,expr2)
compileCtxExpr (Ctx ctx e@(BOr expr1 expr2)) = compileBothInteger ctx (expr1,expr2)
compileCtxExpr (Ctx ctx e@(Xor expr1 expr2)) = compileBothInteger ctx (expr1,expr2)
compileCtxExpr (Ctx ctx e@(ShiftL expr1 expr2)) = compileBothInteger ctx (expr1,expr2)
compileCtxExpr (Ctx ctx e@(ShiftR expr1 expr2)) = compileBothInteger ctx (expr1,expr2)
compileCtxExpr (Ctx ctx e@(Gt expr1 expr2)) = compileRelExpr ctx (expr1,expr2)
compileCtxExpr (Ctx ctx e@(Ge expr1 expr2)) = compileRelExpr ctx (expr1,expr2)
compileCtxExpr (Ctx ctx e@(Le expr1 expr2)) = compileRelExpr ctx (expr1,expr2)
compileCtxExpr (Ctx ctx e@(Lt expr1 expr2)) = compileRelExpr ctx (expr1, expr2)
compileCtxExpr (Ctx ctx e@(And expr1 expr2)) = compileBothInteger ctx (expr1, expr2)
compileCtxExpr (Ctx ctx e@(Or expr1 expr2)) = compileBothInteger ctx (expr1, expr2)
compileCtxExpr (Ctx ctx e@(IncBy (name,All) expr)) =
do reportErrorIfNoModify name
(mt1,mt2) <- compileNameExpr (name,expr)
case (mt1,mt2) of
(Nothing,Nothing) -> return Nothing
(Just t1,Nothing) -> return $ Just t1
(Nothing,Just t2) -> return $ Just t2
(Just LLInteger,Just LLInteger) -> return $ Just LLInteger
(Just LLFloat,Just LLInteger) -> return $ Just LLFloat
(Just LLFloat,Just LLFloat) -> return $ Just LLFloat
(Just LLVector,Just LLVector) -> return $ Just LLVector
(Just LLRot,Just LLRot) -> return $ Just LLRot
(Just LLString,Just LLString) -> return $ Just LLString
(Just LLList,Just LLList) -> return $ Just LLList
(Just LLList,Just t) -> return $ Just LLList
(Just t1,Just t2) -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx e@(IncBy (name,_) expr) ) =
do reportErrorIfNoModify name
(mt1,mt2) <- compileNameExpr (name,expr)
case (mt1,mt2) of
(Nothing,Nothing) -> return Nothing
(Just t1,Nothing) -> return $ Just t1
(Nothing,Just t2) -> return $ Just t2
(Just t1,Just t2) | t1 `elem` [LLVector,LLRot] && t2 `elem` [LLFloat,LLInteger] -> return $ Just LLFloat
| otherwise -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx e@(DecBy (name,All) expr)) =
do reportErrorIfNoModify name
(mt1,mt2) <- compileNameExpr (name,expr)
case (mt1,mt2) of
(Nothing,Nothing) -> return Nothing
(Just t1,Nothing) -> return $ Just t1
(Nothing,Just t2) -> return $ Just t2
(Just LLInteger,Just LLInteger) -> return $ Just LLInteger
(Just LLFloat,Just LLInteger) -> return $ Just LLFloat
(Just LLFloat,Just LLFloat) -> return $ Just LLFloat
(Just LLVector,Just LLVector) -> return $ Just LLVector
(Just LLRot,Just LLRot) -> return $ Just LLRot
(Just t1,Just t2) -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx e@(DecBy (name,_) expr)) =
do reportErrorIfNoModify name
(mt1,mt2) <- compileNameExpr (name,expr)
case (mt1,mt2) of
(Nothing,Nothing) -> return Nothing
(Just t1,Nothing) -> return $ Just t1
(Nothing,Just t2) -> return $ Just t2
(Just t1,Just t2) | t1 `elem` [LLVector,LLRot] && t2 `elem` [LLFloat,LLInteger] -> return $ Just LLFloat
| otherwise -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx e@(MulBy (name,All) expr)) =
do reportErrorIfNoModify name
(mt1,mt2) <- compileNameExpr (name,expr)
case (mt1,mt2) of
(Nothing,Nothing) -> return Nothing
(Just t1,Nothing) -> return $ Just t1
(Nothing,Just t2) -> return $ Just t2
(Just LLInteger,Just LLInteger) -> return $ Just LLInteger
(Just LLFloat,Just LLInteger) -> return $ Just LLFloat
(Just LLFloat,Just LLFloat) -> return $ Just LLFloat
(Just LLVector,Just LLInteger) -> return $ Just LLVector
(Just LLVector,Just LLFloat) -> return $ Just LLVector
(Just LLVector,Just LLVector) -> return $ Just LLVector -- note: LSL compiles this, but it results in runtime error!
(Just LLVector,Just LLRot) -> return $ Just LLVector
(Just LLRot,Just LLRot) -> return $ Just LLRot
(Just t1,Just t2) -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx e@(MulBy (name,_) expr)) =
do reportErrorIfNoModify name
(mt1,mt2) <- compileNameExpr (name,expr)
case (mt1,mt2) of
(Nothing,Nothing) -> return Nothing
(Just t1,Nothing) -> return $ Just t1
(Nothing,Just t2) -> return $ Just t2
(Just t1,Just t2) | t1 `elem` [LLVector,LLRot] && t2 `elem` [LLFloat,LLInteger] -> return $ Just LLFloat
| otherwise -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx e@(DivBy (name,All) expr)) =
do reportErrorIfNoModify name
(mt1,mt2) <- compileNameExpr (name,expr)
case (mt1,mt2) of
(Nothing,Nothing) -> return Nothing
(Just t1,Nothing) -> return $ Just t1
(Nothing,Just t2) -> return $ Just t2
(Just LLInteger,Just LLInteger) -> return $ Just LLInteger
(Just LLFloat,Just LLInteger) -> return $ Just LLFloat
(Just LLFloat,Just LLFloat) -> return $ Just LLFloat
(Just LLVector,Just LLInteger) -> return $ Just LLVector
(Just LLVector,Just LLFloat) -> return $ Just LLVector
(Just LLVector,Just LLRot) -> return $ Just LLVector
(Just LLRot,Just LLRot) -> return $ Just LLRot
(Just t1,Just t2) -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx e@(DivBy (name,_) expr)) =
do reportErrorIfNoModify name
(mt1,mt2) <- compileNameExpr (name,expr)
case (mt1,mt2) of
(Nothing,Nothing) -> return Nothing
(Just t1,Nothing) -> return $ Just t1
(Nothing,Just t2) -> return $ Just t2
(Just t1,Just t2) | t1 `elem` [LLVector,LLRot] && t2 `elem` [LLFloat,LLInteger] -> return $ Just LLFloat
| otherwise -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx e@(ModBy (name,All) expr)) =
do reportErrorIfNoModify name
(mt1,mt2) <- compileNameExpr (name,expr)
case (mt1,mt2) of
(Nothing,Nothing) -> return Nothing
(Just t1,Nothing) -> return $ Just t1
(Nothing,Just t2) -> return $ Just t2
(Just LLInteger,Just LLInteger) -> return $ Just LLInteger
(Just LLVector,Just LLVector) -> return $ Just LLVector
(Just t1,Just t2) -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx e@(ModBy (name,_) expr)) =
do reportErrorIfNoModify name
(mt1,mt2) <- compileNameExpr (name,expr)
case (mt1,mt2) of
(Nothing,Nothing) -> return Nothing
(Just t1,Nothing) -> return $ Just t1
(Nothing,Just t2) -> return $ Just t2
(Just t1,Just t2) -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx e@(PostInc var)) = compileIncDecOp var "++"
compileCtxExpr (Ctx ctx e@(PostDec var)) = compileIncDecOp var "--"
compileCtxExpr (Ctx ctx e@(PreInc var)) = compileIncDecOp var "++"
compileCtxExpr (Ctx ctx e@(PreDec var)) = compileIncDecOp var "++"
compileCtxExpr (Ctx ctx expr0@(Set (name,All) expr)) =
do reportErrorIfNoModify name
(mt1,mt2) <- compileNameExpr (name,expr)
case (mt1,mt2) of
(Nothing,Nothing) -> return Nothing
(Just t1,Nothing) -> return $ Just t1
(Nothing,Just t2) -> return $ Just t2
(Just LLFloat,Just LLInteger) -> return $ Just LLFloat
(Just LLKey,Just LLString) -> return $ Just LLKey
(Just LLString,Just LLKey) -> return $ Just LLString
(Just t1,Just t2) | t1 == t2 -> return $ Just t1
| otherwise -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx expr0@(Set (name,S) expr)) =
do reportErrorIfNoModify name
(mt1,mt2) <- compileNameExpr (name,expr)
case (mt1,mt2) of
(Nothing,Nothing) -> return Nothing
(Just t1,Nothing) -> return $ Just t1
(Nothing,Just t2) -> return $ Just t2
(Just LLRot,Just LLFloat) -> return $ Just LLFloat
(Just LLRot,Just LLInteger) -> return $ Just LLFloat
(Just t1,Just t2) -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx expr0@(Set (name,_) expr)) =
do reportErrorIfNoModify name
(mt1,mt2) <- compileNameExpr (name,expr)
case (mt1,mt2) of
(Nothing,Nothing) -> return Nothing
(Just t1,Nothing) -> return $ Just t1
(Nothing,Just t2) -> return $ Just t2
(Just LLVector,Just LLFloat) -> return $ Just LLFloat
(Just LLVector,Just LLInteger) -> return $ Just LLFloat
(Just LLRot,Just LLFloat) -> return $ Just LLFloat
(Just LLRot,Just LLInteger) -> return $ Just LLFloat
(Just t1,Just t2) -> reportIncompatibleOperands ctx t1 t2 >> return Nothing
compileCtxExpr (Ctx ctx (IntLit i)) = return $ Just LLInteger
compileCtxExpr (Ctx ctx (FloatLit _)) = return $ Just LLFloat
compileCtxExpr (Ctx ctx (StringLit _)) = return $ Just LLString
compileCtxExpr (Ctx ctx (KeyLit _)) = return $ Just LLKey
compileCtxExpr (Ctx ctx (ListExpr es)) = do
mapM compileListExprElement es
return $ Just LLList
compileCtxExpr (Ctx ctx (VecExpr xExpr yExpr zExpr)) =
do xt <- compileCtxExpr xExpr
yt <- compileCtxExpr yExpr
zt <- compileCtxExpr zExpr
when (not (all (`elem` [LLInteger,LLFloat]) [c | Just c <- [xt,yt,zt]])) $ vsmAddErr (ctx, "invalid components for vector")
return $ Just LLVector
compileCtxExpr (Ctx ctx (RotExpr xExpr yExpr zExpr sExpr)) =
do xt <- compileCtxExpr xExpr
yt <- compileCtxExpr yExpr
zt <- compileCtxExpr zExpr
st <- compileCtxExpr sExpr
when (not (all (`elem` [LLInteger,LLFloat]) [ c | Just c <- [xt,yt,zt,st]])) $ vsmAddErr (ctx, "invalid components for rotation")
return $ Just LLRot
reportErrorIfNoModify (Ctx ctx name) =
when (isConstant name) $ vsmAddErr (ctx, "cannot modify " ++ name ++ " because it is a constant")
reportIncompatibleOperands ctx t0 t1 =
vsmAddErr (ctx,"the types of the operands aren't compatible (" ++ lslTypeString t0 ++ " vs. " ++ lslTypeString t1 ++ ")")
compileExpressions es = mapM_ compileCtxExpr es
compileMExpression Nothing = return (Just LLVoid)
compileMExpression (Just expr) = compileCtxExpr expr
compileCall (Ctx ctx fname) exprs = do
vsmAddRef fname ctx
funcs <- get'vsGFs
case findFuncDec fname funcs of
Nothing -> vsmAddErr (ctx,fname ++ ": no such function in scope") >> return Nothing
Just (FuncDec _ t params) ->
let vArg :: Int -> [Var] -> [Ctx Expr] -> VState ()
vArg _ [] [] = return ()
vArg _ (p:ps) [] = vsmAddErr (ctx, "mismatch of arguments vs. formal paramters in call to function " ++ fname)
vArg _ [] (a:as) = vsmAddErr (ctx, "mismatch of arguments vs. formal paramters in call to function " ++ fname)
vArg n (Var name t:ts) (arg:args) =
do mt' <- compileCtxExpr arg
case mt' of
Nothing -> return ()
Just t' -> when (not (matchTypes t t')) $ vsmAddErr (ctx, "argument " ++ (show n) ++ " in call to function (" ++ fname ++ ") is of wrong type:" ++ (lslTypeString t') ++ ", should be " ++ (lslTypeString t))
vArg (n+1) ts args
in vArg 1 (ctxItems params) exprs >> return (Just t)
compileEach :: (Ctx Expr,Ctx Expr) -> VState (Maybe LSLType, Maybe LSLType)
compileEach (expr1,expr2) = do
mt1 <- compileCtxExpr expr1
mt2 <- compileCtxExpr expr2
return (mt1,mt2)
compileBothInteger ctx (expr1,expr2) = do
(mt1,mt2) <- compileEach (expr1,expr2)
case (mt1,mt2) of
(Just LLInteger,Just LLInteger) -> return $ Just LLInteger
(Just _, Just _) -> vsmAddErr (ctx,"operands are of incompatible type") >> return Nothing
_ -> return Nothing
compileRelExpr ctx (expr1,expr2) = do
do (mt1,mt2) <- compileEach (expr1,expr2)
case (mt1,mt2) of
(Nothing,_) -> return Nothing
(_,Nothing) -> return Nothing
(Just LLInteger,Just LLInteger) -> return $ Just LLInteger
(Just LLInteger,Just LLFloat) -> return $ Just LLInteger
(Just LLFloat,Just LLInteger) -> return $ Just LLInteger
(Just LLFloat,Just LLFloat) -> return $ Just LLInteger
(Just t0,Just t1) -> vsmAddErr (ctx,"operands are of incompatible types") >> return Nothing
compileNameExpr (Ctx ctx name,expr) = do
vsmAddRef name ctx
locals <- get'vsLocalVars
globvars <- get'vsGVs
mt <- compileCtxExpr expr
let mvt = findType name (concat locals ++ globvars)
when (isNothing mvt) $ vsmAddErr (ctx,"variable " ++ name ++ " not defined")
return (mvt,mt)
compileIncDecOp (n@(Ctx ctx name),c) op = do
vsmAddRef name ctx
reportErrorIfNoModify n
locals <- get'vsLocalVars
vars <- get'vsGVs
case (findType name (concat locals ++ vars),c) of
(Nothing,_) -> vsmAddErr (ctx, "variable " ++ name ++ " not found") >> return Nothing
(Just LLInteger,All) -> return $ Just LLInteger
(Just LLFloat,All) -> return $ Just LLFloat
(Just LLRot,S) -> return $ Just LLFloat
(Just LLVector,S) -> vsmAddErr (ctx, "s is not a valid component of " ++ name) >> return Nothing
(Just t,All) -> vsmAddErr (ctx, name ++ " is not a valid operand for " ++ op) >> return Nothing
(Just LLVector,_) -> return $ Just LLFloat
(Just LLRot,_) -> return $ Just LLFloat
_ -> vsmAddErr (ctx, name ++ " is not a valid operand for " ++ op) >> return Nothing
compileListExprElement e@(Ctx ctx _) = do
mt <- compileCtxExpr e
case mt of
Nothing -> return ()
Just t | t `elem` [LLVoid,LLList] -> vsmAddErr (ctx,"invalid list element")
| otherwise -> return ()
validBindings vars freevars bindings =
if length freevars /= length bindings then
throwStrError ("wrong number of bindings in import: " ++ (show $ length freevars) ++ " required")
else let f [] = return ()
f ((x,y):xys) =
case (findType x (ctxItems freevars), findType y vars) of
(Nothing,_) -> throwStrError ("free variable " ++ x ++ " not found")
(_,Nothing) -> throwStrError ("global variable " ++ y ++ " not found")
(Just t0,Just t1) | not (matchTypes t0 t1) -> throwStrError ("types of " ++ x ++ " and " ++ y ++ " don't match")
| otherwise -> f xys
in f bindings
defined :: String -> [Var] -> Bool
defined n = any (\ (Var n' _) -> n == n')
-- Validating a library of modules
compileModule :: LModule -> VState (Validity ([Global],[Ctx Func]))
compileModule m@(LModule globs freevars) = do
mapM_ (vsmAddGV . ctxItem) freevars
preprocessGlobDefs_ "" globs
mapM_ vsmAddGF predefFuncs
mapM_ compileGlob globs
errs <- get'vsErr
case errs of
[] -> do
globals <- get'vsGlobals
funcs <- get'vsFuncs
return $ Right (globals,funcs)
_ -> return $ Left errs
-- this function isn't partiuclarly efficient!
moduleDependencies lib chain m =
let f (GI s _ _) = Just (ctxItem s)
f _ = Nothing
in do (LModule globs _) <- lookupM m lib
case filtMap f globs of
[] -> return []
list -> if any (`elem` list) (m:chain) then throwStrError "circular dependency"
else
do deps <- fmap concat (
let chain' = (m:chain) in
mapM (moduleDependencies lib chain') list)
return $ nub (list ++ deps)
-- sort modules by dependency: for each module in the list, after sorting that module
-- will depend only on modules preceding it in the list. This of course implies that
-- there can be no circular dependencies in the modules.
sortModules :: [(String,(LModule,[String]))] -> [(String,LModule)]
sortModules modules =
let cmp (name,(_,deplist)) (name',(_,deplist')) = compare (length deplist, name) (length deplist', name')
sort1 [] = []
sort1 list =
let sorted = sortBy cmp list
(nodeps,deps) = span ((==0).length.snd.snd) sorted
exclude = map fst nodeps
newlist = if length nodeps == 0 then error "circular depencencies in library"
else map (\ (nm,(m,l)) -> (nm, (m,filter (`notElem` exclude) l))) deps
in nodeps ++ sort1 newlist
in map (\ (s,(m,_)) -> (s,m)) $ sort1 modules
compileLibrary :: [(String,LModule)] -> [(String,Validity (LModule,ModuleInfo))]
compileLibrary modules =
let checkDep (n,m) = case moduleDependencies modules [] n of
Right deps -> (n,Right (m,deps))
Left s -> (n,Left s)
categorize (good,bad) (n,Left s) = (good,(n,s):bad)
categorize (good,bad) (n,Right (m,deps)) = ((n,(m,deps)):good,bad)
(good,bad) = foldl categorize ([],[]) $ map checkDep modules
sorted = sortModules good
validate augLib (name,m) =
case evalState (compileModule m) (emptyValidationState { vsLib = libFromAugLib augLib }) of
Left s -> (name, Left s):augLib
Right gs -> (name,Right (m,gs)):augLib
in (foldl validate [] sorted) ++ (map (\ (n,s) -> (n,Left s)) bad)
libFromAugLib :: AugmentedLibrary -> Library
libFromAugLib augLib =
let f (name,Left s) = (name,Left s)
f (name,Right (lm,_)) = (name, Right lm)
in map f augLib
tstLib = [
("alpha", LModule [GI (nullCtx "beta") [] []] []),
("beta", LModule [GI (nullCtx "gamma") [] []] []),
("gamma", LModule [GI (nullCtx "alpha") [] []] []),
("omega", LModule [GI (nullCtx "lambda") [] []] []),
("lambda", LModule [GI (nullCtx "kappa") [] [], GI (nullCtx "sigma") [] []] []),
("kappa", LModule [] []),
("sigma", LModule [] [])]
-- | Transform a script into a module, generating function names for each handler, using the
-- pattern <state-name>_state_<handler-name>.
-- The script following script:
--
-- default {
-- state_entry() {
-- llSay(0,"Hello Avatar");
-- }
-- }
--
-- would be transformed into a module equivalent to:
--
-- $module
--
-- default_state_state_entry() {
-- llSay(0,"Hello Avatar");
-- }
moduleFromScript :: CompiledLSLScript -> LModule
moduleFromScript script = LModule globDefs []
where globDefs = globDefsFromGlobs (scriptGlobals script) ++
globDefsFromFuncs (scriptFuncs script) ++
funcDefsFromStates (scriptStates script)
globDefsFromGlobs = map globDefFromGlob
globDefsFromFuncs = map GF
funcDefsFromStates = concatMap funcDefsFromState
globDefFromGlob (GDecl v me) = GV (nullCtx v) (fmap nullCtx me)
funcDefsFromState (State ctxnm handlers) = map (funcDefFromHandler (ctxItem ctxnm)) handlers
funcDefFromHandler stateName (Handler ctxnm params stmts) = GF $ nullCtx $ Func (FuncDec combinedName LLVoid params) stmts
where combinedName = nullCtx $ stateName ++ "$$" ++ (ctxItem ctxnm)
rewriteCtxExpr :: [(String,String)] -> Ctx Expr -> Ctx Expr
rewriteCtxExpr renames = everywhere (mkT (rewriteName renames))
rewriteName :: [(String,String)] -> Ctx String -> Ctx String
rewriteName renames (Ctx ctx name) =
case lookup name renames of
Nothing -> Ctx ctx name
Just name' -> Ctx ctx name'
rewriteStatements _ _ [] = []
rewriteStatements n bindings (Ctx c s:ss) =
let (n',bindings',s') = rewriteStatement n bindings s in
(Ctx c s'):(rewriteStatements n' bindings' ss)
rewriteStatement n bindings (Compound stmts) = (n, bindings, Compound $ rewriteStatements n bindings stmts)
rewriteStatement n bindings (While expr stmt) =
let (_,_,stmt') = rewriteStatement n bindings stmt in
(n, bindings, While (rewriteCtxExpr bindings expr) stmt')
rewriteStatement n bindings (DoWhile stmt expr) =
let (_,_,stmt') = rewriteStatement n bindings stmt in
(n, bindings, DoWhile stmt' (rewriteCtxExpr bindings expr))
rewriteStatement n bindings (For mexpr1 mexpr2 mexpr3 stmt) =
let (_,_,stmt') = rewriteStatement n bindings stmt
rewriteMExpr = rewriteMExpression bindings
rewriteEs = rewriteCtxExprs bindings in
(n, bindings, For (rewriteEs mexpr1) (rewriteMExpr mexpr2) (rewriteEs mexpr3) stmt')
rewriteStatement n bindings (If expr stmt1 stmt2) =
let (_,_,stmt1') = rewriteStatement n bindings stmt1
(_,_,stmt2') = rewriteStatement n bindings stmt2 in
(n, bindings, If (rewriteCtxExpr bindings expr) stmt1' stmt2')
rewriteStatement n bindings (Decl (Var name t) val) =
let (n',bindings', newname) =
if any (\(name',_) -> name == name') bindings then let newname = "local" ++ (show n) in (n + 1, (name,newname):bindings, newname)
else (n,bindings,name)
in (n',bindings',Decl (Var newname t) (rewriteMExpression bindings val))
rewriteStatement n bindings (Return Nothing) = (n, bindings, Return Nothing)
rewriteStatement n bindings (Return (Just expr)) = (n, bindings, Return $ Just $ rewriteCtxExpr bindings expr)
rewriteStatement n bindings (Do expr) = (n, bindings, Do $ rewriteCtxExpr bindings expr)
rewriteStatement n bindings s = (n, bindings, s)
rewriteCtxExprs bindings ctxExprs = map (rewriteCtxExpr bindings) ctxExprs
rewriteMExpression bindings = fmap (rewriteCtxExpr bindings)