LslPlus 0.2.0 → 0.3.0
raw patch · 31 files changed
+2534/−1131 lines, 31 files
Files
- LslPlus.cabal +16/−53
- src/Data/Generics/Extras/Schemes.hs +22/−0
- src/Language/Lsl/Internal/Compiler.hs +20/−16
- src/Language/Lsl/Internal/Constants.hs +99/−98
- src/Language/Lsl/Internal/DOMProcessing.hs +10/−1
- src/Language/Lsl/Internal/DOMSourceDescriptor.hs +5/−4
- src/Language/Lsl/Internal/DOMUnitTestDescriptor.hs +19/−19
- src/Language/Lsl/Internal/Evaluation.hs +6/−6
- src/Language/Lsl/Internal/Exec.hs +87/−107
- src/Language/Lsl/Internal/ExecInfo.hs +4/−4
- src/Language/Lsl/Internal/ExpressionHandler.hs +29/−2
- src/Language/Lsl/Internal/InternalLLFuncs.hs +17/−13
- src/Language/Lsl/Internal/Load.hs +1/−1
- src/Language/Lsl/Internal/Math.hs +3/−3
- src/Language/Lsl/Internal/Optimize.hs +947/−73
- src/Language/Lsl/Internal/OptimizerOptions.hs +3/−0
- src/Language/Lsl/Internal/Pragmas.hs +7/−0
- src/Language/Lsl/Internal/Type.hs +14/−11
- src/Language/Lsl/Internal/UnitTester.hs +1/−1
- src/Language/Lsl/Internal/WorldState.hs +6/−6
- src/Language/Lsl/NewUnitTest.hs +73/−0
- src/Language/Lsl/NewUnitTestEnv.hs +270/−0
- src/Language/Lsl/Parse.hs +113/−49
- src/Language/Lsl/Render.hs +10/−3
- src/Language/Lsl/Sim.hs +4/−4
- src/Language/Lsl/Syntax.hs +46/−618
- src/Language/Lsl/UnitTest.hs +9/−9
- src/Language/Lsl/UnitTestEnv.hs +80/−26
- src/Language/Lsl/WorldDef.hs +4/−4
- src/Text/ParserCombinators/ParsecExtras/Language.hs +128/−0
- src/Text/ParserCombinators/ParsecExtras/Token.hs +481/−0
LslPlus.cabal view
@@ -1,5 +1,5 @@ Name: LslPlus -version: 0.2.0 +version: 0.3.0 Synopsis: An execution and testing framework for the Linden Scripting Language (LSL) Description: Provides a framework for executing Linden Scripting Language scripts offline, @@ -19,50 +19,6 @@ Tested-With: GHC ==6.10.1 Extra-source-files: NOTICE --- Library --- Build-Depends: ghc >= 6.10, base >= 4.0 && < 4.1, haskell98, filepath >= 1.1.0.0 && < 1.2, parsec >= 2.1.0.0 && < 3, --- HaXml >= 1.19 && < 1.20, directory > 1 && < 1.1, mtl >= 1.1 && < 1.2, array >= 0.2 && < 0.3, --- network >= 2.1 && < 2.3, random >= 1.0, containers >= 0.1 && < 0.3, old-time, utf8-string >= 0.3 && < 0.4, --- pureMD5 >= 0.2 && < 3, bytestring >= 0.9 && < 0.10, template-haskell >= 2.3.0.0 && < 2.4, syb >= 0.1.0.0 && < 0.2.0.0 --- Exposed-Modules: Language.Lsl.Syntax, Language.Lsl.Parse, Language.Lsl.QQ, Language.Lsl.Sim --- Language.Lsl.WorldDef, Language.Lsl.UnitTest, Language.Lsl.UnitTestEnv, Language.Lsl.Render --- Other-modules: --- Language.Lsl.Internal.Animation --- Language.Lsl.Internal.AvEvents --- Language.Lsl.Internal.Breakpoint --- Language.Lsl.Internal.BreakpointsDeserialize --- Language.Lsl.Internal.BuiltInModules --- Language.Lsl.Internal.CodeHelper --- Language.Lsl.Internal.Compiler --- Language.Lsl.Internal.Constants --- Language.Lsl.Internal.DOMProcessing --- Language.Lsl.Internal.DOMSourceDescriptor --- Language.Lsl.Internal.DOMUnitTestDescriptor --- Language.Lsl.Internal.Evaluation --- Language.Lsl.Internal.EventSigs --- Language.Lsl.Internal.Exec --- Language.Lsl.Internal.ExecInfo --- Language.Lsl.Internal.ExpressionHandler --- Language.Lsl.Internal.FuncSigs --- Language.Lsl.Internal.InternalLLFuncs --- Language.Lsl.Internal.Key --- Language.Lsl.Internal.Load --- Language.Lsl.Internal.Log --- Language.Lsl.Internal.Math --- Language.Lsl.Internal.MetaData --- Language.Lsl.Internal.NumberParsing --- Language.Lsl.Internal.Optimize --- Language.Lsl.Internal.Physics --- Language.Lsl.Internal.SHA1 --- Language.Lsl.Internal.SimMetaData --- Language.Lsl.Internal.SystemTester --- Language.Lsl.Internal.TestResult --- Language.Lsl.Internal.Type --- Language.Lsl.Internal.UnitTester --- Language.Lsl.Internal.Util --- Language.Lsl.Internal.WorldState --- Language.Lsl.Internal.XmlCreate --- Hs-Source-Dirs: src, qqsrc Executable LslPlus Build-Depends: ghc >= 6.10, base >= 4.0 && < 4.1, haskell98, filepath >= 1.1.0.0 && < 1.2, parsec >= 2.1.0.0 && < 3, HaXml >= 1.19 && < 1.20, directory > 1 && < 1.1, mtl >= 1.1 && < 1.2, array >= 0.2 && < 0.3, @@ -71,14 +27,7 @@ Main-Is: LslPlus.hs Hs-Source-Dirs: src Other-modules: - Language.Lsl.Syntax - Language.Lsl.Parse - Language.Lsl.QQ - Language.Lsl.Sim - Language.Lsl.WorldDef - Language.Lsl.UnitTest - Language.Lsl.UnitTestEnv - Language.Lsl.Render + Data.Generics.Extras.Schemes Language.Lsl.Internal.AccessGenerator Language.Lsl.Internal.Animation Language.Lsl.Internal.AvEvents @@ -105,7 +54,9 @@ Language.Lsl.Internal.MetaData Language.Lsl.Internal.NumberParsing Language.Lsl.Internal.Optimize + Language.Lsl.Internal.OptimizerOptions Language.Lsl.Internal.Physics + Language.Lsl.Internal.Pragmas Language.Lsl.Internal.SHA1 Language.Lsl.Internal.SimMetaData Language.Lsl.Internal.SystemTester @@ -115,4 +66,16 @@ Language.Lsl.Internal.Util Language.Lsl.Internal.WorldState Language.Lsl.Internal.XmlCreate + Language.Lsl.NewUnitTest + Language.Lsl.NewUnitTestEnv + Language.Lsl.Parse + Language.Lsl.QQ + Language.Lsl.Render + Language.Lsl.Sim + Language.Lsl.Syntax + Language.Lsl.UnitTest + Language.Lsl.UnitTestEnv + Language.Lsl.WorldDef + Text.ParserCombinators.ParsecExtras.Language + Text.ParserCombinators.ParsecExtras.Token Ghc-Options: -fwarn-unused-imports
+ src/Data/Generics/Extras/Schemes.hs view
@@ -0,0 +1,22 @@+{-# LANGUAGE Rank2Types #-} +module Data.Generics.Extras.Schemes ( + downup, + everythingTwice + ) where + +------------------------------------------------------------------------------ + +import Data.Data +import Data.Generics.Aliases +import Control.Monad + +everythingTwice :: (r -> r -> r) -> GenericQ r -> GenericQ r -> GenericQ r +everythingTwice k f g x + = foldl k (f x) (gmapQ (everythingTwice k f g) x) `k` (g x) + + +downup :: Monad m => GenericM m -> GenericM m -> GenericM m + +downup down up x = do x' <- down x + x'' <- gmapM (downup down up) x' + up x''
src/Language/Lsl/Internal/Compiler.hs view
@@ -5,20 +5,23 @@ module Language.Lsl.Internal.Compiler(compile,main0) where import Control.Monad(when) +import qualified Data.ByteString as B +import qualified Data.ByteString.UTF8 as UTF8 import IO(Handle,hGetContents,stdin) import Language.Lsl.Internal.DOMSourceDescriptor(sourceFiles) import Language.Lsl.Internal.Load(loadModules,loadScripts) import Language.Lsl.Render(renderCompiledScript) +import Language.Lsl.Internal.OptimizerOptions(OptimizerOption(..)) import Language.Lsl.Syntax(AugmentedLibrary(..),CompiledLSLScript(..),Ctx(..),Func(..),Global(..), GlobDef(..),Handler(..),LModule(..),SourceContext(..),State(..),Validity,Var(..), - funcName,funcParms,funcType,libFromAugLib) + TextLocation(..),funcName,funcParms,funcType,libFromAugLib) import Language.Lsl.Internal.Type(lslTypeString) import System.Directory(doesFileExist,removeFile) import System.FilePath(replaceExtension) import System.Time(calendarTimeToString,getClockTime,toCalendarTime) import Text.XML.HaXml(Document(..),xmlParse) --hiding (when,xmlEscape) import Text.XML.HaXml.Posn(Posn(..)) -import Language.Lsl.Internal.Optimize(optimizeScript) +import Language.Lsl.Internal.Optimize(optimizeScript,OptimizerOption(..)) import Language.Lsl.Internal.XmlCreate hiding (emit) import qualified Language.Lsl.Internal.XmlCreate as E @@ -26,15 +29,15 @@ readCompileEmit :: Handle -> IO () readCompileEmit h = - do sourceInfo@(_,scriptInfo) <- readSourceList h + do sourceInfo@(optimize,_,scriptInfo) <- readSourceList h results@(_,compiledScripts) <- compile sourceInfo - renderScriptsToFiles compiledScripts scriptInfo + renderScriptsToFiles optimize compiledScripts scriptInfo putStr $ formatCompilationSummary results main0 = readCompileEmit stdin -compile :: ([(String,String)],[(String,String)]) -> IO (AugmentedLibrary,[(String,Validity CompiledLSLScript)]) -compile (moduleInfo,scriptInfo) = +compile :: (Bool,[(String,String)],[(String,String)]) -> IO (AugmentedLibrary,[(String,Validity CompiledLSLScript)]) +compile (_,moduleInfo,scriptInfo) = do augLib <- loadModules moduleInfo scripts <- loadScripts (libFromAugLib augLib) scriptInfo return (augLib,scripts) @@ -73,7 +76,7 @@ emit "globals" (map emitGlobal globals ++ map emitFreeVar freevars)]) where funcs globdefs = [ f | GF f <- globdefs] -emitFunc (Func fd _) = +emitFunc (Ctx _ (Func fd _)) = emit "entryPoint" [ emit "name" [showString (ctxItem $ funcName fd)], emit "returnType" [showString (lslTypeString $ funcType fd)], @@ -101,28 +104,28 @@ formatErr (ctx,msg) = emit "itemError" [formatCtx ctx , emit "msg" [showString (xmlEscape msg)]] -formatCtx UnknownSourceContext = id -formatCtx (TextLocation { textLine0 = l0, textColumn0 = c0, textLine1 = l1, textColumn1 = c1, textName = n }) = +formatCtx Nothing = id +formatCtx (Just (SourceContext { srcTextLocation = TextLocation { textLine0 = l0, textColumn0 = c0, textLine1 = l1, textColumn1 = c1, textName = n }})) = emit "errLoc" (map (\ (x,y) -> emit x [showString y]) [("lineStart",show l0), ("columnStart",show c0), ("lineEnd",show l1), ("columnEnd",show c1)]) -readSourceList :: Handle -> IO ([(String,String)],[(String,String)]) +readSourceList :: Handle -> IO (Bool,[(String,String)],[(String,String)]) readSourceList handle = do input <- hGetContents handle let doc = xmlParse "" input return $ processCompileList doc -processCompileList :: Document Posn -> ([(String,String)],[(String,String)]) +processCompileList :: Document Posn -> (Bool,[(String,String)],[(String,String)]) processCompileList (Document _ _ root _) = case sourceFiles root of Left s -> error s Right v -> v -renderScriptsToFiles :: [(String,Validity CompiledLSLScript)] -> [(String,String)] -> IO () -renderScriptsToFiles compiledScripts pathTable = +renderScriptsToFiles :: Bool -> [(String,Validity CompiledLSLScript)] -> [(String,String)] -> IO () +renderScriptsToFiles opt compiledScripts pathTable = let scriptsToRender = [(path,script) | (Just path,Right script) <- map (\ (name,vs) -> (lookup name pathTable,vs)) compiledScripts] scriptsToRemove = @@ -131,12 +134,13 @@ clockTime <- getClockTime calTime <- toCalendarTime clockTime let stamp = calendarTimeToString calTime - mapM_ (\ (path,script) -> renderScriptToFile stamp path script) scriptsToRender + mapM_ (\ (path,script) -> renderScriptToFile opt stamp path script) scriptsToRender mapM_ (removeOutputScript) scriptsToRemove -renderScriptToFile stamp path script = +renderScriptToFile opt stamp path script = let newPath = replaceExtension path ".lsl" - text = renderCompiledScript stamp (optimizeScript script) in writeFile newPath text + options = if opt then [OptimizationInlining] else [] + text = renderCompiledScript stamp (optimizeScript options script) in B.writeFile newPath (UTF8.fromString text) removeOutputScript path = do exists <- doesFileExist outpath
src/Language/Lsl/Internal/Constants.hs view
@@ -1,52 +1,53 @@+{-# OPTIONS_GHC -XNoMonomorphismRestriction #-} module Language.Lsl.Internal.Constants where import Data.Bits((.|.),shiftL) import Language.Lsl.Internal.Type(LSLValue(..),typeOfLSLValue) import Language.Lsl.Internal.Util(findM) -data Constant = Constant { constName :: String, constVal :: LSLValue }+data Constant a = Constant { constName :: String, constVal :: LSLValue a } deriving (Show) -llcInventoryAll = IVal (-1)-llcInventoryAnimation = IVal 20-llcInventoryBodyPart = IVal 13-llcInventoryClothing = IVal 5-llcInventoryGesture = IVal 21-llcInventoryLandmark = IVal 3-llcInventoryNotecard = IVal 7-llcInventoryNone = IVal (-1)-llcInventoryObject = IVal 6-llcInventoryScript = IVal 10-llcInventorySound = IVal 1-llcInventoryTexture = IVal 0+cInventoryAll = (-1);llcInventoryAll :: RealFloat a => LSLValue a;llcInventoryAll = IVal cInventoryAll+cInventoryAnimation = 20;llcInventoryAnimation :: RealFloat a => LSLValue a; llcInventoryAnimation = IVal cInventoryAnimation+cInventoryBodyPart = 13;llcInventoryBodyPart :: RealFloat a => LSLValue a; llcInventoryBodyPart = IVal cInventoryBodyPart+cInventoryClothing = 5;llcInventoryClothing :: RealFloat a => LSLValue a; llcInventoryClothing = IVal cInventoryClothing+cInventoryGesture = 21;llcInventoryGesture :: RealFloat a => LSLValue a; llcInventoryGesture = IVal cInventoryGesture+cInventoryLandmark = 3;llcInventoryLandmark :: RealFloat a => LSLValue a; llcInventoryLandmark = IVal cInventoryLandmark+cInventoryNotecard = 7;llcInventoryNotecard :: RealFloat a => LSLValue a; llcInventoryNotecard = IVal cInventoryNotecard+cInventoryNone = (-1);llcInventoryNone :: RealFloat a => LSLValue a;llcInventoryNone = IVal cInventoryNone+cInventoryObject = 6;llcInventoryObject :: RealFloat a => LSLValue a; llcInventoryObject = IVal cInventoryObject+cInventoryScript = 10;llcInventoryScript :: RealFloat a => LSLValue a; llcInventoryScript = IVal cInventoryScript+cInventorySound = 1;llcInventorySound :: RealFloat a => LSLValue a; llcInventorySound = IVal cInventorySound+cInventoryTexture = 0;llcInventoryTexture :: RealFloat a => LSLValue a; llcInventoryTexture = IVal cInventoryTexture cPermissionChangeLinks = 0x80 :: Int llcPermissionChangeLinks = IVal cPermissionChangeLinks cChangedLink = 0x20 :: Int llcChangedLink = IVal cChangedLink-(cChangedInventory,llcChangedInventory) = mkIConst 0x1-(cChnagedAllowedDrop,llcChangedAllowedDrop) = mkIConst 0x40+cChangedInventory = 0x1;llcChangedInventory :: RealFloat a => LSLValue a; llcChangedInventory = IVal cChangedInventory+cChangedAllowedDrop = 0x40;llcChangedAllowedDrop :: RealFloat a => LSLValue a; llcChangedAllowedDrop = IVal cChangedAllowedDrop -(cMaskBase,llcMaskBase) = mkIConst 0-(cMaskOwner,llcMaskOwner) = mkIConst 1-(cMaskGroup,llcMaskGroup) = mkIConst 2-(cMaskEveryone,llcMaskEveryone) = mkIConst 3-(cMaskNext,llcMaskNext) = mkIConst 4+cMaskBase = 0;llcMaskBase :: RealFloat a => LSLValue a; llcMaskBase = IVal cMaskBase+cMaskOwner = 1;llcMaskOwner :: RealFloat a => LSLValue a; llcMaskOwner = IVal cMaskOwner+cMaskGroup = 2;llcMaskGroup :: RealFloat a => LSLValue a; llcMaskGroup = IVal cMaskGroup+cMaskEveryone = 3;llcMaskEveryone :: RealFloat a => LSLValue a; llcMaskEveryone = IVal cMaskEveryone+cMaskNext = 4;llcMaskNext :: RealFloat a => LSLValue a; llcMaskNext = IVal cMaskNext -(cPermModify,llcPermModify) = mkIConst 0x00004000-(cPermCopy,llcPermCopy) = mkIConst 0x00008000-(cPermTransfer,llcPermTransfer) = mkIConst 0x00002000-(cPermMove,llcPermMove) = mkIConst 0x00080000+cPermModify = 0x00004000;llcPermModify :: RealFloat a => LSLValue a; llcPermModify = IVal cPermModify+cPermCopy = 0x00008000;llcPermCopy :: RealFloat a => LSLValue a; llcPermCopy = IVal cPermCopy+cPermTransfer = 0x00002000;llcPermTransfer :: RealFloat a => LSLValue a; llcPermTransfer = IVal cPermTransfer+cPermMove = 0x00080000;llcPermMove :: RealFloat a => LSLValue a; llcPermMove = IVal cPermMove cFullPerm = cPermModify .|. cPermMove .|. cPermTransfer .|. cPermCopy -(cPrimTypeBox,llcPrimTypeBox) = mkIConst 0-(cPrimTypeCylinder,llcPrimTypeCylinder) = mkIConst 1-(cPrimTypePrism,llcPrimTypePrism) = mkIConst 2-(cPrimTypeRing,llcPrimTypeRing) = mkIConst 6-(cPrimTypeSphere,llcPrimTypeSphere) = mkIConst 3-(cPrimTypeSculpt,llcPrimTypeSculpt) = mkIConst 7-(cPrimTypeTorus,llcPrimTypeTorus) = mkIConst 4-(cPrimTypeTube,llcPrimTypeTube) = mkIConst 5+cPrimTypeBox = 0;llcPrimTypeBox :: RealFloat a => LSLValue a; llcPrimTypeBox = IVal cPrimTypeBox+cPrimTypeCylinder = 1;llcPrimTypeCylinder :: RealFloat a => LSLValue a; llcPrimTypeCylinder = IVal cPrimTypeCylinder+cPrimTypePrism = 2;llcPrimTypePrism :: RealFloat a => LSLValue a; llcPrimTypePrism = IVal cPrimTypePrism+cPrimTypeRing = 6;llcPrimTypeRing :: RealFloat a => LSLValue a; llcPrimTypeRing = IVal cPrimTypeRing+cPrimTypeSphere = 3;llcPrimTypeSphere :: RealFloat a => LSLValue a; llcPrimTypeSphere = IVal cPrimTypeSphere+cPrimTypeSculpt = 7;llcPrimTypeSculpt :: RealFloat a => LSLValue a; llcPrimTypeSculpt = IVal cPrimTypeSculpt+cPrimTypeTorus = 4;llcPrimTypeTorus :: RealFloat a => LSLValue a; llcPrimTypeTorus = IVal cPrimTypeTorus+cPrimTypeTube = 5;llcPrimTypeTube :: RealFloat a => LSLValue a; llcPrimTypeTube = IVal cPrimTypeTube validAttachmentPoints = [0..36]::[Int] @@ -58,88 +59,88 @@ cPermissionControlCamera = 0x800 :: Int llcPermissionControlCamera = IVal cPermissionControlCamera-(cPermissionTrackCamera,llcPermissionTrackCamera) = mkIConst 0x400-(cPermissionTriggerAnimation,llcPermissionTriggerAnimation) = mkIConst 0x10-(cPermissionDebit,llcPermissionDebit) = mkIConst 0x2-(cPermissionAttach,llcPermissionAttach) = mkIConst 0x20-(cPermissionTakeControls,llcPermissionTakeControls) = mkIConst 0x4+cPermissionTrackCamera = 0x400;llcPermissionTrackCamera :: RealFloat a => LSLValue a; llcPermissionTrackCamera = IVal cPermissionTrackCamera+cPermissionTriggerAnimation = 0x10;llcPermissionTriggerAnimation :: RealFloat a => LSLValue a; llcPermissionTriggerAnimation = IVal cPermissionTriggerAnimation+cPermissionDebit = 0x2;llcPermissionDebit :: RealFloat a => LSLValue a; llcPermissionDebit = IVal cPermissionDebit+cPermissionAttach = 0x20;llcPermissionAttach :: RealFloat a => LSLValue a; llcPermissionAttach = IVal cPermissionAttach+cPermissionTakeControls = 0x4;llcPermissionTakeControls :: RealFloat a => LSLValue a; llcPermissionTakeControls = IVal cPermissionTakeControls -(cActive,llcActive) = mkIConst 0x2-(cAgent,llcAgent) = mkIConst 0x1-(cPassive,llcPassive) = mkIConst 0x4-(cScripted,llcScripted) = mkIConst 0x8+cActive = 0x2;llcActive :: RealFloat a => LSLValue a; llcActive = IVal cActive+cAgent = 0x1;llcAgent :: RealFloat a => LSLValue a; llcAgent = IVal cAgent+cPassive = 0x4;llcPassive :: RealFloat a => LSLValue a; llcPassive = IVal cPassive+cScripted = 0x8;llcScripted :: RealFloat a => LSLValue a; llcScripted = IVal cScripted -(cStatusPhysics,llcStatusPhysics) = mkIConst 1-(cStatusRotateX,llcStatusRotateX) = mkIConst 2-(cStatusRotateY,llcStatusRotateY) = mkIConst 4-(cStatusRotateZ,llcStatusRotateZ) = mkIConst 8-(cStatusPhantom,llcStatusPhantom) = mkIConst 16-(cStatusSandbox,llcStatusSandbox) = mkIConst 32-(cStatusBlockGrab,llcStatusBlockGrab) = mkIConst 64-(cStatusDieAtEdge,llcStatusDieAtEdge) = mkIConst 128-(cStatusReturnAtEdge,llcStatusReturnAtEdge) = mkIConst 256-(cStatusCastShadows,llcStatusCastShadows) = mkIConst 512+cStatusPhysics = 1;llcStatusPhysics :: RealFloat a => LSLValue a; llcStatusPhysics = IVal cStatusPhysics+cStatusRotateX = 2;llcStatusRotateX :: RealFloat a => LSLValue a; llcStatusRotateX = IVal cStatusRotateX+cStatusRotateY = 4;llcStatusRotateY :: RealFloat a => LSLValue a; llcStatusRotateY = IVal cStatusRotateY+cStatusRotateZ = 8;llcStatusRotateZ :: RealFloat a => LSLValue a; llcStatusRotateZ = IVal cStatusRotateZ+cStatusPhantom = 16;llcStatusPhantom :: RealFloat a => LSLValue a; llcStatusPhantom = IVal cStatusPhantom+cStatusSandbox = 32;llcStatusSandbox :: RealFloat a => LSLValue a; llcStatusSandbox = IVal cStatusSandbox+cStatusBlockGrab = 64;llcStatusBlockGrab :: RealFloat a => LSLValue a; llcStatusBlockGrab = IVal cStatusBlockGrab+cStatusDieAtEdge = 128;llcStatusDieAtEdge :: RealFloat a => LSLValue a; llcStatusDieAtEdge = IVal cStatusDieAtEdge+cStatusReturnAtEdge = 256;llcStatusReturnAtEdge :: RealFloat a => LSLValue a; llcStatusReturnAtEdge = IVal cStatusReturnAtEdge+cStatusCastShadows = 512;llcStatusCastShadows :: RealFloat a => LSLValue a; llcStatusCastShadows = IVal cStatusCastShadows -(cPrimBumpShiny,llcPrimBumpShiny) = mkIConst 19-(cPrimColor,llcPrimColor) = mkIConst 18-(cPrimTexture,llcPrimTexture) = mkIConst 17-(cPrimTexgen,llcPrimTexgen) = mkIConst 22-(cPrimFullbright,llcPrimFullbright) = mkIConst 20+cPrimBumpShiny = 19;llcPrimBumpShiny :: RealFloat a => LSLValue a; llcPrimBumpShiny = IVal cPrimBumpShiny+cPrimColor = 18;llcPrimColor :: RealFloat a => LSLValue a; llcPrimColor = IVal cPrimColor+cPrimTexture = 17;llcPrimTexture :: RealFloat a => LSLValue a; llcPrimTexture = IVal cPrimTexture+cPrimTexgen = 22;llcPrimTexgen :: RealFloat a => LSLValue a; llcPrimTexgen = IVal cPrimTexgen+cPrimFullbright = 20;llcPrimFullbright :: RealFloat a => LSLValue a; llcPrimFullbright = IVal cPrimFullbright -(cPrimMaterial,llcPrimMaterial) = mkIConst 2-(cPrimPhantom,llcPrimPhantom) = mkIConst 5-(cPrimPhysics,llcPrimPhysics) = mkIConst 3-(cPrimFlexible,llcPrimFlexible) = mkIConst 21-(cPrimPointLight,llcPrimPointLight) = mkIConst 23-(cPrimPosition,llcPrimPosition) = mkIConst 6-(cPrimRotation,llcPrimRotation) = mkIConst 8-(cPrimSize,llcPrimSize) = mkIConst 7-(cPrimTempOnRez,llcPrimTempOnRez) = mkIConst 4 -(cPrimType,llcPrimType) = mkIConst 9+cPrimMaterial = 2;llcPrimMaterial :: RealFloat a => LSLValue a; llcPrimMaterial = IVal cPrimMaterial+cPrimPhantom = 5;llcPrimPhantom :: RealFloat a => LSLValue a; llcPrimPhantom = IVal cPrimPhantom+cPrimPhysics = 3;llcPrimPhysics :: RealFloat a => LSLValue a; llcPrimPhysics = IVal cPrimPhysics+cPrimFlexible = 21;llcPrimFlexible :: RealFloat a => LSLValue a; llcPrimFlexible = IVal cPrimFlexible+cPrimPointLight = 23;llcPrimPointLight :: RealFloat a => LSLValue a; llcPrimPointLight = IVal cPrimPointLight+cPrimPosition = 6;llcPrimPosition :: RealFloat a => LSLValue a; llcPrimPosition = IVal cPrimPosition+cPrimRotation = 8;llcPrimRotation :: RealFloat a => LSLValue a; llcPrimRotation = IVal cPrimRotation+cPrimSize = 7;llcPrimSize :: RealFloat a => LSLValue a; llcPrimSize = IVal cPrimSize+cPrimTempOnRez = 4;llcPrimTempOnRez :: RealFloat a => LSLValue a; llcPrimTempOnRez = IVal cPrimTempOnRez +cPrimType = 9;llcPrimType :: RealFloat a => LSLValue a; llcPrimType = IVal cPrimType -(cParcelDetailsName,llcParcelDetailsName) = mkIConst 0-(cParcelDetailsDesc,llcParcelDetailsDesc) = mkIConst 1-(cParcelDetailsOwner,llcParcelDetailsOwner) = mkIConst 2-(cParcelDetailsGroup,llcParcelDetailsGroup) = mkIConst 3 -(cParcelDetailsArea,llcParcelDetailsArea) = mkIConst 4+cParcelDetailsName = 0;llcParcelDetailsName :: RealFloat a => LSLValue a; llcParcelDetailsName = IVal cParcelDetailsName+cParcelDetailsDesc = 1;llcParcelDetailsDesc :: RealFloat a => LSLValue a; llcParcelDetailsDesc = IVal cParcelDetailsDesc+cParcelDetailsOwner = 2;llcParcelDetailsOwner :: RealFloat a => LSLValue a; llcParcelDetailsOwner = IVal cParcelDetailsOwner+cParcelDetailsGroup = 3;llcParcelDetailsGroup :: RealFloat a => LSLValue a; llcParcelDetailsGroup = IVal cParcelDetailsGroup +cParcelDetailsArea = 4;llcParcelDetailsArea :: RealFloat a => LSLValue a; llcParcelDetailsArea = IVal cParcelDetailsArea -(cClickActionNone,llcClickActionNone) = mkIConst 0-(cClickActionTouch,llcClickActionTouch) = mkIConst 0-(cClickActionSit,llcClickActionSit) = mkIConst 1-(cClickActionBuy,llcClickActionBuy) = mkIConst 2-(cClickActionPay,llcClickActionPay) = mkIConst 3-(cClickActionOpen,llcClickActionOpen) = mkIConst 4-(cClickActionPlay,llcClickActionPlay) = mkIConst 5-(cClickActionOpenMedia,llcClickActionOpenMedia) = mkIConst 6+cClickActionNone = 0;llcClickActionNone :: RealFloat a => LSLValue a; llcClickActionNone = IVal cClickActionNone+cClickActionTouch = 0;llcClickActionTouch :: RealFloat a => LSLValue a; llcClickActionTouch = IVal cClickActionTouch+cClickActionSit = 1;llcClickActionSit :: RealFloat a => LSLValue a; llcClickActionSit = IVal cClickActionSit+cClickActionBuy = 2;llcClickActionBuy :: RealFloat a => LSLValue a; llcClickActionBuy = IVal cClickActionBuy+cClickActionPay = 3;llcClickActionPay :: RealFloat a => LSLValue a; llcClickActionPay = IVal cClickActionPay+cClickActionOpen = 4;llcClickActionOpen :: RealFloat a => LSLValue a; llcClickActionOpen = IVal cClickActionOpen+cClickActionPlay = 5;llcClickActionPlay :: RealFloat a => LSLValue a; llcClickActionPlay = IVal cClickActionPlay+cClickActionOpenMedia = 6;llcClickActionOpenMedia :: RealFloat a => LSLValue a; llcClickActionOpenMedia = IVal cClickActionOpenMedia cClickActions = [cClickActionTouch,cClickActionSit,cClickActionBuy,cClickActionPay,cClickActionOpen,cClickActionPlay,cClickActionOpenMedia] -(cDataBorn,llcDataBorn) = mkIConst 3-(cDataName,llcDataName) = mkIConst 2-(cDataOnline,llcDataOnline) = mkIConst 1-(cDataPayinfo,llcDataPayinfo) = mkIConst 8-(cDataRating,llcDataRating) = mkIConst 4-(cDataSimPos,llcDataSimPos) = mkIConst 5-(cDataSimRating,llcDataSimRating) = mkIConst 7-(cDataSimStatus,llcDataSimStatus) = mkIConst 6+cDataBorn = 3;llcDataBorn :: RealFloat a => LSLValue a; llcDataBorn = IVal cDataBorn+cDataName = 2;llcDataName :: RealFloat a => LSLValue a; llcDataName = IVal cDataName+cDataOnline = 1;llcDataOnline :: RealFloat a => LSLValue a; llcDataOnline = IVal cDataOnline+cDataPayinfo = 8;llcDataPayinfo :: RealFloat a => LSLValue a; llcDataPayinfo = IVal cDataPayinfo+cDataRating = 4;llcDataRating :: RealFloat a => LSLValue a; llcDataRating = IVal cDataRating+cDataSimPos = 5;llcDataSimPos :: RealFloat a => LSLValue a; llcDataSimPos = IVal cDataSimPos+cDataSimRating = 7;llcDataSimRating :: RealFloat a => LSLValue a; llcDataSimRating = IVal cDataSimRating+cDataSimStatus = 6;llcDataSimStatus :: RealFloat a => LSLValue a; llcDataSimStatus = IVal cDataSimStatus -(cHTTPBodyMaxlength,llcHTTPBodyMaxlength) = mkIConst 2-(cHTTPBodyTruncated,llcHTTPBodyTruncated) = mkIConst 0-(cHTTPMethod,llcHTTPMethod) = mkIConst 0-(cHTTPMimetype,llcHTTPMimetype) = mkIConst 1-(cHTTPVerifyCert,llcHTTPVerifyCert) = mkIConst 3+cHTTPBodyMaxlength = 2;llcHTTPBodyMaxlength :: RealFloat a => LSLValue a; llcHTTPBodyMaxlength = IVal cHTTPBodyMaxlength+cHTTPBodyTruncated = 0;llcHTTPBodyTruncated :: RealFloat a => LSLValue a; llcHTTPBodyTruncated = IVal cHTTPBodyTruncated+cHTTPMethod = 0;llcHTTPMethod :: RealFloat a => LSLValue a; llcHTTPMethod = IVal cHTTPMethod+cHTTPMimetype = 1;llcHTTPMimetype :: RealFloat a => LSLValue a; llcHTTPMimetype = IVal cHTTPMimetype+cHTTPVerifyCert = 3;llcHTTPVerifyCert :: RealFloat a => LSLValue a; llcHTTPVerifyCert = IVal cHTTPVerifyCert -(cRemoteDataChannel,llcRemoteDataChannel) = mkIConst 1-(cRemoteDataRequest,llcRemoteDataRequest) = mkIConst 2-(cRemoteDataReply,llcRemoteDataReply) = mkIConst 3+cRemoteDataChannel = 1;llcRemoteDataChannel :: RealFloat a => LSLValue a; llcRemoteDataChannel = IVal cRemoteDataChannel+cRemoteDataRequest = 2;llcRemoteDataRequest :: RealFloat a => LSLValue a; llcRemoteDataRequest = IVal cRemoteDataRequest+cRemoteDataReply = 3;llcRemoteDataReply :: RealFloat a => LSLValue a; llcRemoteDataReply = IVal cRemoteDataReply llcZeroVector = VVal 0 0 0 llcZeroRotation = RVal 0 0 0 1 -mkIConst :: Int -> (Int,LSLValue)+mkIConst :: RealFloat a => Int -> (Int,LSLValue a) mkIConst i = (i,IVal i) -allConstants :: [Constant]+allConstants :: RealFloat a => [Constant a] allConstants = [ Constant "ACTIVE" llcActive, Constant "AGENT" llcAgent,@@ -248,7 +249,7 @@ Constant "DATA_SIM_STATUS" llcDataSimStatus, Constant "DEBUG_CHANNEL" llcDebugChannel, Constant "DEG_TO_RAD" (FVal 0.01745329238),- Constant "EOF" llcEOF,+ Constant "EOF" $ SVal cEOF, Constant "FALSE" (IVal 0), Constant "HTTP_BODY_MAXLENGTH" llcHTTPBodyMaxlength, Constant "HTTP_BODY_TRUNCATED" llcHTTPBodyTruncated,
src/Language/Lsl/Internal/DOMProcessing.hs view
@@ -5,6 +5,7 @@ findSimple, findSimpleOrDefault, findValueOrDefault, + findBoolOrDefault, findValue, valueAcceptor, -- String -> ElemAcceptor m t ctxelem, @@ -116,13 +117,21 @@ value <- readM sval return (value,rest) +findBoolOrDefault def name contents = + do (sval,rest) <- findOptionalElement (simpleElement name) contents + case sval of + Nothing -> return (def,contents) + Just "true" -> return (True,rest) + Just "false" -> return (False,rest) + Just s -> fail ("unable to parse " ++ s) + findValueOrDefault def name contents = do (sval,rest) <- findOptionalElement (simpleElement name) contents case sval of Nothing -> return (def, contents) Just s -> do value <- readM s - return (value, contents) + return (value, rest) -- for backwards compatibility attrString (AttValue v) = concatMap decode v
src/Language/Lsl/Internal/DOMSourceDescriptor.hs view
@@ -2,17 +2,18 @@ module Language.Lsl.Internal.DOMSourceDescriptor(sourceFiles,sourceFilesElement) where import Control.Monad.Error(MonadError(..)) -import Language.Lsl.Internal.DOMProcessing(ElemAcceptor(..),elementList,findElement,match,simple) +import Language.Lsl.Internal.DOMProcessing(ElemAcceptor(..),elementList,findElement,match,simple,findBoolOrDefault) import Text.XML.HaXml(Element(..),Content(..)) sourceFiles e = match sourceFilesElement e -sourceFilesElement :: MonadError String m => ElemAcceptor m ([(String,String)],[(String,String)]) +sourceFilesElement :: MonadError String m => ElemAcceptor m (Bool,[(String,String)],[(String,String)]) sourceFilesElement = let f (Elem _ _ contents) = do - (m,contents1) <- findElement modulesElement [ e | e@(CElem _ _) <- contents ] + (optimize,contents0) <- findBoolOrDefault False "optimize" contents + (m,contents1) <- findElement modulesElement [ e | e@(CElem _ _) <- contents0 ] (s,[]) <- findElement scriptsElement contents1 - return (m,s) + return (optimize,m,s) in ElemAcceptor "source_files" f modulesElement :: MonadError String m => ElemAcceptor m [(String,String)]
src/Language/Lsl/Internal/DOMUnitTestDescriptor.hs view
@@ -56,58 +56,58 @@ pathElement :: MonadError String m => ElemAcceptor m String pathElement = ElemAcceptor "path" simple -argumentsElement :: MonadError String m => ElemAcceptor m [LSLValue] +argumentsElement :: MonadError String m => ElemAcceptor m [LSLValue Float] argumentsElement = let f (Elem _ _ contents) = mapM (matchChoice [lslString, lslInteger, lslFloat, lslVector, lslRotation, lslKey, lslList1]) (elementsOnly contents) in ElemAcceptor "arguments" f -lslString :: MonadError String m => ElemAcceptor m LSLValue +lslString :: MonadError String m => ElemAcceptor m (LSLValue Float) lslString = ElemAcceptor "lsl-string" (\ e -> do s <- simple e case evaluateExpression LLString s of Just k -> return k Nothing -> fail "invalid content for lsl-string") -lslKey :: MonadError String m => ElemAcceptor m LSLValue +lslKey :: MonadError String m => ElemAcceptor m (LSLValue Float) lslKey = ElemAcceptor "lsl-key" (\ e -> do s <- simple e case evaluateExpression LLKey s of Just k -> return k Nothing -> fail "invalid content for lsl-key") -lslInteger :: MonadError String m => ElemAcceptor m LSLValue +lslInteger :: MonadError String m => ElemAcceptor m (LSLValue Float) lslInteger = ElemAcceptor "lsl-integer" (\ e -> do s <- simple e case evaluateExpression LLInteger s of Just f -> return f Nothing -> fail "invalid content for lsl-integer") -lslFloat :: MonadError String m => ElemAcceptor m LSLValue +lslFloat :: MonadError String m => ElemAcceptor m (LSLValue Float) lslFloat = ElemAcceptor "lsl-float" (\ e -> do s <- simple e case evaluateExpression LLFloat s of Just f -> return f Nothing -> fail "invalid content for lsl-float") -lslVector :: MonadError String m => ElemAcceptor m LSLValue +lslVector :: MonadError String m => ElemAcceptor m (LSLValue Float) lslVector = ElemAcceptor "lsl-vector" (\ e -> do s <- simple e case evaluateExpression LLVector s of Just f -> return f Nothing -> fail "invalid content for lsl-vector") -lslRotation :: MonadError String m => ElemAcceptor m LSLValue +lslRotation :: MonadError String m => ElemAcceptor m (LSLValue Float) lslRotation = ElemAcceptor "lsl-rotation" (\ e -> do s <- simple e case evaluateExpression LLRot s of Just f -> return f Nothing -> fail "invalid content for lsl-rotation") -lslList :: MonadError String m => ElemAcceptor m LSLValue +lslList :: MonadError String m => ElemAcceptor m (LSLValue Float) lslList = let f (Elem _ _ contents) = do list <- mapM (matchChoice [lslString,lslInteger,lslKey,lslVector,lslRotation,lslFloat]) (elementsOnly contents) return (LVal list) in ElemAcceptor "lsl-list" f -lslList1 :: MonadError String m => ElemAcceptor m LSLValue +lslList1 :: MonadError String m => ElemAcceptor m (LSLValue Float) lslList1 =ElemAcceptor "lsl-list1" (\ e -> do s <- simple e case evaluateExpression LLList s of @@ -115,17 +115,17 @@ Nothing -> fail "invalid content for lsl-list1") -lslVoid :: MonadError String m => ElemAcceptor m LSLValue +lslVoid :: MonadError String m => ElemAcceptor m (LSLValue Float) lslVoid = let f (Elem _ _ []) = return VoidVal f (Elem name _ _) = fail ("unexpected content in " ++ name ++ " tag.") in ElemAcceptor "lsl-void" f -expectedReturnElement :: MonadError String m => ElemAcceptor m (Maybe LSLValue) +expectedReturnElement :: MonadError String m => ElemAcceptor m (Maybe (LSLValue Float)) expectedReturnElement = maybeSomeVal "expectedReturn" -expectationsElement :: MonadError String m => ElemAcceptor m FuncCallExpectations +expectationsElement :: MonadError String m => ElemAcceptor m (FuncCallExpectations Float) expectationsElement = let f (Elem _ _ contents) = do (modeString,contents1) <- findElement modeElement (elementsOnly contents) @@ -145,10 +145,10 @@ modeElement :: MonadError String m => ElemAcceptor m String modeElement = ElemAcceptor "mode" simple -callsElement :: MonadError String m => ElemAcceptor m [((String, [Maybe LSLValue]),LSLValue)] +callsElement :: MonadError String m => ElemAcceptor m [((String, [Maybe (LSLValue Float)]),LSLValue Float)] callsElement = elementList "calls" callElement -callElement :: MonadError String m => ElemAcceptor m ((String, [Maybe LSLValue]),LSLValue) +callElement :: MonadError String m => ElemAcceptor m ((String, [Maybe (LSLValue Float)]),LSLValue Float) callElement = let f (Elem _ _ contents) = do (name,contents1) <- findElement (ElemAcceptor "name" simple) (elementsOnly contents) @@ -157,19 +157,19 @@ return ((name,args),retval) in ElemAcceptor "call" f -callArgsElement :: MonadError String m => ElemAcceptor m [Maybe LSLValue] +callArgsElement :: MonadError String m => ElemAcceptor m [Maybe (LSLValue Float)] callArgsElement = elementList "args" (maybeSomeVal "maybe-value") -returnsElement :: MonadError String m => ElemAcceptor m LSLValue +returnsElement :: MonadError String m => ElemAcceptor m (LSLValue Float) returnsElement = someVal "returns" -initialBindingsElement :: MonadError String m => ElemAcceptor m [(String,LSLValue)] +initialBindingsElement :: MonadError String m => ElemAcceptor m [(String,LSLValue Float)] initialBindingsElement = elementList "initialBindings" globalBindingElement -finalBindingsElement :: MonadError String m => ElemAcceptor m [(String,LSLValue)] +finalBindingsElement :: MonadError String m => ElemAcceptor m [(String,LSLValue Float)] finalBindingsElement = elementList "finalBindings" globalBindingElement -globalBindingElement :: MonadError String m => ElemAcceptor m (String,LSLValue) +globalBindingElement :: MonadError String m => ElemAcceptor m (String,LSLValue Float) globalBindingElement = let f (Elem _ _ contents) = do (name,contents1) <- findElement (ElemAcceptor "name" simple) (elementsOnly contents)
src/Language/Lsl/Internal/Evaluation.hs view
@@ -8,17 +8,17 @@ import Language.Lsl.Internal.Breakpoint(Breakpoint) --type ScriptInfo = (String,Int,String,String) -- (object id, prim index, script name, prim key) -data ScriptInfo = ScriptInfo { scriptInfoObjectKey :: String, - scriptInfoPrimIndex :: Int, - scriptInfoScriptName :: String, - scriptInfoPrimKey :: String, - scriptInfoCurrentEvent :: Maybe Event } +data ScriptInfo a = ScriptInfo { scriptInfoObjectKey :: String, + scriptInfoPrimIndex :: Int, + scriptInfoScriptName :: String, + scriptInfoPrimKey :: String, + scriptInfoCurrentEvent :: Maybe (Event a) } deriving (Show) data EvalResult = EvalIncomplete | EvalComplete (Maybe String) | YieldTil Int | BrokeAt Breakpoint deriving (Show) -data Event = Event { eventName :: String, eventValues :: [LSLValue], eventInfo :: Map String LSLValue } +data Event a = Event { eventName :: String, eventValues :: [LSLValue a], eventInfo :: Map String (LSLValue a) } deriving (Show)
src/Language/Lsl/Internal/Exec.hs view
@@ -1,3 +1,4 @@+{-# OPTIONS_GHC -XNoMonomorphismRestriction #-} module Language.Lsl.Internal.Exec( ScriptImage(..), EvalState, @@ -38,6 +39,7 @@ State(..), Ctx(..), Global(..), + TextLocation(..), SourceContext(..), Handler(..), ctxVr2Vr, @@ -49,7 +51,7 @@ predefFuncs, isTextLocation) import Language.Lsl.Internal.Type(LSLType(..),LSLValue(..),typeOfLSLComponent,typeOfLSLValue,toFloat,toSVal, - lslShowVal,replaceLslValueComponent,vecMulScalar,rotMulVec, + lslShowVal,replaceLslValueComponent,vecMulScalar,rotMulVec,parseVector,parseRotation, parseInt,parseFloat,invRot,rotMul,vcross,Component(..),lslValueComponent) import Language.Lsl.Internal.Key(nullKey,nextKey) import Language.Lsl.Internal.Evaluation(EvalResult(..),Event(..),ScriptInfo(..)) @@ -59,14 +61,14 @@ import Control.Monad.Error(ErrorT(..)) -- initialize a script for execution -initLSLScript :: CompiledLSLScript -> ScriptImage +initLSLScript :: RealFloat a => CompiledLSLScript -> ScriptImage a initLSLScript (CompiledLSLScript globals fs ss) = ScriptImage { scriptImageName = "", curState = "default", executionState = Waiting, glob = initGlobals globals, - funcs = fs, + funcs = map ctxItem fs, states = ss, valueStack = [], callStack = [], @@ -106,49 +108,49 @@ (Right queue',evalState) -> return $ Right $ (scriptImage evalState, queue') -- The state of evaluation for a script. -data EvalState m = EvalState { - scriptImage :: ScriptImage, +data EvalState m a = EvalState { + scriptImage :: ScriptImage a, objectId :: String, primId :: Int, scriptName :: String, myPrimKey :: String, - performAction :: String -> ScriptInfo -> [LSLValue] -> m (EvalResult,LSLValue), + performAction :: String -> ScriptInfo a -> [LSLValue a] -> m (EvalResult,LSLValue a), logMessage :: String -> m (), qwtick :: m Int, uwtick :: Int -> m (), checkBreakpoint :: Breakpoint -> StepManager -> m (Bool, StepManager), - nextEvent :: String -> String -> m (Maybe Event) } + nextEvent :: String -> String -> m (Maybe (Event a)) } -type Eval m = ErrorT String (StateT (EvalState m) m) +type Eval m a = ErrorT String (StateT (EvalState m a) m) -evalT :: Monad m => ((EvalState m) -> (a,EvalState m)) -> Eval m a +evalT :: Monad m => ((EvalState m b) -> (a,EvalState m b)) -> Eval m b a evalT v = lift ((\ f -> StateT (\ s -> return (f s))) v) -- the image, or 'memory state' of a running script. Includes the -- status of all stacks, global variables, and immutable items like -- the functions and state definitions. -data ScriptImage = ScriptImage { +data ScriptImage a = ScriptImage { scriptImageName :: String, curState :: StateName, executionState :: ExecutionState, - glob :: MemRegion, + glob :: MemRegion a, funcs :: [Func], states :: [State], - valueStack :: ValueStack, - callStack :: CallStack, + valueStack :: ValueStack a, + callStack :: CallStack a, stepManager :: StepManager, globals :: [Global], - currentEvent :: Maybe Event + currentEvent :: Maybe (Event a) } deriving (Show) -data FrameInfo = FrameInfo { frameInfoImageName :: String, frameInfoFrames :: [(String,SourceContext,Maybe Int,[(String,LSLValue)])] } +data FrameInfo a = FrameInfo { frameInfoImageName :: String, frameInfoFrames :: [(String,Maybe SourceContext,Maybe Int,[(String,LSLValue a)])] } deriving (Show) frameInfo scriptImage = FrameInfo (scriptImageName scriptImage) $ frames ++ [("glob", bottomContext, Nothing, glob scriptImage)] where frames = (map collapseFrame $ callStack scriptImage) (bottomContext,bottomLine) = case frames of - [] -> (UnknownSourceContext,Nothing) + [] -> (Nothing,Nothing) _ -> let (_,ctx,_,_) = last frames in (ctx,Just 1) collapseFrame (Frame name ctx line (ss,_)) = (name,ctx,line,concat $ map fst ss) @@ -221,14 +223,14 @@ -- location has a name (rather than an address) and can hold a value -- which can be of any LSL type. So 1 memory location can hold a -- vector, or a string, or a list, etc. -type Binding = (String,LSLValue) +type Binding a = (String,LSLValue a) -- a memory region is just a collection of NameElementPairs -type MemRegion = [Binding] +type MemRegion a = [Binding a] emptyMemRegion = [] initVar name LLInteger Nothing = (name,IVal 0) -initVar name LLFloat Nothing = (name,FVal 0.0) +initVar name LLFloat Nothing = (name,FVal $ realToFrac 0.0) initVar name LLString Nothing = (name,SVal "") initVar name LLKey Nothing = (name,SVal "") initVar name LLList Nothing = (name,LVal []) @@ -240,16 +242,16 @@ initVar name LLInteger (Just (FVal f)) = (name,IVal $ floor f) initVar name _ (Just v) = (name,v) -writeMem :: Monad m => String -> LSLValue -> MemRegion -> m MemRegion +writeMem :: Monad m => String -> LSLValue a -> MemRegion a -> m (MemRegion a) writeMem name value cells = case break (\(name',element) -> name' == name) cells of (cells',[]) -> fail "no such variable" (xs,y:ys) -> return ((name,value):(xs ++ ys)) -readMem :: Monad m => String -> MemRegion -> m LSLValue +readMem :: Monad m => String -> MemRegion a -> m (LSLValue a) readMem = lookupM -initGlobals :: [Global] -> MemRegion +initGlobals :: RealFloat a => [Global] -> MemRegion a initGlobals globals = map (initGlobal globals) globals initGlobal globals (GDecl (Var name t) mexpr) = initVar name t $ fmap (evalLit globals) mexpr @@ -264,9 +266,9 @@ _ -> error "invalid float expression" in case expr of Neg (Ctx _ (IntLit i)) -> IVal (-i) - Neg (Ctx _ (FloatLit f)) -> FVal (-f) + Neg (Ctx _ (FloatLit f)) -> FVal (-(realToFrac f)) IntLit i -> IVal i - FloatLit f -> FVal f + FloatLit f -> FVal (realToFrac f) StringLit s -> SVal s KeyLit k -> KVal k ListExpr l -> LVal $ map (evalCtxLit globals) l @@ -298,25 +300,25 @@ toBool x = if x == 0 then False else True --type Frame = (ScopeStack,EvalStack) -data Frame = Frame { frameName :: String, frameContext :: SourceContext, frameSourceLine :: Maybe Int, - frameStacks :: (ScopeStack, EvalStack) } +data Frame a = Frame { frameName :: String, frameContext :: Maybe SourceContext, frameSourceLine :: Maybe Int, + frameStacks :: (ScopeStack a, EvalStack) } deriving (Show) type LabelSet = [LBlock] -type Scope = (MemRegion,LabelSet) -type ScopeStack = [Scope] -type CallStack = [Frame] +type Scope a = (MemRegion a,LabelSet) +type ScopeStack a = [Scope a] +type CallStack a = [Frame a] -readVarScope :: String -> Scope -> Maybe LSLValue +readVarScope :: String -> Scope a -> Maybe (LSLValue a) readVarScope name (mem,_) = readMem name mem -readVarSStack :: String -> ScopeStack -> Maybe LSLValue +readVarSStack :: String -> ScopeStack a -> Maybe (LSLValue a) readVarSStack name ss = foldl mplus Nothing $ map (readVarScope name) ss -readVarFrame :: String -> Frame -> Maybe LSLValue +readVarFrame :: String -> Frame a -> Maybe (LSLValue a) readVarFrame name = (readVarSStack name) . fst . frameStacks -readVarCallStack :: String -> CallStack -> Maybe LSLValue +readVarCallStack :: String -> CallStack a -> Maybe (LSLValue a) readVarCallStack name = (readVarFrame name) . head -writeVarScope :: String -> LSLValue -> Scope -> Maybe Scope +writeVarScope :: String -> LSLValue a -> Scope a -> Maybe (Scope a) writeVarScope name val (mem,l) = writeMem name val mem >>= return . (flip (,) l) writeVarSStack rs name val [] = Nothing writeVarSStack rs name val (s:ss) = @@ -332,7 +334,7 @@ type EvalStack = [EvalElement] -type ValueStack = [LSLValue] +type ValueStack a = [LSLValue a] data EvalElement = EvBlock [Ctx Statement] | EvCtxStatement (Ctx Statement) | EvStatement Statement | EvExpr Expr | EvMexpr (Maybe Expr) @@ -341,20 +343,20 @@ | EvShiftL | EvShiftR | EvCast LSLType | EvGet (String,Component) | EvSet (String,Component) | EvCons | EvMkVec | EvMkRot | EvPop | EvReturn | EvDiscard | EvBind String LSLType - | EvCond Statement Statement | EvCall String SourceContext [Var] [Ctx Statement] Bool + | EvCond Statement Statement | EvCall String (Maybe SourceContext) [Var] [Ctx Statement] Bool | EvPredef String | EvLoop Expr [Ctx Expr] Statement deriving (Show) -- Note: lifted from Hudak, p.273 -queryState :: Monad w => (EvalState w -> a) -> Eval w a +--queryState :: Monad w => (EvalState w a -> a) -> Eval w b a queryState q = evalT (\s -> (q s, s)) -updateState :: Monad w => (EvalState w -> EvalState w) -> Eval w () +updateState :: Monad w => (EvalState w a -> EvalState w a) -> Eval w a () updateState u = evalT (\s -> ((), u s)) queryExState q = queryState (q . scriptImage) -updateExState :: Monad w => (ScriptImage -> ScriptImage) -> Eval w () +updateExState :: Monad w => (ScriptImage a -> ScriptImage a) -> Eval w a () updateExState u = updateState (\s -> s { scriptImage = u $ scriptImage s }) -getTick :: Monad w => Eval w Int +getTick :: Monad w => Eval w a Int getTick = join $ evalT (\s -> (lift $ lift $ qwtick s,s)) setTick v = do f <- (queryState uwtick) lift $ lift $ f v @@ -371,35 +373,35 @@ setStepManager sm' return result -getGlob :: Monad w => Eval w MemRegion +getGlob :: Monad w => Eval w a (MemRegion a) getGlob = queryExState glob -getFuncs :: Monad w => Eval w [Func] +getFuncs :: Monad w => Eval w a [Func] getFuncs = queryExState funcs -getVStack :: Monad w => Eval w ValueStack +getVStack :: Monad w => Eval w a (ValueStack a) getVStack = queryExState valueStack -getCallStack :: Monad w => Eval w CallStack +getCallStack :: Monad w => Eval w a (CallStack a) getCallStack = queryExState callStack -getStates :: Monad w => Eval w [State] +getStates :: Monad w => Eval w a [State] getStates = queryExState states -getCurrentEvent :: Monad w => Eval w (Maybe Event) +getCurrentEvent :: Monad w => Eval w a (Maybe (Event a)) getCurrentEvent = queryExState currentEvent -getExecutionState :: Monad w => Eval w ExecutionState +getExecutionState :: Monad w => Eval w a ExecutionState getExecutionState = queryExState executionState -getCurState :: Monad w => Eval w String +getCurState :: Monad w => Eval w a String getCurState = queryExState curState -getEvalState :: Monad w => Eval w (EvalState w) +--getEvalState :: (RealFloat a, Read a, Monad w) => Eval w a (EvalState w a) getEvalState = queryState id -getObjectId :: Monad w => Eval w String +getObjectId :: Monad w => Eval w a String getObjectId = queryState objectId -getPrimId :: Monad w => Eval w Int +getPrimId :: Monad w => Eval w a Int getPrimId = queryState primId -getScriptName :: Monad w => Eval w String +getScriptName :: Monad w => Eval w a String getScriptName = queryState scriptName -getScriptImageName :: Monad w => Eval w String +getScriptImageName :: Monad w => Eval w a String getScriptImageName = queryExState scriptImageName -getMyPrimKey :: Monad w => Eval w String +getMyPrimKey :: Monad w => Eval w a String getMyPrimKey = queryState myPrimKey -getStepManager :: Monad w => Eval w StepManager +getStepManager :: Monad w => Eval w a StepManager getStepManager = queryExState stepManager setGlob g = updateExState (\e -> e { glob = g }) @@ -411,12 +413,12 @@ setCurrentEvent event = updateExState (\e -> e { currentEvent = Just event }) setScriptImageName n = updateExState (\ e -> e { scriptImageName = n }) -initStacks :: Monad w => Eval w () +initStacks :: Monad w => Eval w a () initStacks = do setVStack [] setCallStack [] -popScope :: Monad w => Eval w Scope +popScope :: Monad w => Eval w a (Scope a) popScope = do --((s:ss,es):cs) <- getCallStack (frame:frames) <- getCallStack @@ -424,7 +426,7 @@ setCallStack (frame { frameStacks = (ss,es) }:frames) return s -pushScope :: Monad w => MemRegion -> LabelSet -> Eval w () +pushScope :: Monad w => MemRegion a -> LabelSet -> Eval w a () pushScope mem labels = do (frame:frames) <- getCallStack let (ss,es) = frameStacks frame @@ -434,7 +436,7 @@ do vstack <- getVStack setVStack (value:vstack) -popVal :: Monad w => Eval w LSLValue +popVal :: Monad w => Eval w a (LSLValue a) popVal = do vstack <- getVStack case vstack of @@ -443,24 +445,24 @@ setVStack vs return v -peekVal :: Monad w => Eval w LSLValue +peekVal :: Monad w => Eval w a (LSLValue a) peekVal = do vstack <- getVStack case vstack of [] -> fail "empty value stack" (v:_) -> return v -valStackEmpty :: Monad w => Eval w Bool +valStackEmpty :: (RealFloat a, Read a, Monad w) => Eval w a Bool valStackEmpty = getVStack >>= (return . (==[])) -elementStackEmpty :: Monad w => Eval w Bool +elementStackEmpty :: Monad w => Eval w a Bool elementStackEmpty = do (frame:cs) <- getCallStack case frameStacks frame of (_,[]) -> return True _ -> return False -popElement :: Monad w => Eval w EvalElement +popElement :: Monad w => Eval w a EvalElement popElement = do (frame:frames) <- getCallStack let (ss,e:es) = frameStacks frame @@ -481,10 +483,10 @@ do mapM pushElement elements return EvalIncomplete -callStackEmpty :: Monad w => Eval w Bool +callStackEmpty :: Monad w => Eval w a Bool callStackEmpty = getCallStack >>= return . null -popFrame :: Monad w => Eval w () +popFrame :: Monad w => Eval w a () popFrame = do (f:cs) <- getCallStack stepMgr <- getStepManager @@ -500,12 +502,12 @@ cs <- getCallStack setCallStack (Frame { frameName = name, frameContext = ctx, frameSourceLine = line, frameStacks = ([],[])}:cs) -getFunc :: Monad w => String -> Eval w Func +getFunc :: Monad w => String -> Eval w a Func getFunc name = do funcs <- getFuncs incontext ("func: " ++ name) $ findFunc name funcs -setVar :: Monad w => String -> LSLValue -> Eval w () +setVar :: Monad w => String -> LSLValue a -> Eval w a () setVar name val = do cs <- getCallStack case writeVarCallStack name val cs of @@ -515,7 +517,7 @@ glob' <- incontext ("setting " ++ name ++ ":") $ writeMem name val glob setGlob glob' -getVar :: Monad w => String -> Eval w LSLValue +getVar :: (RealFloat a, Read a, Monad w) => String -> Eval w a (LSLValue a) getVar name = do cs <- getCallStack glob <- getGlob @@ -523,7 +525,7 @@ Nothing -> fail ("no such variable " ++ name) Just val -> return val -initVar1 :: Monad w => String -> LSLType -> Maybe LSLValue -> Eval w () +initVar1 :: (RealFloat a, Read a, Monad w) => String -> LSLType -> Maybe (LSLValue a) -> Eval w a () initVar1 name t mval = do --(((m,l):ss,es):cs) <- getCallStack (frame:frames) <- getCallStack @@ -531,7 +533,7 @@ let frame' = frame { frameStacks = (((initVar name t mval):m,l):ss,es) } setCallStack (frame':frames) -initVars1 :: Monad w => [Var] -> [LSLValue] -> Eval w () +initVars1 :: (RealFloat a, Read a, Monad w) => [Var] -> [LSLValue a] -> Eval w a () initVars1 vars vals = foldM_ (\_ -> \ (Var n t, v) -> initVar1 n t $ Just v) () $ zip vars vals @@ -564,7 +566,7 @@ findM (\ (Handler (Ctx _ name') _ _) -> name' == name) handlers -evalScriptSimple :: Monad w => Int -> [String] -> [Binding] -> [LSLValue] -> Eval w (EvalResult,Maybe LSLValue) +evalScriptSimple :: (Read a, RealFloat a, Monad w) => Int -> [String] -> [Binding a] -> [LSLValue a] -> Eval w a (EvalResult,Maybe (LSLValue a)) evalScriptSimple maxTick path globbindings args = do setupSimple path globbindings args evalSimple maxTick @@ -611,7 +613,7 @@ Left s -> fail s Right v -> return v -evalScript :: Monad w => Int -> [Event] -> Eval w [Event] +evalScript :: (RealFloat a, Read a, Monad w) => Int -> [Event a] -> Eval w a [Event a] evalScript maxTick queue = do executionState <- getExecutionState case executionState of @@ -671,7 +673,7 @@ else return queue Halted -> return queue -eval :: Monad w => Int -> Eval w EvalResult +eval :: (RealFloat a, Read a, Monad w) => Int -> Eval w a EvalResult eval maxTick = do t <- getTick @@ -685,10 +687,10 @@ else return EvalIncomplete) -eval' :: Monad w => Eval w EvalResult +eval' :: (RealFloat a, Read a, Monad w) => Eval w a EvalResult eval' = let continue = return EvalIncomplete - popAndCheck :: Monad w => Eval w EvalResult + popAndCheck :: Monad w => Eval w a EvalResult popAndCheck = do popFrame noMoreFrames <- callStackEmpty @@ -709,12 +711,13 @@ EvBlock (s:ss) -> pushElements [EvBlock ss,EvCtxStatement s] EvCtxStatement s -> do pushElements [EvStatement $ ctxItem s] - if (isTextLocation $ srcCtx s) then - let bp = mkBreakpoint (textName $ srcCtx s) (textLine0 $ srcCtx s) 0 in - do brk <- checkBp bp - if brk then return $ BrokeAt bp - else continue - else continue + case srcCtx s of + Just (SourceContext { srcTextLocation = txtl }) -> + let bp = mkBreakpoint (textName txtl) (textLine0 txtl) 0 in + do brk <- checkBp bp + if brk then return $ BrokeAt bp + else continue + Nothing -> continue EvStatement (Return mexpr) -> pushElements [EvReturn,EvMexpr $ fromMCtx mexpr] EvStatement (NullStmt) -> eval' EvStatement (StateChange s) -> return $ EvalComplete $ Just s @@ -765,7 +768,7 @@ pushElement (EvStatement (if trueCondition val then stmt1 else stmt2)) continue EvExpr (IntLit i) -> pushVal (IVal i) >> continue - EvExpr (FloatLit f) -> pushVal (FVal f) >> continue + EvExpr (FloatLit f) -> pushVal (FVal (realToFrac f)) >> continue EvExpr (StringLit s) -> pushVal (SVal s) >> continue EvExpr (KeyLit k) -> pushVal (KVal k) >> continue EvExpr (VecExpr e1 e2 e3) -> @@ -1041,29 +1044,6 @@ "ffffffff-ffff-ffff-ffff-ffffffffffff" -> True _ -> False where tr c = if c `elem` "0123456789abcdef" then 'f' else c --- TODO: LSL also will parse hex notation for strings and floats... -parseVector s = - case [(VVal x y z,t) | ("<",t0) <- lex s, - (x,t1) <- reads t0, - (",",t2) <- lex t1, - (y,t3) <- reads t2, - (",",t4) <- lex t3, - (z,t5) <- reads t4, - (">",t) <- lex t5] of - [] -> VVal 0.0 0.0 0.0 - (v,_):_ -> v -parseRotation s = - case [(RVal x y z w,t) | ("<",t0) <- lex s, - (x,t1) <- reads t0, - (",",t2) <- lex t1, - (y,t3) <- reads t2, - (",",t4) <- lex t3, - (z,t5) <- reads t4, - (",",t6) <- lex t5, - (w,t7) <- reads t6, - (">",t) <- lex t7] of - [] -> RVal 0.0 0.0 0.0 0.0 - (v,_):_ -> v toLslBool bool = IVal (if bool then 1 else 0) evalBinary f = @@ -1097,9 +1077,9 @@ convertArg LLString (KVal k) = SVal k convertArg _ v = v -ctxList es = map (Ctx UnknownSourceContext) es +ctxList es = map (Ctx Nothing) es -data ExecutionInfo = ExecutionInfo String Int FrameInfo deriving (Show) +data ExecutionInfo a = ExecutionInfo String Int (FrameInfo a) deriving (Show) hasActiveHandler simage handler = case find ( \ (State ctxname _) -> curState simage == ctxItem ctxname) (states simage) of
src/Language/Lsl/Internal/ExecInfo.hs view
@@ -1,7 +1,7 @@ module Language.Lsl.Internal.ExecInfo(emitExecutionInfo) where import Language.Lsl.Internal.Exec(ExecutionInfo(..),FrameInfo(..)) -import Language.Lsl.Syntax(SourceContext(..)) +import Language.Lsl.Syntax(SourceContext(..),TextLocation(..)) import Language.Lsl.Internal.Type(LSLValue(..)) import Language.Lsl.Internal.XmlCreate(emit,emitSimple) @@ -22,8 +22,8 @@ emitCtx ctx, emitLine line, emit "bindings" [] (map emitBinding bindings)] - where emitCtx UnknownSourceContext = id - emitCtx ctx = emitSimple "file" [] ( textName ctx ) + where emitCtx Nothing = id + emitCtx (Just (SourceContext { srcTextLocation = txtl })) = emitSimple "file" [] ( textName txtl ) emitLine Nothing = id emitLine (Just i) = emitSimple "line" [] (show i) @@ -48,4 +48,4 @@ emitSimple "z" [] (show z), emitSimple "s" [] (show s)] emitVal (LVal l) = - emit "val" [("class","list-value")] [emit "elements" [] (map emitVal l)]+ emit "val" [("class","list-value")] [emit "elements" [] (map emitVal l)]
src/Language/Lsl/Internal/ExpressionHandler.hs view
@@ -118,6 +118,10 @@ validPrimitiveExpr (Le e0 e1) = validRelExpr e0 e1 validPrimitiveExpr (Gt e0 e1) = validRelExpr e0 e1 validPrimitiveExpr (Ge e0 e1) = validRelExpr e0 e1 +validPrimitiveExpr (Cast t e) = do + t' <- validPrimitiveCtxExpr e + when (not $ isCastValid t' t) $ fail "invalid cast" + return t validPrimitiveExpr expr = fail "expression not valid in this context" validPrimEach e0 e1 = @@ -198,7 +202,7 @@ case t of (IVal i) -> return $ IVal (if i == 0 then 1 else 0) evalExpr (IntLit i) = return (IVal i) -evalExpr (FloatLit f) = return (FVal f) +evalExpr (FloatLit f) = return (FVal $ realToFrac f) evalExpr (StringLit s) = return (SVal s) evalExpr (KeyLit k) = return (KVal k) evalExpr (VecExpr xExpr yExpr zExpr) = @@ -280,6 +284,29 @@ evalExpr (Le e0 e1) = evalRelExpr (<=) (<=) e0 e1 evalExpr (Gt e0 e1) = evalRelExpr (>) (>) e0 e1 evalExpr (Ge e0 e1) = evalRelExpr (>=) (>=) e0 e1 +evalExpr (Cast t e) = do + v <- evalCtxExpr e + case (t,v) of + (LLInteger,IVal i) -> return $ IVal i + (LLInteger,FVal f) -> return $ IVal (truncate f) + (LLInteger,SVal s) -> return $ IVal (parseInt s) + -- TODO: can you cast a key to an int? + (LLFloat,FVal f) -> return $ FVal f + (LLFloat,IVal i) -> return $ FVal (fromInteger $ toInteger i) + (LLFloat,SVal s) -> return $ FVal (parseFloat s) + -- TODO: can you cast a key to a float? + (LLString,v) -> return $ toSVal v + -- TODO: can you cast anything but a string to a key? + (LLVector,SVal s) -> return $ parseVector s + (LLRot,SVal s) -> return $ parseRotation s + (LLList,SVal s) -> return $ LVal [SVal s] + (LLKey,SVal s) -> return $ KVal s + (LLKey,KVal s) -> return $ KVal s + (LLVector, (VVal _ _ _)) -> return $ v + (LLRot, (RVal _ _ _ _)) -> return $ v + (LLList, LVal l) -> return $ v + _ -> fail "invalid cast!" + evalExpr expr = fail "expression not valid in this context" evalArithExpr fi ff e0 e1 = @@ -347,4 +374,4 @@ Left s -> error (show s) putStr $ expressionValidatorEmitter (checkExpr t text) - +
src/Language/Lsl/Internal/InternalLLFuncs.hs view
@@ -126,9 +126,9 @@ import Network.URI(escapeURIChar,unEscapeString) internalLLFuncNames :: [String] -internalLLFuncNames = map fst (internalLLFuncs :: [(String, a -> [LSLValue] -> Maybe (EvalResult,LSLValue))]) +internalLLFuncNames = map fst (internalLLFuncs :: (Read a, RealFloat a) => [(String, a -> [LSLValue a] -> Maybe (EvalResult,LSLValue a))]) -internalLLFuncs :: (Monad m) => [(String, a -> [LSLValue] -> m (EvalResult,LSLValue))] +internalLLFuncs :: (Read a, RealFloat a, Monad m) => [(String, b -> [LSLValue a] -> m (EvalResult,LSLValue a))] internalLLFuncs = [ ("llAbs",llAbs), ("llAcos",llAcos), @@ -274,7 +274,7 @@ llSHA1String _ [SVal string] = continueWith $ SVal (hashStoHex string) -- Math functions -unaryToLL :: (Monad m) => (Float -> Float) -> [LSLValue] -> m (EvalResult,LSLValue) +unaryToLL :: (RealFloat a, Monad m) => (a -> a) -> [LSLValue a] -> m (EvalResult,LSLValue a) unaryToLL f [FVal v] = continueWith $ FVal (f v) llCos _ = unaryToLL cos @@ -308,7 +308,7 @@ in pow (a `mod` c) b' -- should use a map for this, but have been using lists for everything, so will stick with it... -statFuncs :: [(Int, [Float] -> Float)] +statFuncs :: RealFloat a => [(Int, [a] -> a)] statFuncs = map (\(Just (IVal op), f) -> (op,f)) [ (findConstVal "LIST_STAT_RANGE", listStatRange), (findConstVal "LIST_STAT_MAX", listStatMax), @@ -321,7 +321,7 @@ (findConstVal "LIST_STAT_NUM_COUNT", fromInt . length), (findConstVal "LIST_STAT_GEOMETRIC_MEAN", listStatGeometricMean)] -toF :: LSLValue -> Maybe Float +toF :: Num a => LSLValue a -> Maybe a toF (IVal i) = Just (fromInt i) toF (FVal f) = Just f toF _ = Nothing @@ -436,7 +436,7 @@ llCSV2List _ [SVal s] = continueWith $ LVal $ map SVal (lslCsvToList [] s) llList2CSV _ [LVal l] = - continueWith $ SVal $ concat (intersperse "," (map lslValToString l)) + continueWith $ SVal $ concat (intersperse ", " (map lslValToString l)) lslValToString (VVal x y z) = "<" ++ (show x) ++ "," ++ (show y) ++ "," ++ (show z) ++ ">" lslValToString (RVal x y z s) = "<" ++ (show x) ++ "," ++ (show y) ++ "," ++ (show z) ++ "," ++ (show s) ++ ">" @@ -492,6 +492,7 @@ concat (direction $ sort $ strideList list stride) +typeCodes :: RealFloat a => [(LSLType, LSLValue a)] typeCodes = map (\ (t,Just v) -> (t,v)) [ (LLInteger, findConstVal "TYPE_INTEGER"), (LLFloat, findConstVal "TYPE_FLOAT"), @@ -499,7 +500,8 @@ (LLKey, findConstVal "TYPE_KEY"), (LLVector, findConstVal "TYPE_VECTOR"), (LLRot, findConstVal "TYPE_ROTATION")] - + +invalidType :: RealFloat a => LSLValue a invalidType = let Just v = findConstVal "TYPE_INVALID" in v -- TODO: might this function work with negative indices? assuming no. @@ -508,9 +510,11 @@ if index < 0 || index >= length l then invalidType else let Just v = lookup (typeOfLSLValue $ l !! index) typeCodes in v +elemAtM' i = if i >= 0 then elemAtM i else elemAtM ((-1) - i) . reverse + llList2Float _ [LVal l, IVal index] = continueWith $ FVal $ - case elemAtM index l of + case elemAtM' index l of Just (SVal s) -> parseFloat s Just (IVal i) -> fromInt i Just (FVal f) -> f @@ -518,7 +522,7 @@ llList2Integer _ [LVal l, IVal index] = continueWith $ IVal $ - case elemAtM index l of + case elemAtM' index l of Just (SVal s) -> parseInt s Just (IVal i) -> i Just (FVal f) -> truncate f @@ -526,22 +530,22 @@ llList2Key _ [LVal l, IVal index] = continueWith $ - case elemAtM index l of + case elemAtM' index l of Just v -> let (SVal v') = toSVal v in KVal v' _ -> let (Just (SVal v)) = findConstVal "NULL_KEY" in KVal v llList2Rot _ [LVal l, IVal index] = - continueWith $ case elemAtM index l of + continueWith $ case elemAtM' index l of Just r@(RVal _ _ _ _) -> r _ -> let (Just r) = findConstVal "ZERO_ROTATION" in r llList2String _ [LVal l, IVal index] = continueWith $ - case elemAtM index l of + case elemAtM' index l of Nothing -> SVal "" Just v -> toSVal v llList2Vector _ [LVal l, IVal index] = - continueWith $ case elemAtM index l of + continueWith $ case elemAtM' index l of Just v@(VVal _ _ _) -> v _ -> let (Just v) = findConstVal "ZERO_VECTOR" in v
src/Language/Lsl/Internal/Load.hs view
@@ -12,7 +12,7 @@ let parseFile (name,path) = do result <- tryJust (\ e@(SomeException x) -> Just (show e)) $ p path case result of - Left msg -> return (name,Left (UnknownSourceContext,msg)) + Left msg -> return (name,Left (Nothing,msg)) Right (Left err) -> return (name,Left err) Right (Right m) -> return (name,Right m) in liftIO $ mapM parseFile files
src/Language/Lsl/Internal/Math.hs view
@@ -65,7 +65,7 @@ invertQuaternion (x,y,z,s) = (-x,-y,-z,s) -rotationsToQuaternion :: Permutation3 -> (Float,Float,Float) -> (Float,Float,Float,Float) +rotationsToQuaternion :: RealFloat a => Permutation3 -> (a,a,a) -> (a,a,a,a) rotationsToQuaternion order (x,y,z) = let rx = (sin (x/2), 0.0, 0.0, cos (x/2)) ry = (0.0, sin (y/2), 0.0, cos (y/2)) @@ -86,7 +86,7 @@ sign f = if f < 0 then -1 else 1 -quaternionToRotations :: Permutation3 -> Bool -> (Float,Float,Float,Float) -> (Float,Float,Float) +quaternionToRotations :: RealFloat a => Permutation3 -> Bool -> (a,a,a,a) -> (a,a,a) quaternionToRotations rotOrder lh quat= let (p1,p2,p3,p0) = quatPermute quat rotOrder mult = if lh then -1 else 1 @@ -126,4 +126,4 @@ x' = x / sinVal y' = y / sinVal z' = z / sinVal - in if sinVal == 0 then ((1,0,0),0) else ((x',y',z'),angle)+ in if sinVal == 0 then ((1,0,0),0) else ((x',y',z'),angle)
src/Language/Lsl/Internal/Optimize.hs view
@@ -1,73 +1,947 @@-{-# OPTIONS_GHC -fwarn-incomplete-patterns #-} -module Language.Lsl.Internal.Optimize(optimizeScript) where - -import Language.Lsl.Syntax(CompiledLSLScript(..),Expr(..),Statement(..),Func(..),FuncDec(..),State(..),Ctx(..),Handler(..)) - -optimizeScript :: CompiledLSLScript -> CompiledLSLScript -optimizeScript (CompiledLSLScript gs fs ss) = (CompiledLSLScript gs fs' ss) - where usedFuncs = concatMap stateUsesFuncs ss ++ concatMap funcUsesFuncs fs - fs' = [ f | f@(Func fd _) <- fs, (ctxItem . funcName) fd `elem` usedFuncs ] - -exprUsesFuncs :: Expr -> [String] -exprUsesFuncs (Call (ctxName) es) = ctxItem ctxName : (exprsUsesFuncs es) -exprUsesFuncs (IntLit _) = [] -exprUsesFuncs (FloatLit _) = [] -exprUsesFuncs (StringLit _) = [] -exprUsesFuncs (KeyLit _) = [] -exprUsesFuncs (ListExpr es) = exprsUsesFuncs es -exprUsesFuncs (VecExpr x y z) = exprsUsesFuncs [x,y,z] -exprUsesFuncs (RotExpr x y z s) = exprsUsesFuncs [x,y,z,s] -exprUsesFuncs (Add e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (Sub e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (Mul e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (Div e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (Mod e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (Set _ e) = exprUsesFuncs' e -exprUsesFuncs (BAnd e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (BOr e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (Xor e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (ShiftL e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (ShiftR e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (And e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (Or e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (Equal e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (NotEqual e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (Lt e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (Gt e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (Le e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (Ge e1 e2) = exprsUsesFuncs [e1,e2] -exprUsesFuncs (IncBy _ e) = exprUsesFuncs' e -exprUsesFuncs (DecBy _ e) = exprUsesFuncs' e -exprUsesFuncs (MulBy _ e) = exprUsesFuncs' e -exprUsesFuncs (DivBy _ e) = exprUsesFuncs' e -exprUsesFuncs (ModBy _ e) = exprUsesFuncs' e -exprUsesFuncs (Not e) = exprUsesFuncs' e -exprUsesFuncs (Neg e) = exprUsesFuncs' e -exprUsesFuncs (Inv e) = exprUsesFuncs' e -exprUsesFuncs (Cast _ e) = exprUsesFuncs' e -exprUsesFuncs _ = [] - -exprUsesFuncs' = exprUsesFuncs . ctxItem -exprsUsesFuncs = concatMap exprUsesFuncs' - -statementUsesFuncs :: Statement -> [String] -statementUsesFuncs (Compound ss) = concatMap (statementUsesFuncs . ctxItem) ss -statementUsesFuncs (While e s) = exprUsesFuncs' e ++ statementUsesFuncs s -statementUsesFuncs (DoWhile s e) = statementUsesFuncs s ++ exprUsesFuncs' e -statementUsesFuncs (For e1 me2 e3 s) = - exprsUsesFuncs e1 ++ maybe [] exprUsesFuncs' me2 ++ exprsUsesFuncs e3 ++ statementUsesFuncs s -statementUsesFuncs (If e s1 s2) = exprUsesFuncs' e ++ statementUsesFuncs s1 ++ statementUsesFuncs s2 -statementUsesFuncs (Decl _ (Just e)) = exprUsesFuncs' e -statementUsesFuncs (Return (Just e)) = exprUsesFuncs' e -statementUsesFuncs (Do e) = exprUsesFuncs' e -statementUsesFuncs _ = [] - -statementUsesFuncs' = statementUsesFuncs . ctxItem - -funcUsesFuncs :: Func -> [String] -funcUsesFuncs (Func _ stmts) = concatMap statementUsesFuncs' stmts -handlerUsesFuncs :: Handler -> [String] -handlerUsesFuncs (Handler _ _ stmts) = concatMap statementUsesFuncs' stmts - -stateUsesFuncs :: State -> [String] -stateUsesFuncs (State _ handlers) = concatMap handlerUsesFuncs handlers +{-# OPTIONS_GHC -fwarn-incomplete-patterns -XStandaloneDeriving -XNoMonomorphismRestriction #-} +module Language.Lsl.Internal.Optimize(optimizeScript,OptimizerOption(..)) where + +import Control.Monad.State hiding (State) +import qualified Control.Monad.Identity as Id +import qualified Control.Monad.State as State(State) + +import Data.Bits((.&.),(.|.),xor,shiftL,shiftR,complement) +import Data.Generics +import Data.Generics.Extras.Schemes(everythingTwice,downup) +import Data.List(foldl',nub,lookup) +import Data.Graph +import qualified Data.Set as Set +import qualified Data.Map as M + +import Debug.Trace + +import Language.Lsl.Parse +import Language.Lsl.Internal.Constants(allConstants,Constant(..),findConstVal) +import Language.Lsl.Internal.FuncSigs(funcSigs) +import Language.Lsl.Internal.InternalLLFuncs(internalLLFuncs,internalLLFuncNames) +import Language.Lsl.Internal.OptimizerOptions(OptimizerOption(..)) +import Language.Lsl.Syntax(CompiledLSLScript(..),Expr(..),Statement(..),Var(..), + Func(..),FuncDec(..),State(..),Ctx(..),Handler(..), + Global(..),Component(..),SourceContext(..),rewriteCtxExpr) +import Language.Lsl.Internal.Type(LSLType(..),toSVal) +import Language.Lsl.Internal.Pragmas(Pragma(..)) +import Language.Lsl.Internal.Type(LSLValue(..)) +import Language.Lsl.UnitTestEnv(simSFunc) + +optionInlining = elem OptimizationInlining + +-- deriving instance (Show a) => Show (SCC a) + +optimizeScript :: [OptimizerOption] -> CompiledLSLScript -> CompiledLSLScript +optimizeScript options script@(CompiledLSLScript gs fsIn ss) = CompiledLSLScript gsReachable fsReachable ss1 + where inline = optionInlining options + gcs = globalConstants gs fsIn ss + scc = graphInfo fsIn + funFacts = sccsPurity gcs basicFunctionFacts scc + pure = Set.fromList [ nm | (nm,ff) <- M.toList funFacts, isPureFunction ff] + --pure = trace (show pure') pure' + ifs = [ f | AcyclicSCC f <- scc, inlineable f] -- inlineables + nifs = [ f | f <- fsIn, fname f `notElem` (map fname ifs)] -- non-inlineables + ss1 = if inline then simp $ map runInliningOnState (simp ss) else ss + ifs' = foldl (\ fs f -> let f' = runInliningOnFunc funFacts fs gs f in f':fs) [] ifs -- inlineables that have had any inlining done + nifs' = map (runInliningOnFunc funFacts ifs' gs) nifs -- non-inlineables that have had any inlining done + fs' = if inline then (nifs' ++ ifs') else fsIn + fsReachable = reachableFuncs ss1 (simp fs') -- funcs that are still reachable from handlers + gsReachable = reachableGlobs gs fsReachable ss1 -- globals that are still reachable from handlers/funcs + simp = if inline then simplify script pure gcs else id + runInliningOnState s@(State nm hs) = if noinlining nm then s + else (State nm (map (runInliningOnHandler funFacts ifs' gs) hs)) + +hasPragma p (Ctx (Just SourceContext { srcPragmas = l }) _) | p `elem` l = True + | otherwise = False +hasPragma _ _ = False + +inlineable = hasPragma PragmaInline +noinlining = hasPragma PragmaNoInline + +data EPKey = HK String String | FK String deriving (Show, Eq, Ord) + +stateEdges :: [State] -> [(EPKey,EPKey,[EPKey])] +stateEdges ss = concatMap (\ (State (Ctx _ nm) hs) -> (map (\ h@(Handler (Ctx _ nm') _ _) -> (HK nm nm',HK nm nm', (map FK (handlerCallsFuncs h)))) hs)) ss + +funcEdges :: [Ctx Func] -> [(EPKey,EPKey,[EPKey])] +funcEdges fs = map (\ f -> let fn = fname f in (FK fn, FK fn,map FK $ funcCallsFuncs f)) fs + +reachableFuncs ss fs = [ f | f <- fs, fname f `elem` allReachableFnames] + where ses = stateEdges ss + (graph,v2n,k2v) = graphFromEdges (ses ++ funcEdges fs) + allReachableIndices = nub $ concatMap (reachable graph) [ i | Just i <- map ( \ (k,_,_) -> k2v k) ses] + allReachableFnames = [fn | (FK fn,_,_) <- map ( \ i -> v2n i) allReachableIndices] + +varNameInList :: Var -> [String] +varNameInList = (:[]) . varName + +varsDefinedByHandler :: Handler -> [String] +varsDefinedByHandler = everything (++) ([] `mkQ` varNameInList) + +varsDefinedByFunc :: Ctx Func -> [String] +varsDefinedByFunc = everything (++) ([] `mkQ` varNameInList) + +labels (Label nm) = [nm] +labels _ = [] + +labelsDefinedByFunc :: Ctx Func -> [String] +labelsDefinedByFunc = everything (++) ([] `mkQ` labels) + +labelsDefinedByHandler :: Handler -> [String] +labelsDefinedByHandler = everything (++) ([] `mkQ` labels) + +namesDefinedByHandler :: Handler -> [String] +namesDefinedByHandler handler = labelsDefinedByHandler handler ++ varsDefinedByHandler handler + +namesDefinedByFunc :: Ctx Func -> [String] +namesDefinedByFunc func = labelsDefinedByFunc func ++ varsDefinedByFunc func + +exprCallsFuncDirectly :: Expr -> [String] +exprCallsFuncDirectly (Call (ctxName) _) = [ctxItem ctxName] +exprCallsFuncDirectly _ = [] + +funcCallsFuncs :: Ctx Func -> [String] +funcCallsFuncs = everything (++) ([] `mkQ` exprCallsFuncDirectly) + +stateCallsFuncs :: State -> [String] +stateCallsFuncs = everything (++) ([] `mkQ` exprCallsFuncDirectly) + +handlerCallsFuncs :: Handler -> [String] +handlerCallsFuncs = everything (++) ([] `mkQ` exprCallsFuncDirectly) + +fname (Ctx _ (Func (FuncDec (Ctx _ name) _ _) _)) = name +fstmts (Ctx _ (Func _ stmts)) = stmts + +graphInfo :: [Ctx Func] -> [SCC (Ctx Func)] +graphInfo funcs = scc + where edges = (map (\ f -> (f, fname f, funcCallsFuncs f)) funcs) + scc = stronglyConnComp edges + acyc = [ f | AcyclicSCC f <- scc] + cyc = [ l | CyclicSCC l <- scc] + +newNames seed curNames verbotenNames = foldl' newName (seed,[]) curNames + where newName (n,renames) name = + if name `notElem` verbotenNames + then (n,(name,name):renames) + else if nm `notElem` verbotenNames + then (n+1,(name,nm):renames) + else newName (n+1,renames) name + where nm = "_" ++ show n ++ "inl" + +nullCtx :: a -> Ctx a +nullCtx = Ctx Nothing + +newtype FunctionFacts = FunctionFacts { isPureFunction :: Bool } deriving (Show) + +data OptimizerState = OptimizerState { + optAllFuncs :: !(M.Map String (Ctx Func)), + optFunFacts :: !(M.Map String FunctionFacts), + optNameIndex :: Int, + optGlobalNames :: !(Set.Set String), + optVerbotenNames :: !(Set.Set String), + optLocals :: ![[String]], + optInlinerRenames :: ![M.Map String String], -- names that must be renamed in the 'destination' function/handler + optRenames :: !(M.Map String String), -- names that must be renamed in the function to-be-inlined + optRewrites :: !(M.Map String Expr), + optRetVar :: !(Maybe String), + optStmts :: ![[Ctx Statement]] } + +type OState a = State.State OptimizerState a + +basicFunctionFacts = M.fromList (zip internalLLFuncNames (repeat (FunctionFacts { isPureFunction = True }))) `M.union` + M.fromList [(nm,FunctionFacts False) | (nm,_,_) <- funcSigs] + +freshOptimizerState funFacts fs gnames = OptimizerState { + optAllFuncs = M.fromList (map (\ f@(Ctx _ (Func (FuncDec nm _ _) _)) -> (ctxItem nm,f)) fs), + optFunFacts = funFacts, + optNameIndex = 0, + optGlobalNames = (Set.fromList gnames), + optVerbotenNames = Set.empty, + optLocals = [], + optInlinerRenames = [], + optRenames = M.empty, + optRewrites = M.empty, + optRetVar = Nothing, + optStmts = [] } + +removeOStateStmts :: OState [Ctx Statement] +removeOStateStmts = do + st <- get + let stmts = (concat (reverse (optStmts st))) + put st { optStmts = [] } + return stmts + +refreshOState :: OState () +refreshOState = do + st <- get + put st { optRenames = M.empty, optRetVar = Nothing, optStmts = [] } + +pushLocal s = do + st <- get + case optLocals st of + [] -> put st { optLocals = [[s]] } + top:rest -> put st { optLocals = (s:top):rest } + +mkName s = do + st <- get + let i = optNameIndex st + let name = "_" ++ s ++ show i + put st { optNameIndex = i + 1 } + st <- get + verboten <- isVerboten name + if not verboten + then do addVerboten name + return name + else mkName s -- make another... + +putRetVar s = get >>= ( \ os -> put os { optRetVar = Just s }) +clearRetVar s = get >>= (\ os -> put os { optRetVar = Nothing }) + +addVerboten name = do + st <- get + let s = optVerbotenNames st + put st { optVerbotenNames = (Set.insert name s) } + +addRename s s' = do + st <- get + put st { optRenames = M.insert s s' (optRenames st) } + +rewriteLabel s = do + renames <- get >>= return . optRenames + return $ maybe s id (M.lookup s renames) + +renameToNew s = do + verboten <- isVerboten s + if verboten + then do + s' <- mkName s + addRename s s' + return s' + else do + st <- get + put st { optVerbotenNames = Set.insert s (optVerbotenNames st) } + return s + +pushInlinerScope = do + st <- get + put st { optInlinerRenames = M.empty : (optInlinerRenames st) } +popInlinerScope = do + st <- get + case optInlinerRenames st of + [] -> error "cannot pop inliner scope" + (top:rest) -> put st { optInlinerRenames = rest } + +renameToNewInInliner s = do + --verboten <- isVerboten s + verboten <- get >>= return . (Set.member s) . optGlobalNames + if verboten + then do + s' <- mkName s + st <- get + case optInlinerRenames st of + [] -> error "empty inliner rename stack" + (top:rest) -> put st { optInlinerRenames = M.insert s s' top : rest } + return s' + else do + st <- get + put st { optVerbotenNames = Set.insert s (optVerbotenNames st) } + return s + +inlinerRenamingFor s = do + renameStack <- get >>= return . optInlinerRenames + return $ renamingFor renameStack s + where renamingFor [] s = s + renamingFor (top:rest) s = case M.lookup s top of + Just s' -> s' + Nothing -> renamingFor rest s + +renameVar (Var s t) = do + s' <- renameToNew s + return (Var s' t) + +getRewriteInfo :: OState (M.Map String String, M.Map String Expr) +getRewriteInfo = get >>= ( \ st -> return (optRenames st, optRewrites st)) + +isVerboten s = do + verbotenNames <- get >>= return . optVerbotenNames + globalNames <- get >>= return . optGlobalNames + return (s `Set.member` (verbotenNames `Set.union` globalNames)) + +inlineProc :: + Ctx Func -> -- the function to inline + [Ctx Expr] -> -- the arguments to the call + OState ([Ctx Statement],[Ctx Statement]) -- +inlineProc (Ctx c (Func fd ss)) args = do + endLabel <- mkName "end" + parmVars <- mkParmVars ss (zip ps args) + stmts <- inlineStmts endLabel (map ctxItem ss) >>= return . map nullCtx >>= return . withoutFinalJumpTo endLabel + return (if jumpsTo endLabel stmts == 0 then [] else [nullCtx $ Label endLabel], parmVars ++ stmts) + -- simplify $ nullCtx (Compound (parmVars ++ stmts))) + where ft = funcType fd + ps = funcParms fd + +mkParmVars :: [Ctx Statement] -> [(Ctx Var,Ctx Expr)] -> OState [Ctx Statement] +mkParmVars ss ves = mapM (mkParmVar ss) ves >>= return . concat +mkParmVar ss (Ctx _ v@(Var nm _),arg) = do + funFacts <- get >>= return . optFunFacts + locals <- get >>= return . concat . optLocals + if isRelativelyPure locals funFacts arg && + (staticComplexity arg < 2 || usageCount nm ss == 1) && not (nm `isModifiedIn` ss) && (simpleRef arg || nm `isUsedOnlyWholeIn` ss) + then do + st <- get + put st { optRewrites = M.insert (varName v) (ctxItem arg) (optRewrites st) } + return [] + else do + v' <- renameVar v + return [nullCtx $ Decl v' $ Just arg] + where simpleRef (Ctx _ (Get (_,All))) = True + simpleRef _ = False + +printStatement s = show s ++ "\n" + +inlineFunc :: Ctx Func -> [Ctx Expr] -> OState (Expr,[Ctx Statement]) +inlineFunc f@(Ctx _ (Func (FuncDec _ t _) _)) args + | t == LLVoid = error "cannot inline a void function in a context that requires a value (internal error!)" + | otherwise = do + ret <- mkName "ret" + putRetVar ret + (endStmts,stmts) <- inlineProc f args + let count = countOfSetsOf ret stmts + case (count,last stmts) of + (1,Ctx _ (Do ((Ctx _ (Set _ expr))))) -> return (ctxItem expr,init stmts) + _ -> return ((Get (nullCtx ret,All)),(nullCtx $ Decl (Var ret t) Nothing): (stmts ++ endStmts)) + +inlineVoidFunc :: Ctx Func -> [Ctx Expr] -> OState [Ctx Statement] +inlineVoidFunc f args = do + (endStmts,stmts) <- inlineProc f args + return (stmts ++ endStmts) + +inlineStmts :: String -> [Statement] -> OState [Statement] +inlineStmts _ [] = return [] +inlineStmts endLabel (NullStmt:NullStmt:stmts) = inlineStmts endLabel (NullStmt:stmts) +inlineStmts endLabel (NullStmt:stmts) = inlineStmts endLabel stmts >>= return . (NullStmt:) +inlineStmts endLabel (Return Nothing:stmts) = inlineStmts endLabel stmts >>= return . (Jump endLabel:) +inlineStmts endLabel (Return (Just expr):stmts) = do + stmts' <- inlineStmts endLabel stmts + retVar <- get >>= return . optRetVar + (renames,rewrites) <- getRewriteInfo + case retVar of + Nothing -> return (Do (rewriteCtxExpr' renames rewrites expr):Jump endLabel:stmts') + Just rv -> + return (Do (nullCtx (Set (nullCtx rv,All) (rewriteCtxExpr' renames rewrites expr))):Jump endLabel:stmts') +inlineStmts endLabel (Decl v mexpr:stmts) = do + v' <- renameVar v + (renames,rewrites) <- getRewriteInfo + stmts' <- inlineStmts endLabel stmts + return (Decl v' (fmap (rewriteCtxExpr' renames rewrites) mexpr):stmts') +inlineStmts endLabel (Do expr:stmts) = do + stmts' <- inlineStmts endLabel stmts + (renames,rewrites) <- getRewriteInfo + return (Do (rewriteCtxExpr' renames rewrites expr):stmts') +inlineStmts endLabel (Label s:stmts) = do + -- must rename the label BEFORE rewriting the rest of the statements + s' <- renameToNew s + stmts' <- inlineStmts endLabel stmts + return (Label s':stmts) +inlineStmts endLabel (Jump s:stmts) = do + -- must process the rest of the statements BEFORE rewriting the Jump + stmts' <- inlineStmts endLabel stmts + s' <- rewriteLabel s + return (Jump s':stmts') +inlineStmts endLabel (StateChange s:stmts) = inlineStmts endLabel stmts >>= return . (StateChange s:) +inlineStmts endLabel (Compound ss:stmts) = do + stmts' <- inlineStmts endLabel stmts + st <- get + ss' <- inlineStmts endLabel (map ctxItem ss) + return (Compound (map nullCtx ss'):stmts') +inlineStmts endLabel (While expr stmt:stmts) = do + (renames,rewrites) <- getRewriteInfo + stmts' <- inlineStmts endLabel stmts + ss <- inlineStmts endLabel [stmt] + return (While (rewriteCtxExpr' renames rewrites expr) (Compound (map nullCtx ss)) : stmts') +inlineStmts endLabel (DoWhile stmt expr:stmts) = do + stmts' <- inlineStmts endLabel stmts + ss <- inlineStmts endLabel [stmt] + (renames,rewrites) <- getRewriteInfo + return (DoWhile (Compound (map nullCtx ss)) (rewriteCtxExpr' renames rewrites expr) : stmts') +inlineStmts endLabel (For es0 e es1 stmt:stmts) = do + (renames,rewrites) <- getRewriteInfo + let rewriteExprs = map (rewriteCtxExpr' renames rewrites) + stmts' <- inlineStmts endLabel stmts + ss <- inlineStmts endLabel [stmt] + let body = case ss of + s:[] -> s + _ -> Compound (map nullCtx ss) + return (For (rewriteExprs es0) (fmap (rewriteCtxExpr' renames rewrites) e) (rewriteExprs es1) body: stmts') +inlineStmts endLabel (If e s0 s1:stmts) = do + (renames,rewrites) <- getRewriteInfo + stmts' <- inlineStmts endLabel stmts + s0s <- inlineStmts endLabel [s0] + s1s <- inlineStmts endLabel [s1] + return (If (rewriteCtxExpr' renames rewrites e) (Compound (map nullCtx s0s)) (Compound (map nullCtx s1s)) : stmts') + +runInliningOnFunc :: M.Map String FunctionFacts -> [Ctx Func] -> [Global] -> Ctx Func -> Ctx Func +runInliningOnFunc ff fs gs f = if noinlining f then f else + evalState (performInliningOnFunc f) (freshOptimizerState ff fs (map (\ (GDecl (Var nm _) _) -> nm) gs)) + +performInliningOnFunc :: Ctx Func -> OState (Ctx Func) +performInliningOnFunc f@(Ctx ctx (Func (FuncDec nm t parms) stmts)) = do + pushInlinerScope + parms' <- mapM renameParm parms + st <- get + put st { optVerbotenNames = Set.fromList verbotenNames, optLocals = [map (\ (Ctx _ v) -> varName v) parms'] } + stmts' <- mapM performInliningForStmt stmts + popInlinerScope + return (Ctx ctx (Func (FuncDec nm t parms') $ concat stmts')) + where verbotenNames = namesDefinedByFunc f + renameParm (Ctx _ (Var nm t)) = do + nm' <- renameToNewInInliner nm + return (nullCtx $ Var nm' t) + +runInliningOnHandler :: M.Map String FunctionFacts -> [Ctx Func] -> [Global] -> Handler -> Handler +runInliningOnHandler ff fs gs h = if noinlining (handlerName h) then h else + evalState (performInliningOnHandler h) (freshOptimizerState ff fs (map (\ (GDecl (Var nm _) _) -> nm) gs)) + +performInliningOnHandler :: Handler -> OState Handler +performInliningOnHandler h@(Handler nm parms stmts) = do + pushInlinerScope + parms' <- mapM renameParm parms + st <- get + put st { optVerbotenNames = Set.fromList verbotenNames, optLocals = [map (\ (Ctx _ v) -> varName v) parms] } + stmts' <- mapM performInliningForStmt stmts + popInlinerScope + return (Handler nm parms' (concat stmts')) + where verbotenNames = namesDefinedByHandler h + renameParm (Ctx _ (Var nm t)) = do + nm' <- renameToNewInInliner nm + return (nullCtx $ Var nm' t) + +performInliningForStmt :: Ctx Statement -> OState [Ctx Statement] +performInliningForStmt s@(Ctx _ (Do (Ctx _ (Call cnm exprs)))) = do + refreshOState + (es,sss) <- inlineExprs exprs + fs <- get >>= return . optAllFuncs + case M.lookup (ctxItem cnm) fs of + Nothing -> return (concat sss ++ [nullCtx $ (Do (nullCtx (Call cnm es)))]) + Just f -> do + ss <- inlineVoidFunc f es + return $ (concat sss) ++ ss +performInliningForStmt s@(Ctx _ (Do expr)) = do + refreshOState + expr' <- inlineExpr expr + stmts <- removeOStateStmts + return (stmts ++ [nullCtx (Do expr')]) +performInliningForStmt s@(Ctx _ (Compound ss)) = do + pushInlinerScope + refreshOState + st <- get + let locals = optLocals st + put st { optLocals = []:locals } + sss <- mapM performInliningForStmt ss + put st { optLocals = locals } + popInlinerScope + return [(nullCtx (Compound (concat sss)))] +performInliningForStmt (Ctx _ (While expr s)) = do + refreshOState + bgnLoop <- mkName "bgnLoop" + (expr',stmts) <- inlineExpr' expr + ss <- performInliningForStmt (nullCtx s) + return $ case (stmts,ss) of + ([],[s']) -> [nullCtx $ While expr' (ctxItem s')] + ([],_) -> [nullCtx $ While expr' (Compound ss)] + _ -> ((nullCtx $ Label bgnLoop) : (stmts ++ case ss of + [] -> [nullCtx $ If expr' (Jump bgnLoop) NullStmt] + _ -> [nullCtx $ If expr' (Compound (ss ++ [nullCtx $ Jump bgnLoop])) NullStmt])) +performInliningForStmt s@(Ctx _ NullStmt) = refreshOState >> return [nullCtx NullStmt] +performInliningForStmt s@(Ctx _ (Decl (Var v t) Nothing)) = do + refreshOState + v' <- renameToNewInInliner v + pushLocal $ v' + return [nullCtx (Decl (Var v' t) Nothing)] +performInliningForStmt s@(Ctx _ (Decl (Var v t) (Just expr))) = do + refreshOState + (expr',stmts) <- inlineExpr' expr + v' <- renameToNewInInliner v + pushLocal v' + return (stmts ++ [nullCtx (Decl (Var v' t) (Just expr'))]) +performInliningForStmt s@(Ctx _ (Return Nothing)) = refreshOState >> return [nullCtx (Return Nothing)] +performInliningForStmt s@(Ctx _ (Return (Just expr))) = do + refreshOState + expr' <- inlineExpr expr + stmts <- removeOStateStmts + return (stmts ++ [nullCtx (Return (Just expr'))]) +performInliningForStmt (Ctx _ l@(Label _)) = refreshOState >> return [nullCtx l] +performInliningForStmt (Ctx _ j@(Jump _)) = refreshOState >> return [nullCtx j] +performInliningForStmt (Ctx _ s@(StateChange _)) = refreshOState >> return [nullCtx s] +performInliningForStmt (Ctx _ (DoWhile s expr)) = do + refreshOState + bgnLoop <- mkName "bgnLoop" + ss <- performInliningForStmt (nullCtx s) + (expr',stmts) <- inlineExpr' expr + let s' = case ss ++ stmts of + [s''] -> ctxItem s'' + ss' -> Compound ss' + return [nullCtx $ DoWhile s' expr'] + --return ((nullCtx $ Label bgnLoop) : (ss ++ stmts ++ [nullCtx $ If expr' (Jump bgnLoop) NullStmt])) +performInliningForStmt (Ctx _ (For ies1 mte ses2 s)) = do + refreshOState + bgnLoop <- mkName "bgnLoop" + (ies1',iss1s) <- inlineExprs ies1 + (mte',tss) <- case mte of + Nothing -> return (Nothing,[]) + Just te -> do (te',tss) <- inlineExpr' te + return (Just te',tss) + (ses2', sss2s) <- inlineExprs ses2 + ss <- performInliningForStmt (nullCtx s) + let iss1 = concat iss1s + let sss2 = concat sss2s + case (iss1,tss,sss2,ss) of + ([],[],[],[s']) -> return [(nullCtx $ For ies1' mte' ses2' (ctxItem s'))] + ([],[],[],_) -> return [(nullCtx $ For ies1' mte' ses2' (Compound ss))] + _ -> return (iss1 ++ (map (nullCtx . Do) ies1') ++ [nullCtx $ Label bgnLoop] ++ rest) + where rest = case mte' of + Nothing -> ss ++ sss2 ++ (map (nullCtx . Do) ses2') ++ [nullCtx $ Jump bgnLoop] + Just te' -> tss ++ + [nullCtx $ If te' (Compound (ss ++ sss2 ++ (map (nullCtx . Do) ses2') ++ [nullCtx $ Jump bgnLoop])) NullStmt] +performInliningForStmt (Ctx _ (If e s0 s1)) = do + refreshOState + (e',ess) <- inlineExpr' e + s0s <- performInliningForStmt (nullCtx s0) + s1s <- performInliningForStmt (nullCtx s1) + let branch1 = case s0s of + [s] -> ctxItem s + _ -> Compound s0s + let branch2 = case s1s of + [s] -> ctxItem s + _ -> Compound s1s + return (ess ++ [nullCtx $ If e' branch1 branch2]) + +inlineExprs :: [Ctx Expr] -> OState ([Ctx Expr], [[Ctx Statement]]) +inlineExprs exprs = do + es <- mapM inlineExpr' exprs + return $ foldr combine ([],[]) es + where combine :: (Ctx Expr, [Ctx Statement]) -> ([Ctx Expr],[[Ctx Statement]]) -> ([Ctx Expr],[[Ctx Statement]]) + combine (e,ss) (es,sss) = (e:es,ss:sss) +inlineExpr' :: Ctx Expr -> OState (Ctx Expr,[Ctx Statement]) +inlineExpr' expr = do + expr' <- inlineExpr expr + stmts <- removeOStateStmts + return (expr',stmts) + +inlineExpr :: Ctx Expr -> OState (Ctx Expr) +inlineExpr = everywhereM (mkM inlineCall `extM` renameRef) + +inlineCall c@(Call (Ctx _ nm) es) = do + fs <- get >>= return . optAllFuncs + tmp <- get >>= return . optInlinerRenames + tmp1 <- get >>= return . optRenames + case M.lookup nm fs of + Nothing -> return c + Just f -> do + (e, stmts) <- inlineFunc f es + st <- get + put st { optStmts = (stmts:(optStmts st)) } + return e +inlineCall e = return e +renameRef :: (Ctx String, Component) -> OState (Ctx String,Component) +renameRef (Ctx ctx nm, v) = do + nm' <- inlinerRenamingFor nm + return (Ctx ctx nm', v) + +isRelativelyPure :: [String] -> (M.Map String FunctionFacts) -> Ctx Expr -> Bool +isRelativelyPure locals ff = everything (&&) (True `mkQ` go) + where + go (Get (cnm,_)) = nm `elem` locals || nm `elem` (map constName allConstants) where nm = ctxItem cnm + go (Set _ _) = False + go (IncBy _ _) = False + go (DecBy _ _) = False + go (MulBy _ _) = False + go (DivBy _ _) = False + go (ModBy _ _) = False + go (PostInc _) = False + go (PostDec _) = False + go (PreInc _) = False + go (PreDec _) = False + go (Call (Ctx _ nm) _) = + case M.lookup nm ff of + Nothing -> False + Just facts -> isPureFunction facts + go _ = True + +-- a notion of how much codespace would be wasted if an expression +-- was copied wherever it was used, versus computed in one place +staticComplexity :: Ctx Expr -> Int +staticComplexity = everything (+) (0 `mkQ` go) + where go :: Expr -> Int + go e = 1 + +rewriteCtxExpr' :: (M.Map String String) -> (M.Map String Expr) -> Ctx Expr -> Ctx Expr +rewriteCtxExpr' renames rewrites = everywhere' (mkT rwName `extT` rwExpr) + where + rwExpr e@(Get (Ctx _ nm,All)) = + case M.lookup nm rewrites of + Nothing -> e + Just e' -> e' + rwExpr e = e + rwName c@(Ctx _ nm) = + case M.lookup nm renames of + Nothing -> c + Just nm' -> nullCtx nm' + +usageCount :: String -> [Ctx Statement] -> Int +usageCount nm = everything (+) (0 `mkQ` refCount) + where refCount :: (Ctx String, Component) -> Int + refCount (Ctx _ nm',_) | nm == nm' = 1 + | otherwise = 0 + +isUsedOnlyWholeIn :: String -> [Ctx Statement] -> Bool +isUsedOnlyWholeIn nm = everything (&&) (True `mkQ` whole) + where whole :: (Ctx String, Component) -> Bool + whole (_, All) = True + whole (Ctx _ nm',_) | nm == nm' = False + | otherwise = True + +countOfSetsOf :: String -> [Ctx Statement] -> Int +countOfSetsOf nm = everything (+) (0 `mkQ` count) + where count (Set (Ctx _ nm',All) _) | nm == nm' = 1 + | otherwise = 0 + count _ = 0 + +isModifiedIn :: String -> [Ctx Statement] -> Bool +isModifiedIn nm = everything (||) (False `mkQ` modified) + where modified (Set (Ctx _ nm',_) _) = nm == nm' + modified (IncBy (Ctx _ nm',_) _) = nm == nm' + modified (DecBy (Ctx _ nm',_) _) = nm == nm' + modified (MulBy (Ctx _ nm',_) _) = nm == nm' + modified (DivBy (Ctx _ nm',_) _) = nm == nm' + modified (ModBy (Ctx _ nm',_) _) = nm == nm' + modified (PreDec (Ctx _ nm',_)) = nm == nm' + modified (PreInc (Ctx _ nm',_)) = nm == nm' + modified (PostDec (Ctx _ nm',_)) = nm == nm' + modified (PostInc (Ctx _ nm',_)) = nm == nm' + modified _ = False + +jumpsTo :: String -> [Ctx Statement] -> Int +jumpsTo label = everything (+) (0 `mkQ` count) + where count (Jump l) | l == label = 1 + | otherwise = 0 + count _ = 0 + +withoutFinalJumpTo label [] = [] +withoutFinalJumpTo label ss = + case final of + (Ctx _ (Jump l)) | l == label -> initial + | otherwise -> ss + (Ctx c (Compound ss')) -> initial ++ [(Ctx c (Compound (withoutFinalJumpTo label ss')))] + (Ctx c (If expr s0 s1)) -> initial ++ [Ctx c (If expr (rmvj s0) (rmvj s1))] + (Ctx c s) -> ss + where final = last ss + initial = init ss + rmvj s@(Jump l) | l == label = NullStmt + | otherwise = s + rmvj (Compound ss) = Compound (withoutFinalJumpTo label ss) + rmvj s = s + +-- an explicit dictionary (could create a class for this, but seems unnecessary) +data ScopeFuncs m = ScopeFuncs { sfPushFrame :: m (), sfPopFrame :: m (), sfPushVar :: String -> m (), sfVars :: m [String] } + +type NamesState = State.State [[String]] + +nameStateScopeFuncs :: ScopeFuncs NamesState +nameStateScopeFuncs = ScopeFuncs pushFrame popFrame pushVar (get >>= return . concat) + +pushFrame :: NamesState () +pushFrame = get >>= put . ([]:) +popFrame :: NamesState () +popFrame = get >>= ( \ s -> if null s then error "stack empty: cannot pop frame" else put (tail s)) +pushVar v = do + st <- get + case st of + [] -> error "stack empty: cannot add variable" + (f:fs) -> put ((v:f):fs) + +sccsPurity :: M.Map String Expr -> M.Map String FunctionFacts -> [SCC (Ctx Func)] -> M.Map String FunctionFacts +sccsPurity gcs ff = foldl' (sccPurity gcs) ff + +sccPurity :: M.Map String Expr -> M.Map String FunctionFacts -> SCC (Ctx Func) -> M.Map String FunctionFacts +sccPurity gcs ff scc = + case scc of + AcyclicSCC f -> go [f] + CyclicSCC fs -> go fs + where go fs = ff `M.union` ( M.fromList $ map ( \ f -> (fname f, FunctionFacts purity)) fs) + where purity = not $ or $ map (isImpure (M.keysSet gcs) ff) fs + +stmtIn sfs s@(Compound _) = (sfPushFrame sfs) >> return s +stmtIn _ s = return s +funcDecIn sfs fd@(Func (FuncDec _ _ parms) _) = (sfPushFrame sfs) >> mapM_ (\ cv -> (sfPushVar sfs) $ (varName . ctxItem) cv) parms >> return fd +stmtOut sfs s@(Compound _) = (sfPopFrame sfs) >> return s +stmtOut sfs s@(Decl v _) = ((sfPushVar sfs) $ varName v) >> return s +stmtOut _ s = return s +handlerDecIn sfs h@(Handler _ parms _) = (sfPushFrame sfs) >> mapM_ (\ cv -> (sfPushVar sfs) $ (varName . ctxItem) cv) parms >> return h +handlerDecOut sfs h@(Handler _ _ _) = (sfPopFrame sfs) >> return h +funcDecOut sfs f@(Func (FuncDec _ _ _) _) = (sfPopFrame sfs) >> return f + +cvt :: Monad m => (a -> m a) -> b -> a -> m b +cvt f v x = f x >> return v + +isImpure :: Set.Set String -> M.Map String FunctionFacts -> Ctx Func -> Bool +isImpure consts ff f = + evalState (go f) [] + where go :: Ctx Func -> NamesState Bool + go = everythingTwice (liftM2 (||)) (return False `mkQ` cvt (funcDecIn nameStateScopeFuncs) False `extQ` + cvt (stmtIn nameStateScopeFuncs) False `extQ` call `extQ` ref nameStateScopeFuncs) + (return False `mkQ` cvt (stmtOut nameStateScopeFuncs) False) + call c@(Call nm _) = do + case M.lookup (ctxItem nm) ff of + Just (FunctionFacts { isPureFunction = False }) -> return True + _ -> return False + call e = return False + ref:: ScopeFuncs NamesState -> (Ctx String, Component) -> NamesState Bool + ref sfs v@(Ctx _ nm,_) = do + locals <- sfVars sfs + return (nm `notElem` locals && (not $ isConst nm)) + isConst nm = (nm `Set.member` consts) || (nm `elem` map constName allConstants) + +isConstant :: Data a => String -> [a] -> Bool +isConstant s xs = not $ evalState (isntConstantM s xs) [] + +isntConstantM :: (Data a) => String -> [a] -> NamesState Bool +isntConstantM s = everythingTwice (liftM2 (||)) (return False `mkQ` cvt (funcDecIn sfs) False `extQ` + cvt (stmtIn sfs) False `extQ` modified `extQ` cvt (handlerDecIn sfs) False) + (return False `mkQ` cvt (funcDecOut sfs) False `extQ` + cvt (handlerDecOut sfs) False `extQ` cvt (stmtOut sfs) False) + where sfs = nameStateScopeFuncs + checkNm nm = (sfVars sfs) >>= return . ((nm == s) &&) . (notElem nm) + modified (Set (Ctx _ nm,_) _) = checkNm nm + modified (IncBy (Ctx _ nm,_) _) = checkNm nm + modified (DecBy (Ctx _ nm,_) _) = checkNm nm + modified (MulBy (Ctx _ nm,_) _) = checkNm nm + modified (DivBy (Ctx _ nm,_) _) = checkNm nm + modified (ModBy (Ctx _ nm,_) _) = checkNm nm + modified (PreDec (Ctx _ nm,_)) = checkNm nm + modified (PreInc (Ctx _ nm,_)) = checkNm nm + modified (PostDec (Ctx _ nm,_)) = checkNm nm + modified (PostInc (Ctx _ nm,_)) = checkNm nm + modified _ = return False + +reachableGlobs gs fs ss = [ g | g@(GDecl (Var nm _) _) <- gs, (nm `isUsedIn` fs || nm `isUsedIn` ss)] + +isUsedIn :: Data a => String -> a -> Bool +isUsedIn s v = + evalState + (everythingTwice (liftM2 (||)) (return False `mkQ` cvt (funcDecIn sfs) False `extQ` + cvt (stmtIn sfs) False `extQ` used `extQ` cvt (handlerDecIn sfs) False) + (return False `mkQ` cvt (funcDecOut sfs) False `extQ` + cvt (handlerDecOut sfs) False `extQ` cvt (stmtOut sfs) False) v) [] + where sfs = nameStateScopeFuncs + used :: (Ctx String,Component) -> NamesState Bool + used (Ctx _ nm,_) = (sfVars sfs) >>= return . ((nm == s) &&) . (notElem nm) + +globalConstants :: [Global] -> [Ctx Func] -> [State] -> M.Map String Expr +globalConstants gs fs ss = + foldl globalConstant M.empty gs + where globalConstant m (GDecl (Var nm t) mexpr) = if isAConst nm then M.insert nm (mexpr2expr m t mexpr) m else m + isAConst nm = isConstant nm fs && isConstant nm ss + expr2expr :: M.Map String Expr -> Expr -> Expr + expr2expr m = everywhere (mkT go) + where go e@(Get (nm,All)) = case M.lookup (ctxItem nm) m of + Nothing -> e + Just e' -> e' + go e@(Neg (Ctx _ (IntLit i))) = (IntLit (-i)) + go e@(Neg (Ctx _ (FloatLit f))) = (FloatLit (-f)) + go e = e + mexpr2expr m _ (Just expr) = expr2expr m expr + mexpr2expr m LLFloat Nothing = FloatLit 0 + mexpr2expr m LLInteger Nothing = IntLit 0 + mexpr2expr m LLString Nothing = StringLit "" + mexpr2expr m LLList Nothing = ListExpr [] + mexpr2expr m LLVector Nothing = VecExpr (nullCtx $ FloatLit 0) (nullCtx $ FloatLit 0) (nullCtx $ FloatLit 0) + mexpr2expr m LLRot Nothing = RotExpr (nullCtx $ FloatLit 0) (nullCtx $ FloatLit 0) (nullCtx $ FloatLit 0) (nullCtx $ FloatLit 1) + mexpr2expr m LLKey Nothing = KeyLit "" + mexpr2expr m LLVoid Nothing = error "somehow, an expression of type void?" + +bb2int :: (a -> a -> Bool) -> a -> a -> Int +bb2int op x y = if op x y then 1 else 0 + +fromBool :: Num a => Bool -> a +fromBool x = if x then 1 else 0 + +liftBool :: Monad m => Bool -> m Expr +liftBool b = return (IntLit (fromBool b)) + +data SimplificationInfo = SimplificationInfo { + siScript :: !CompiledLSLScript, + siPureFuncs :: !(Set.Set String), + siConstants :: !(M.Map String Expr), + siLocalsInScope :: ![[String]] + } + +simpInfoScopeFuncs = ScopeFuncs { + sfPushFrame = get >>= \ si -> put si { siLocalsInScope = [] : (siLocalsInScope si) }, + sfPopFrame = do + si <- get + case siLocalsInScope si of + [] -> error "stack empty, cannot pop frame" + (f:fs) -> put si { siLocalsInScope = fs }, + sfPushVar = (\ s -> do + si <- get + case siLocalsInScope si of + [] -> error "stack empty, cannot push variable" + (f:fs) -> put si { siLocalsInScope = ((s:f):fs) }), + sfVars = get >>= return . concat . siLocalsInScope + } + +type SimpState a = State.State SimplificationInfo a + +floatToLit :: RealFloat a => a -> Expr +floatToLit = FloatLit . realToFrac + +valToExpr :: RealFloat a => LSLValue a -> Expr +valToExpr (IVal i) = IntLit i +valToExpr (FVal f) = floatToLit f +valToExpr (SVal s) = StringLit s +valToExpr (KVal k) = KeyLit k +valToExpr (VVal x y z) = VecExpr (nullCtx $ floatToLit x) (nullCtx $ floatToLit y) (nullCtx $ floatToLit z) +valToExpr (RVal x y z s) = RotExpr (nullCtx $ floatToLit x) (nullCtx $ floatToLit y) (nullCtx $ floatToLit z) (nullCtx $ floatToLit s) +valToExpr (LVal l) = ListExpr (map (nullCtx . valToExpr) l) +valToExpr VoidVal = error "can't convert the void value to an expression" + +predefToLit :: String -> Maybe Expr +predefToLit s = fmap valToExpr (findConstVal s) + +constVarToLit :: M.Map String Expr -> String -> Maybe Expr +constVarToLit m s = M.lookup s m + +nameToLit :: M.Map String Expr -> String -> Maybe Expr +nameToLit m s = predefToLit s `mplus` constVarToLit m s + +exprsToVals :: [Ctx Expr] -> Maybe [LSLValue Double] +exprsToVals es = mapM exprToVal es + where exprToVal :: Ctx Expr -> Maybe (LSLValue Double) + exprToVal (Ctx _ (IntLit i)) = Just (IVal i) + exprToVal (Ctx _ (FloatLit f)) = Just (FVal f) + exprToVal (Ctx _ (StringLit s)) = Just (SVal s) + exprToVal (Ctx _ (KeyLit k)) = Just (KVal k) + exprToVal (Ctx _ (VecExpr ex ey ez)) = + case (exprToVal ex, exprToVal ey, exprToVal ez) of + (Just (FVal x),Just (FVal y),Just (FVal z)) -> Just (VVal x y z) + _ -> Nothing + exprToVal (Ctx _ (RotExpr ex ey ez es)) = + case (exprToVal ex, exprToVal ey, exprToVal ez, exprToVal es) of + (Just (FVal x),Just (FVal y),Just (FVal z),Just (FVal s)) -> Just (RVal x y z s) + _ -> Nothing + exprToVal (Ctx _ (ListExpr es)) = case exprsToVals es of + Nothing -> Nothing + Just vs -> Just $ LVal vs + exprToVal _ = Nothing + +simplifyE :: Expr -> SimpState Expr +simplifyE (Neg (Ctx _ (IntLit i))) = return (IntLit (-i)) +simplifyE (Not (Ctx _ (IntLit i))) = return (IntLit (fromBool (i == 0))) +simplifyE (Add (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (i + j)) +simplifyE (Mul (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (i * j)) +simplifyE (Sub (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (i - j)) +simplifyE (And (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (fromBool (i /= 0 && j /= 0))) +simplifyE (Or (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (fromBool (i /= 0 || j /= 0))) +simplifyE (Lt (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (bb2int (<) i j)) +simplifyE (Gt (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (bb2int (>) i j)) +simplifyE (Ge (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (bb2int (>=) i j)) +simplifyE (Le (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (bb2int (<=) i j)) +simplifyE (Equal (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (bb2int (==) i j)) +simplifyE (NotEqual (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (bb2int (==) i j)) +simplifyE (BAnd (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (i .&. j)) +simplifyE (BOr (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (i .|. j)) +simplifyE (Xor (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (i `xor` j)) +simplifyE (ShiftL (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (i `shiftL` j)) +simplifyE (ShiftR (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) = return (IntLit (i `shiftR` j)) +simplifyE e@(Div (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) | j /= 0 = return (IntLit ( i `div` j)) + | otherwise = return e +simplifyE e@(Mod (Ctx _ (IntLit i)) (Ctx _ (IntLit j))) | j /= 0 = return (IntLit ( i `mod` j)) + | otherwise = return e +simplifyE (Neg (Ctx _ (FloatLit i))) = return (FloatLit (-i)) +simplifyE (Add (Ctx _ (FloatLit i)) (Ctx _ (FloatLit j))) = return (FloatLit (i + j)) +simplifyE (Mul (Ctx _ (FloatLit i)) (Ctx _ (FloatLit j))) = return (FloatLit (i * j)) +simplifyE (Sub (Ctx _ (FloatLit i)) (Ctx _ (FloatLit j))) = return (FloatLit (i - j)) +simplifyE (Lt (Ctx _ (FloatLit i)) (Ctx _ (FloatLit j))) = return (IntLit (bb2int (<) i j)) +simplifyE (Gt (Ctx _ (FloatLit i)) (Ctx _ (FloatLit j))) = return (IntLit (bb2int (>) i j)) +simplifyE (Ge (Ctx _ (FloatLit i)) (Ctx _ (FloatLit j))) = return (IntLit (bb2int (>=) i j)) +simplifyE (Le (Ctx _ (FloatLit i)) (Ctx _ (FloatLit j))) = return (IntLit (bb2int (<=) i j)) +simplifyE (Equal (Ctx _ (FloatLit i)) (Ctx _ (FloatLit j))) = return (IntLit (bb2int (==) i j)) +simplifyE (NotEqual (Ctx _ (FloatLit i)) (Ctx _ (FloatLit j))) = return (IntLit (bb2int (==) i j)) +simplifyE e@(Div (Ctx _ (FloatLit i)) (Ctx _ (FloatLit j))) | j /= 0 = return (FloatLit ( i / j)) + | otherwise = return e +simplifyE (Add (Ctx _ (IntLit i)) (Ctx _ (FloatLit j))) = return (FloatLit (fromIntegral i + j)) +simplifyE (Mul (Ctx _ (IntLit i)) (Ctx _ (FloatLit j))) = return (FloatLit (fromIntegral i * j)) +simplifyE (Sub (Ctx _ (IntLit i)) (Ctx _ (FloatLit j))) = return (FloatLit (fromIntegral i - j)) +simplifyE e@(Div (Ctx _ (IntLit i)) (Ctx _ (FloatLit j))) | j /= 0 = return (FloatLit ( fromIntegral i / j)) + | otherwise = return e +simplifyE (Equal (Ctx _ (IntLit i)) (Ctx _ (FloatLit j))) = return (IntLit (if fromIntegral i == j then 1 else 0)) +simplifyE (NotEqual (Ctx _ (IntLit i)) (Ctx _ (FloatLit j))) = return (IntLit (if fromIntegral i == j then 0 else 1)) +simplifyE (Lt (Ctx _ (IntLit i)) (Ctx _ (FloatLit j))) = return (IntLit (if fromIntegral i < j then 1 else 0)) +simplifyE (Gt (Ctx _ (IntLit i)) (Ctx _ (FloatLit j))) = return (IntLit (if fromIntegral i > j then 1 else 0)) +simplifyE (Le (Ctx _ (IntLit i)) (Ctx _ (FloatLit j))) = return (IntLit (if fromIntegral i <= j then 1 else 0)) +simplifyE (Ge (Ctx _ (IntLit i)) (Ctx _ (FloatLit j))) = return (IntLit (if fromIntegral i >= j then 1 else 0)) +simplifyE (Add (Ctx _ (FloatLit i)) (Ctx _ (IntLit j))) = return (FloatLit (i + fromIntegral j)) +simplifyE (Mul (Ctx _ (FloatLit i)) (Ctx _ (IntLit j))) = return (FloatLit (i * fromIntegral j)) +simplifyE (Sub (Ctx _ (FloatLit i)) (Ctx _ (IntLit j))) = return (FloatLit (i - fromIntegral j)) +simplifyE e@(Div (Ctx _ (FloatLit i)) (Ctx _ (IntLit j))) | j /= 0 = return (FloatLit ( i / fromIntegral j)) + | otherwise = return e +simplifyE (Equal (Ctx _ (FloatLit i)) (Ctx _ (IntLit j))) = return (IntLit (if i == fromIntegral j then 1 else 0)) +simplifyE (NotEqual (Ctx _ (FloatLit i)) (Ctx _ (IntLit j))) = return (IntLit (if i == fromIntegral j then 0 else 1)) +simplifyE (Lt (Ctx _ (FloatLit i)) (Ctx _ (IntLit j))) = return (IntLit (if i < fromIntegral j then 1 else 0)) +simplifyE (Gt (Ctx _ (FloatLit i)) (Ctx _ (IntLit j))) = return (IntLit (if i > fromIntegral j then 1 else 0)) +simplifyE (Le (Ctx _ (FloatLit i)) (Ctx _ (IntLit j))) = return (IntLit (if i <= fromIntegral j then 1 else 0)) +simplifyE (Ge (Ctx _ (FloatLit i)) (Ctx _ (IntLit j))) = return (IntLit (if i >= fromIntegral j then 1 else 0)) +simplifyE e@(Get (nm,c)) = do + locals <- get >>= return . concat . siLocalsInScope + if name `elem` locals + then return e + else newExpr + where name = ctxItem nm + newExpr = do + m <- get >>= return . siConstants + return $ case nameToLit m name of + Nothing -> e + Just e' -> case (c,e') of + (All,_) -> e' + (X,VecExpr x _ _) -> ctxItem x + (X,RotExpr x _ _ _) -> ctxItem x + (Y,VecExpr _ y _) -> ctxItem y + (Y,RotExpr _ y _ _) -> ctxItem y + (Z,VecExpr _ _ z) -> ctxItem z + (Z,RotExpr _ _ z _) -> ctxItem z + (S,RotExpr _ _ _ s) -> ctxItem s + _ -> e +simplifyE e@(Call (Ctx _ nm) exprs) = + case exprsToVals exprs of + Nothing -> return e + Just vs -> + case lookup nm internalLLFuncs of + Just f -> return (valToExpr $ snd (Id.runIdentity (f () vs))) + Nothing -> do + pureFuncs <- get >>= return . siPureFuncs + script <- get >>= return . siScript + if nm `Set.member` pureFuncs + then case simSFunc (script,[nm]) [] vs of + Left _ -> return e + Right (VoidVal,_) -> return e + Right (v,_) -> return $ valToExpr v + else return e +simplifyE e@(Cast LLString (Ctx _ (IntLit i))) = return (StringLit (show i)) +simplifyE e@(Cast LLString (Ctx _ (FloatLit f))) = return (StringLit s) + where SVal s = toSVal (FVal f') + f' :: Float + f' = realToFrac f +simplifyE e@(Cast LLFloat (Ctx _(IntLit i))) = return (FloatLit $ fromIntegral i) +simplifyE e@(Cast LLInteger (Ctx _ (FloatLit f))) = return (IntLit $ truncate f) +simplifyE e@(Add (Ctx _ (StringLit s0)) (Ctx _ (StringLit s1))) = return (StringLit (s0 ++ s1)) +simplifyE e = return e + +simplify :: Data a => CompiledLSLScript -> Set.Set String -> M.Map String Expr -> a -> a +simplify script pureFuncs gcs v = + evalState (go v) (SimplificationInfo script pureFuncs gcs []) + where go :: Data a => a -> SimpState a + go = downup (mkM (stmtIn simpInfoScopeFuncs) `extM` funcDecIn simpInfoScopeFuncs `extM` handlerDecIn simpInfoScopeFuncs) + (mkM simplifyE `extM` stmtOut simpInfoScopeFuncs `extM` + funcDecOut simpInfoScopeFuncs `extM` handlerDecOut simpInfoScopeFuncs)
+ src/Language/Lsl/Internal/OptimizerOptions.hs view
@@ -0,0 +1,3 @@+module Language.Lsl.Internal.OptimizerOptions(OptimizerOption(..)) where + +data OptimizerOption = OptimizationInlining deriving (Show,Eq)
+ src/Language/Lsl/Internal/Pragmas.hs view
@@ -0,0 +1,7 @@+{-# OPTIONS_GHC -XDeriveDataTypeable #-} +module Language.Lsl.Internal.Pragmas(Pragma(..)) where + +import Data.Data(Data,Typeable) + +data Pragma = PragmaInline | PragmaNoInline + deriving (Show,Ord,Eq,Typeable,Data)
src/Language/Lsl/Internal/Type.hs view
@@ -7,6 +7,8 @@ typeOfLSLComponent, parseFloat, parseInt, + parseVector, + parseRotation, lslValueComponent, replaceLslValueComponent, lslValString, @@ -43,16 +45,16 @@ import Language.Lsl.Internal.Util(lookupM,readM,cross,quaternionMultiply,quaternionToMatrix,fromInt) import Language.Lsl.Internal.DOMProcessing(Element(..),ElemAcceptor(..),findValue,elementsOnly,simple,attrString,acceptList) -import Text.Printf(printf) +import Text.Printf(printf,PrintfArg(..)) data LSLType = LLList | LLInteger | LLVector | LLFloat | LLString | LLRot | LLKey | LLVoid deriving (Eq, Show, Typeable, Data) -- A value. Values correspond to the built in types (LSLType) that LSL -- supports. A value is an item that can be pushed onto the value stack. -data LSLValue = IVal Int | FVal Float | SVal String | VVal Float Float Float - | RVal Float Float Float Float | LVal [LSLValue] | KVal String - | VoidVal deriving (Show,Eq,Ord) +data LSLValue a = IVal Int | FVal a | SVal String | VVal a a a + | RVal a a a a | LVal [LSLValue a] | KVal String + | VoidVal deriving (Show,Eq,Ord) data Component = X | Y | Z | S | All deriving (Eq,Show, Typeable, Data) @@ -84,6 +86,7 @@ replaceLslValueComponent All v v' = v' replaceLslValueComponent c v v' = error ("can't replace component " ++ (show c) ++ " of value " ++ (show v) ++ " with value " ++ (show v')) +typeOfLSLValue :: (RealFloat a) => LSLValue a -> LSLType typeOfLSLValue v = case v of (IVal _) -> LLInteger @@ -103,7 +106,7 @@ -- convert a value to a string 'internally' (TODO: where SHOULD this be used? It's used in internal funcs, but -- probably will not work completely correctly when tabs, newlines, or double quotes are involved) lslValString (IVal i) = (show i) -lslValString (FVal f) = (printf "%.6f" f) +lslValString (FVal f) = (printf "%.6f" ((realToFrac f) :: Double)) lslValString (SVal s) = s lslValString (KVal k) = k lslValString (VVal x y z) = concat ["<",comp2Str x,",",comp2Str y,",",comp2Str z,">"] @@ -186,9 +189,9 @@ [] -> reads s v -> v -toSVal :: LSLValue -> LSLValue +toSVal :: RealFloat a => LSLValue a -> LSLValue a toSVal (SVal s) = SVal s -toSVal (FVal f) = SVal (printf "%.6f" f) +toSVal (FVal f) = SVal (printf "%.6f" (realToFrac f :: Double)) toSVal (IVal i) = SVal (show i) toSVal (KVal k) = SVal k toSVal (VVal x y z) = SVal $ concat ["<",comp2Str x,",",comp2Str y,",",comp2Str z,">"] @@ -198,10 +201,10 @@ SVal $ concatMap toS l where toS v = let (SVal s) = toSVal v in s -comp2Str :: Float -> String -comp2Str f = printf "%.5f" f +comp2Str :: RealFloat a => a -> String +comp2Str f = printf "%.5f" (realToFrac f :: Double) -lslValueElement :: MonadError String m => ElemAcceptor m LSLValue +lslValueElement :: (RealFloat a, Read a, MonadError String m) => ElemAcceptor m (LSLValue a) lslValueElement = let f e@(Elem _ attrs contents) = do valType <- lookupM "class" attrs @@ -256,4 +259,4 @@ liftV2 f = \ x y -> vec2VVal $ f (vVal2Vec x) (vVal2Vec y) rot2RVal (x,y,z,s) = RVal x y z s -rVal2Rot (RVal x y z s) = (x,y,z,s)+rVal2Rot (RVal x y z s) = (x,y,z,s)
src/Language/Lsl/Internal/UnitTester.hs view
@@ -20,7 +20,7 @@ -- trace1 x = trace (show x) x -testExecutionElement :: MonadError String m => ElemAcceptor m (([(String,String)],[(String,String)]),[LSLUnitTest]) +testExecutionElement :: MonadError String m => ElemAcceptor m ((Bool,[(String,String)],[(String,String)]),[LSLUnitTest]) testExecutionElement = let f (Elem _ _ contents) = do (sources,contents1) <- findElement sourceFilesElement [ e | e@(CElem _ _) <- contents]
src/Language/Lsl/Internal/WorldState.hs view
@@ -203,7 +203,7 @@ randGen :: !StdGen, wlibrary :: ![(String,Validity LModule)], wscripts :: ![(String,Validity CompiledLSLScript)], - worldEventHandler :: !(Maybe (String, [(String,LSLValue)])), + worldEventHandler :: !(Maybe (String, [(String,LSLValue Float)])), worldAvatars :: !(Map String Avatar), worldBreakpointManager :: !BreakpointManager, worldSuspended :: !(Maybe (String,String)), -- prim-key, script-name, image @@ -285,7 +285,7 @@ getWorldPendingHTTPRequests = queryWorld worldPendingHTTPRequests getWorldOpenDataChannels :: Monad m => WorldM m (Map String (String,String), Map (String,String) String) getWorldOpenDataChannels = queryWorld worldOpenDataChannels -getWorldEventHandler :: Monad m => WorldM m (Maybe (String,[(String,LSLValue)])) +getWorldEventHandler :: Monad m => WorldM m (Maybe (String,[(String,LSLValue Float)])) getWorldEventHandler = queryWorld worldEventHandler getWorldXMLRequestRegistry :: Monad m => WorldM m (Map String XMLRequestSourceType) getWorldXMLRequestRegistry = queryWorld worldXMLRequestRegistry @@ -465,7 +465,7 @@ | ResetScript String String -- prim key, script name | ResetScripts String -- object name | WorldSimEvent { worldSimEventName :: String, worldSimEventArgs :: [SimEventArg] } - | DeferredScriptEvent { deferredScriptEvent :: Event, deferredScriptEventTarget :: DeferredScriptEventTarget } + | DeferredScriptEvent { deferredScriptEvent :: Event Float, deferredScriptEventTarget :: DeferredScriptEventTarget } | Chat { chatChannel :: Int, chatterName :: String, chatterKey :: String, chatMessage :: String, chatLocation :: ((Int,Int),(Float,Float,Float)), chatRange :: Maybe Float } @@ -567,12 +567,12 @@ data PredefFunc m = PredefFunc { predefFuncName :: String, predefFuncResultType :: LSLType, - predef :: ScriptInfo -> [LSLValue] -> ErrorT String (WorldM m) (EvalResult,LSLValue) } + predef :: ScriptInfo Float -> [LSLValue Float] -> ErrorT String (WorldM m) (EvalResult,LSLValue Float) } deriving (Show) -instance Monad m => Show (ScriptInfo -> [LSLValue] -> WorldM m (EvalResult,LSLValue)) where +instance Monad m => Show (ScriptInfo Float -> [LSLValue Float] -> WorldM m (EvalResult,LSLValue Float)) where showsPrec _ _ = showString "(function :: ScriptInfo -> [LSLValue] -> WorldM m (EvalResult,LSLValue))" -instance Monad m => Show (ScriptInfo -> [LSLValue] -> ErrorT String (WorldM m) (EvalResult,LSLValue)) where +instance Monad m => Show (ScriptInfo Float -> [LSLValue Float] -> ErrorT String (WorldM m) (EvalResult,LSLValue Float)) where showsPrec _ _ = showString "(function :: ScriptInfo -> [LSLValue] -> ErrorT String (WorldM m) (EvalResult,LSLValue))" evalErrorT :: ErrorT String m v -> m (Either String v)
+ src/Language/Lsl/NewUnitTest.hs view
@@ -0,0 +1,73 @@+module Language.Lsl.NewUnitTest( + LSLUnitTest(..), + FuncCallExpectations(..), + ExpectationMode(..), + EntryPoint(..), + Binding, + expectedReturns, + removeExpectation) where + +import Control.Monad(liftM2) +import Data.List(maximumBy) +import Language.Lsl.Internal.Type(LSLValue(..)) +import Language.Lsl.Internal.Exec(Binding(..)) +import Language.Lsl.Internal.Util(removeLookup) + +data FuncCallExpectations = FuncCallExpectations { + expectationMode :: ExpectationMode, + callList :: [((String, [Maybe LSLValue]),LSLValue)] } deriving (Show) + +data ExpectationMode = Nice | Normal | Exhaust | Strict deriving (Show,Eq) + +data EntryPoint = ModuleFunc String String | ScriptFunc String String | ScriptHandler String String String + deriving (Show) + +data LSLUnitTest = LSLUnitTest { + unitTestName :: String, + entryPoint :: EntryPoint, + initialGlobs :: [Binding], + arguments :: [LSLValue], + expectedCalls :: FuncCallExpectations, + expectedReturn :: Maybe LSLValue, + expectedGlobalVals :: [Binding], + expectedNewState :: Maybe String + } deriving (Show) + +argMatch (Just x) y | lslValuesMatch x y = Just 1 + | otherwise = Nothing +argMatch Nothing _ = Just 0 +argsMatch expectArgs args = foldl (liftM2 (+)) (Just 0) $ zipWith argMatch expectArgs args + +lslValuesMatch (FVal a) (FVal b) = a == b || + if a == 0.0 then b < 0.000001 + else if b == 0.0 then a < 0.000001 + else abs ((b - a) / a) < 0.000001 +lslValuesMatch x y = x == y + +matchFail :: Monad m => m a +matchFail = fail "no matching call" + +expectedReturns :: (Monad m) => String -> [LSLValue] -> FuncCallExpectations -> m ((String,[Maybe LSLValue]),LSLValue) +expectedReturns name args (FuncCallExpectations Strict (match@((name',expectArgs),returns):_)) = + if name /= name' || argsMatch expectArgs args == Nothing then matchFail + else return match +expectedReturns name args (FuncCallExpectations Strict _) = matchFail +expectedReturns n a (FuncCallExpectations mode callList) = + let rightNames = filter ((n==) . fst . fst) callList + argMatch (Just x) y | x == y = Just 1 + | otherwise = Nothing + argMatch Nothing _ = Just 0 + argsMatch args expectArgs = foldl (liftM2 (+)) (Just 0) $ zipWith argMatch expectArgs args + ranked = zip (map (argsMatch a) (map (snd . fst) rightNames)) rightNames + orderMatch (Nothing,_) (Nothing,_) = EQ + orderMatch _ (Nothing,_) = GT + orderMatch (Nothing,_) _ = LT + orderMatch ((Just a),_) ((Just b),_) = compare a b + in case ranked of + [] -> fail matchFail + _ -> case maximumBy orderMatch ranked of + (Nothing,_) -> matchFail + (_,e) -> return e + +removeExpectation :: (String,[Maybe LSLValue]) -> FuncCallExpectations -> FuncCallExpectations +removeExpectation m fce = fce { callList = removeLookup m (callList fce) }
+ src/Language/Lsl/NewUnitTestEnv.hs view
@@ -0,0 +1,270 @@+module Language.Lsl.NewUnitTestEnv( + simStep, + simFunc, + hasFunc, + SimpleWorld, + TestEvent(..), + ExecutionInfo(..), + ExecCommand(..), + FrameInfo, + TestResult) where + +import Control.Monad(liftM2) +import Control.Monad.State(MonadState(..),State(..),StateT(..),evalState) +import Control.Monad.Error(ErrorT(..)) +import Data.List(find,intersperse) +import Data.Maybe(isJust) +import Language.Lsl.Internal.Breakpoint(Breakpoint,BreakpointManager,checkBreakpoint,emptyBreakpointManager, + replaceBreakpoints,setStepBreakpoint,setStepOverBreakpoint,setStepOutBreakpoint, + breakpointFile,breakpointLine) +import Language.Lsl.Internal.CodeHelper(renderCall) +import Language.Lsl.Internal.FuncSigs(funcSigs) +import Language.Lsl.Internal.InternalLLFuncs(internalLLFuncs) +import Language.Lsl.Syntax hiding (State) +import qualified Language.Lsl.Syntax as L +import Language.Lsl.Internal.Type(LSLValue,lslValString,lslShowVal,defaultValue) +import Language.Lsl.Internal.Evaluation(EvalResult(..)) +import Language.Lsl.Internal.Exec( + EvalState,ExecutionInfo(..),FrameInfo,ScriptImage(..),evalSimple,runEval,scriptImage,setupSimple,initStateSimple,frameInfo) +import Language.Lsl.Internal.TestResult(TestResult(..)) +import Language.Lsl.UnitTest(EntryPoint(..),LSLUnitTest(..),ExpectationMode(..),FuncCallExpectations(..),expectedReturns,removeExpectation) +import Language.Lsl.Internal.Util(findM,ctx) + +--trace1 v = trace ("->>" ++ (show v)) v + +data SimpleWorld = SimpleWorld { + maxTick :: Int, + tick :: Int, + msgLog :: [(Int,String)], + wScripts :: [(String,Validity CompiledLSLScript)], + wLibrary :: [(String,Validity LModule)], + expectations :: FuncCallExpectations, + breakpointManager :: BreakpointManager + } + +type SimpleWorldM = ErrorT String (State SimpleWorld) +simpleWorldM = ErrorT . State +getTick :: SimpleWorldM Int +getTick = get >>= return . tick +getMaxTick :: SimpleWorldM Int +getMaxTick = get >>= return . maxTick +getMsgLog :: SimpleWorldM [(Int,String)] +getMsgLog = get >>= return . msgLog +getWScripts :: SimpleWorldM [(String, Validity CompiledLSLScript)] +getWScripts = get >>= return . wScripts +getWLibrary :: SimpleWorldM [(String, Validity LModule)] +getWLibrary = get >>= return . wLibrary +getExpectations :: SimpleWorldM FuncCallExpectations +getExpectations = get >>= return . expectations +getBreakpointManager :: SimpleWorldM BreakpointManager +getBreakpointManager = get >>= return . breakpointManager + +setTick t = do w <- get; put (w { tick = t }) +setMsgLog l = do w <- get; put (w { msgLog = l }) +setExpectations e = do w <- get; put (w { expectations = e }) +setBreakpointManager bpm = do w <- get; put (w { breakpointManager = bpm }) +modifyMsgLog f = do w <- get; put (w { msgLog = f (msgLog w) }) + +checkBp bp sm = + do bpm <- getBreakpointManager + let (result,bpm',sm') = checkBreakpoint bp bpm sm + setBreakpointManager bpm' + return (result,sm') + +logMsg s = do + tick <- getTick + modifyMsgLog ((tick,s):) + +doPredef n i a = + do logMsg $ "call: " ++ renderCall n a + case lookup n internalLLFuncs of + Just f -> do + result@(m, v) <- f i a + logMsg ("return: " ++ lslShowVal v) + return result + Nothing -> do + fce <- getExpectations + let allowed = Nice == expectationMode fce + case expectedReturns n a fce of + Nothing -> handleUnexpected allowed + Just (m, v) -> do + logMsg ("return: " ++ lslShowVal v) + setExpectations $ removeExpectation m fce + return (EvalIncomplete,v) + where handleUnexpected allowed = + if allowed then + do (_,rt,_) <- ctx ("finding predef " ++ n) $ + findM (\ (n',_,_) -> n' == n) funcSigs + return $ (EvalIncomplete, defaultValue rt) + else fail ("unexpected call: " ++ renderCall n a) + +mkScript (LModule globdefs vars) = + LSLScript (varsToGlobdefs ++ globdefs) [L.State (nullCtx "default") []] + where varsToGlobdefs = map (\ v -> GV v Nothing) vars + +getValidScript name = + do scripts <- getWScripts + case lookup name scripts of + Nothing -> return (Left $ "No such script: " ++ name) + Just (Left s) -> return $ Left $ "Invalid script: " ++ name + Just (Right script) -> return $ Right script + +convertEntryPoint (ScriptFunc scriptName funcName) = + do script <- getValidScript scriptName + return $ liftM2 (,) script (Right [funcName]) +convertEntryPoint (ScriptHandler scriptName stateName handlerName) = + do script <- getValidScript scriptName + return $ liftM2 (,) script (Right [stateName,handlerName]) +convertEntryPoint (ModuleFunc moduleName funcName) = + do lib <- getWLibrary + case lookup moduleName lib of + Nothing -> return (Left $ "No such module: " ++ moduleName) + Just (Left s) -> return (Left $ "Invalid module: " ++ moduleName) + Just (Right lmodule) -> + case validLSLScript lib (mkScript lmodule) of + Left _ -> return $ Left "Invalid entry point (internal error?)" + Right script -> return $ Right (script,[funcName]) + +checkResults (ms1, val, globs, w) unitTest = + let name = unitTestName unitTest + ms0 = expectedNewState unitTest + expectedR = expectedReturn unitTest in + if ((expectationMode $ expectations w) `elem` [Strict,Exhaust]) && + (not (null (callList $ expectations w))) then + FailureResult name ("some expected function calls not made: " ++ + concat (intersperse ", " $ map (fst.fst) $ callList $ expectations w)) + (msgLog w) + else case (ms0, ms1) of + (Nothing, Just st) -> + FailureResult name ("expected no state change, but changed to " ++ st) (msgLog w) + (Just st, Nothing) -> + FailureResult name ("expected state change to " ++ st ++ ", but no change occurred") (msgLog w) + (ms0, ms1) | ms0 /= ms1 -> let (Just s0, Just s1) = (ms0,ms1) in + FailureResult name ("expected state change to " ++ s0 ++ + ", but acutally changed to " ++ s1) (msgLog w) + | otherwise -> + if expectedR /= Nothing && expectedR /= Just val then + let (Just val') = expectedR in + FailureResult name ("expected return value was " ++ (lslValString val') ++ + ", but actually was " ++ (lslValString val)) (msgLog w) + else + case find (`notElem` globs) (expectedGlobalVals unitTest) of + Just (globname,val) -> + case lookup globname globs of + Nothing -> + FailureResult name ("expected global " ++ globname ++ " to have final value of " ++ + (lslValString val) ++ ", but no such global was found") + (msgLog w) + Just val' -> + FailureResult name ("expected global " ++ globname ++ " to have final value of " ++ + (lslValString val) ++ ", but actually had value of " ++ + (lslValString val')) (msgLog w) + Nothing -> SuccessResult name (msgLog w) + +-------------------------------------------------- +-- 'Interactive' testing + +data TestEvent = TestComplete TestResult | TestSuspended ExecutionInfo | AllComplete + +data ExecCommand = ExecContinue [Breakpoint] | ExecStep [Breakpoint] | ExecStepOver [Breakpoint] | + ExecStepOut [Breakpoint] + +breakpointsFromCommand (ExecContinue bps) = bps +breakpointsFromCommand (ExecStep bps) = bps +breakpointsFromCommand (ExecStepOver bps) = bps +breakpointsFromCommand (ExecStepOut bps) = bps + +hasFunc :: [(String,Validity LModule)] -> (String,String) -> Either String Bool +hasFunc lib (moduleName,functionName) = + case converted of + Left s -> Left ("no such module: " ++ moduleName) + Right (Left s) -> Left ("no such module: " ++ moduleName) + Right (Right (script,path)) -> Right $ isJust (findFunc functionName $ scriptFuncs script) + where converted = evalState (runErrorT (convertEntryPoint ep)) world + ep = ModuleFunc moduleName functionName + world = SimpleWorld { maxTick = 10000, tick = 0, msgLog = [], wScripts = [], wLibrary = lib, + expectations = FuncCallExpectations Nice [], breakpointManager = emptyBreakpointManager } + +simFunc :: [(String,Validity LModule)] -> (String,String) -> [(String,LSLValue)] -> [LSLValue] -> Either String (LSLValue,[(String,LSLValue)]) +simFunc lib (moduleName,functionName) globs args = + let world = SimpleWorld { maxTick = 10000, tick = 0, msgLog = [], wScripts = [], wLibrary = lib, + expectations = FuncCallExpectations Nice [], breakpointManager = emptyBreakpointManager } + ep = ModuleFunc moduleName functionName + init = runState (runErrorT ( + do converted <- convertEntryPoint ep + case converted of + Left s -> fail s + Right (script,path) -> + do result <- runEval (setupSimple path globs args) exec + case result of + (Left s, _) -> fail s + (Right (), exec') -> return exec' + where exec = initStateSimple script doPredef logMsg getTick setTick checkBp)) world + in case init of + (Left s, world') -> Left s + (Right exec,world') -> + case (runState $ runErrorT $ (runStateT $ runErrorT $ evalSimple 1000) exec) world of + (Left s,_) -> Left s + (Right r, _) -> + case r of + (Left s,_) -> Left s + (Right (EvalComplete newState, Just val), exec') -> Right (val,glob $ scriptImage exec') + (Right (EvalComplete newState, _),_) -> Left "execution error" + (Right (EvalIncomplete,_),_) -> Left "execution error: timeout" + (Right _,_) -> Left "execution error" + +simSome exec world = runState (runErrorT ( + do maxTick <- getMaxTick + (runStateT $ runErrorT $ evalSimple maxTick) exec)) world + +-- no more tests, not currently executing +simStep _ ([], Nothing) _ = (AllComplete,([],Nothing)) +-- not currently executing, more tests +simStep codebase (unitTest:tests, Nothing) command = + let world = (SimpleWorld { maxTick = 10000, tick = 0, msgLog = [], + wScripts = (codebaseScripts codebase), wLibrary = libFromAugLib (codebaseLib codebase), + expectations = (expectedCalls unitTest), + breakpointManager = emptyBreakpointManager}) + ep = entryPoint unitTest + globs = initialGlobs unitTest + args = arguments unitTest + name = unitTestName unitTest + init = runState (runErrorT ( + do converted <- convertEntryPoint ep + case converted of + Left s -> fail s + Right (script,path) -> + do result <- runEval (setupSimple path globs args) exec + case result of + (Left s, _) -> fail s + (Right (),exec') -> return exec' + where exec = initStateSimple script doPredef logMsg getTick setTick checkBp)) world + in case init of + (Left s,world') -> (TestComplete $ ErrorResult name s [],(tests, Nothing)) + (Right exec,world') -> simStep codebase (unitTest:tests, Just (world',exec)) command +-- currently executing +simStep _ (unitTest:tests, Just (world,exec)) command = + let name = unitTestName unitTest + breakpoints = breakpointsFromCommand command + world' = world { breakpointManager = replaceBreakpoints breakpoints (breakpointManager world) } + updateStepManager f ex = let img = scriptImage ex + stepMgr = stepManager img in ex { scriptImage = img { stepManager = f stepMgr } } + execNew = case command of + ExecStep _ -> updateStepManager setStepBreakpoint exec + ExecStepOver _ -> updateStepManager setStepOverBreakpoint exec + ExecStepOut _ -> updateStepManager setStepOutBreakpoint exec + _ -> exec + in + case simSome execNew world' of + (Left s,world'') -> (TestComplete $ ErrorResult name s (msgLog world''),(tests,Nothing)) + (Right res,world'') -> + case res of + (Left s,_) -> (TestComplete $ ErrorResult name s (msgLog world''),(tests,Nothing)) + (Right (EvalComplete newState,Just val), exec') -> (TestComplete checkedResult, (tests,Nothing)) + where checkedResult = checkResults (newState, val, glob $ scriptImage exec', world'') unitTest + (Right (EvalIncomplete,_),_) -> (TestComplete $ Timeout name (msgLog world''),(tests,Nothing)) + (Right (BrokeAt bp,_),exec') -> + (TestSuspended (ExecutionInfo file line frames),(unitTest:tests,Just (world'',exec'))) + where file = breakpointFile bp + line = breakpointLine bp + frames = frameInfo (scriptImage exec')
src/Language/Lsl/Parse.hs view
@@ -9,17 +9,79 @@ parseModuleFromStringAQ ) where +import qualified Data.ByteString as B +import qualified Data.ByteString.UTF8 as UTF8 import Data.Char(digitToInt) import Data.List(intersperse) -import Language.Lsl.Syntax(Expr(..),Statement(..),Func(..),FuncDec(..),Handler(..),State(..),Ctx(..),SourceContext(..),LSLType(..), +import Language.Lsl.Internal.Pragmas(Pragma(..)) +import Language.Lsl.Syntax(Expr(..),Statement(..),Func(..),FuncDec(..),Handler(Handler),State(..),Ctx(..),TextLocation(..),SourceContext(..),LSLType(..), Component(..),Var(..),LModule(..),LSLScript(..),GlobDef(..),goodHandlers) import Text.ParserCombinators.Parsec hiding (State) -import qualified Text.ParserCombinators.Parsec.Token as P -import Text.ParserCombinators.Parsec.Language( javaStyle ) +import qualified Text.ParserCombinators.ParsecExtras.Token as P +import Text.ParserCombinators.ParsecExtras.Language( javaStyle, emptyDef ) import Text.ParserCombinators.Parsec.Error import Control.Monad.Error(liftIO) import Control.Monad.Trans(MonadIO) +import Debug.Trace + +data ParseState = ParseState { + atStart :: !Bool, + parseAntiQuotations :: !Bool, + pendingPragmas :: ![Pragma], + trailingWS :: !String, + leadingWS :: !String + } + +getAQState = getState >>= return . parseAntiQuotations + +newAQState = ParseState { atStart = True, parseAntiQuotations = True, pendingPragmas = [], trailingWS = "", leadingWS = "" } +newNoAQState = ParseState { atStart = True, parseAntiQuotations = False, pendingPragmas = [], trailingWS = "", leadingWS = "" } + +data WS = WSSimple { wsText :: String } | WSSingle { wsText :: String } | WSMulti { wsText :: String } + deriving Show +custWS simpleSpace oneLineComment multiLineComment = do + st <- getState + ws <- many ((simpleSpace >>= return . WSSimple) <|> + (oneLineComment >>= return . WSSingle) <|> + (multiLineComment >>= return . WSMulti) <?> "") + let (trailing,leading) = if atStart st then ([],ws) else extractTrailing ws + let pragmas = foldl extractPragma [] leading + setState st { atStart = False, + pendingPragmas = pragmas, + trailingWS = wsCat trailing, + leadingWS = wsCat leading } + where wsCat = concatMap wsText + extractTrailing [] = ([],[]) + extractTrailing (WSSimple txt:rest) = + case break (=='\n') txt of + (ttxt,[]) -> let (t,rest') = extractTrailing rest in (WSSimple ttxt:t,rest') + ([],mtxt) -> ([],WSSimple mtxt:rest) + (ttxt,mtxt) -> ([WSSimple ttxt],WSSimple mtxt:rest) + extractTrailing (ws@(WSSingle txt):rest) = ([ws],rest) + extractTrailing (ws@(WSMulti txt):rest) | '\n' `elem` txt = ([ws],rest) + | otherwise = let (t,rest') = extractTrailing rest in (ws:t,rest') + extractPragma ps (WSSingle txt) = maybe ps (:ps) (parsePragma txt) + extractPragma ps _ = ps + +parsePragma :: String -> Maybe Pragma +parsePragma txt = + case parse parser "" txt of + Left _ -> Nothing + Right p -> Just p + where + pragmaStyle = emptyDef { P.reservedNames = ["pragma","inline","noinlining"], P.commentLine = "--" } + lexer :: P.TokenParser () + lexer = P.makeTokenParser pragmaStyle + reserved = P.reserved lexer + ws = P.whiteSpace lexer + parser = do + ws + reserved "pragma" + pragma <- (reserved "inline" >> return PragmaInline) <|> (reserved "noinlining" >> return PragmaNoInline) + eof + return pragma + -- define basic rules for lexical analysis lslStyle = javaStyle { P.reservedOpNames= ["*","/","+","++","-","--","^","&","&&", @@ -30,8 +92,9 @@ P.caseSensitive = True, P.identStart = letter <|> char '_', P.opLetter = oneOf "*/+:!#$%&*+./=?@\\^|-~", - P.opStart = oneOf ":!#$%&*+./<=>?@\\^|-~" } -lexer :: P.TokenParser Bool + P.opStart = oneOf ":!#$%&*+./<=>?@\\^|-~", + P.custWhiteSpace = Just custWS } +lexer :: P.TokenParser ParseState lexer = P.makeTokenParser lslStyle identLetter = P.identLetter lslStyle @@ -106,9 +169,9 @@ do wholeDigits <- many1 digit <?> "number" let w = foldl (\ b d -> b * 10 + d) 0 $ map digitToInt wholeDigits mf <- option Nothing (char '.' >> option (fromIntegral w) (fracPart False (fromIntegral w)) >>= return . Just) - return $ case mf of - Nothing -> Left w - Just f -> Right f + case mf of + Nothing -> try ( expon >>= \ p -> return $ Right (fromIntegral w * p) ) <|> (return $ Left w) + Just f -> return $ Right f stringLiteral = lexeme ( do{ str <- between (char '"') @@ -308,17 +371,18 @@ return $ Cast t e ctxify f = do + pragmas <- getState >>= return . pendingPragmas pos0 <- getPosition v <- f pos1 <- getPosition - return $ Ctx (pos2Loc (pos0,pos1)) v + return $ Ctx (pos2Ctx (pos0,pos1) pragmas) v notExpr = ctxify ((char '!' <?> "prefix operator") >> whiteSpace >> expr2 >>= return.Not) invExpr = ctxify ((char '~' <?> "prefix operator") >> whiteSpace >> expr2 >>= return.Inv) negExpr = ctxify ((char '-' <?> "prefix operator") >> whiteSpace >> expr2 >>= return.Neg) atomicExpr = do - aq <- getState + aq <- getAQState (ctxify $ try ( do m <- option 1 (reservedOp "-" >> return (-1)) n <- naturalOrFloat @@ -350,18 +414,18 @@ unaryExpr = choice [try prefixExpr,notExpr,invExpr, negExpr,try castExpr,atomicExpr] -expr2 :: GenParser Char Bool (Ctx Expr) +expr2 :: GenParser Char ParseState (Ctx Expr) expr2 = choice [try assignment, try postfixExpr, unaryExpr] expr1 = orExpr -expr :: GenParser Char Bool (Ctx Expr) +expr :: GenParser Char ParseState (Ctx Expr) expr = do r <- choice [try assignment, expr1] mtrace "expr: " r exprParser :: String -> Either ParseError (Ctx Expr) -exprParser text = runParser expr False "" text +exprParser text = runParser (whiteSpace>>expr) newNoAQState "" text ------------------------------------------------------------------------------ -- STATEMENT PARSING @@ -451,9 +515,10 @@ stmt <- statement reserved "while" e <- parens expr + semi return $ DoWhile stmt e -parseType text = runParser typeName False "" text +parseType text = runParser typeName newNoAQState "" text ------------------------------------------------------------ -- HANDLER Parsing @@ -498,16 +563,21 @@ stateDecls = many stateDecl -------------------------------------------------------------- -varOrFunc = do (Ctx ctx (t,id)) <- ctxify $ do +varOrFunc = do pos0 <- getPosition + pragmas <- getState >>= return . pendingPragmas + (Ctx ctx (t,id)) <- ctxify $ do t <- try typeName id <- ctxify identifier <?> "identifier" return (t,id) - choice [func t id, gvar ctx t id] - <|> do id <- ctxify identifier <?> "identifier" - func LLVoid id -func t id = do ps <- parens params - stmts <- braces statements - return $ GF $ Func (FuncDec id t ps) stmts + choice [func t id pragmas pos0, gvar ctx t id] + <|> do pos0 <- getPosition + pragmas <- getState >>= return . pendingPragmas + id <- ctxify identifier <?> "identifier" + func LLVoid id pragmas pos0 +func t id pragmas pos0 = do ps <- parens params + stmts <- braces statements + pos1 <- getPosition + return $ GF $ Ctx (pos2Ctx (pos0, pos1) pragmas) $ Func (FuncDec id t ps) stmts gvar ctx t (Ctx _ id) = do mexpr <- option Nothing (reservedOp' "=" >> expr >>= return . Just) semi return $ GV (Ctx ctx (Var id t)) mexpr @@ -573,7 +643,7 @@ return $ LModule globs freevars parseFromString parser string = - case runParser parser False "" string of + case runParser parser newNoAQState "" string of Left err -> Left (snd (fromParseError err)) Right x -> Right x @@ -586,55 +656,49 @@ -- | Parse an LSL script, with possible antiquotations, into its syntax tree. parseScriptFromStringAQ :: String -> Either ParseError LSLScript -parseScriptFromStringAQ s = runParser lslParser True "" s +parseScriptFromStringAQ s = runParser lslParser newAQState "" s -- | Parse an LSL (Plus) module, with possible antiquotations, into its syntax tree. parseModuleFromStringAQ :: String -> Either ParseError LModule -parseModuleFromStringAQ s = runParser moduleParser True "" s +parseModuleFromStringAQ s = runParser moduleParser newAQState "" s -parseModule :: (MonadIO m) => SourceName -> m (Either (SourceContext,String) LModule) +parseModule :: (MonadIO m) => SourceName -> m (Either (Maybe SourceContext,String) LModule) parseModule file = parseFile moduleParser file parseScript file = parseFile lslParser file -fromParseError :: ParseError -> (SourceContext,String) +fromParseError :: ParseError -> (Maybe SourceContext,String) fromParseError err = let pos = errorPos err msg = showErrorMessages "or" "unknown parse error" "expecting" "unexpected" "end of input" (errorMessages err) - in (TextLocation { textLine0 = sourceLine pos, textColumn0 = sourceColumn pos, - textLine1 = sourceLine pos, textColumn1 = sourceColumn pos, - textName = sourceName pos }, + in (Just $ SourceContext TextLocation { textLine0 = sourceLine pos, textColumn0 = sourceColumn pos, + textLine1 = sourceLine pos, textColumn1 = sourceColumn pos, + textName = sourceName pos } + "" "" [], msg) parseFile p file = - do s <- liftIO $ readFile file + do s <- (liftIO $ B.readFile file) >>= return . UTF8.toString case parser s of Left err -> return $ Left (fromParseError err) Right x -> return $ Right x - where parser = runParser p False file - -txtLoc pos len = - TextLocation { textName = sourceName pos, - textColumn0 = sourceColumn pos, - textLine0 = sourceLine pos, - textColumn1 = sourceColumn pos + (len-1), - textLine1 = sourceLine pos } + where parser = runParser p newNoAQState file -idLoc pos id = txtLoc pos $ length id - pos2Loc (pos0,pos1) = - TextLocation { + SourceContext TextLocation { textName = sourceName pos0, textColumn0 = sourceColumn pos0, textLine0 = sourceLine pos0, textColumn1 = sourceColumn pos1, textLine1 = sourceLine pos1 - } + } "" "" [] -combineContexts (UnknownSourceContext,pos0,pos1,UnknownSourceContext) = pos2Loc (pos0,pos1) -combineContexts (TextLocation l0 c0 l1 c1 n,_,_,TextLocation l0' c0' l1' c1' n') = - TextLocation l0 c0 l1' c1' n -combineContexts (TextLocation l0 c0 l1 c1 n,_,pos,_) = - TextLocation l0 c0 (sourceLine pos) (sourceColumn pos) n -combineContexts (_,pos,_,TextLocation l0 c0 l1 c1 n) = - TextLocation (sourceLine pos) (sourceColumn pos) l1 c1 n +pos2Ctx (pos0,pos1) pragmas = Just (pos2Loc (pos0,pos1)) { srcPragmas = pragmas } + +combineContexts (Nothing,pos0,pos1,Nothing) = Just $ pos2Loc (pos0,pos1) +combineContexts (Just (SourceContext (TextLocation l0 c0 l1 c1 n) pre _ prag),_,_,Just (SourceContext (TextLocation l0' c0' l1' c1' n') _ post _ )) = + Just $ SourceContext (TextLocation l0 c0 l1' c1' n) pre post prag +combineContexts (Just (SourceContext (TextLocation l0 c0 l1 c1 n) pre post prag),_,pos,_) = + Just $ SourceContext (TextLocation l0 c0 (sourceLine pos) (sourceColumn pos) n) pre post prag +combineContexts (_,pos,_,Just (SourceContext (TextLocation l0 c0 l1 c1 n) pre post prag)) = + Just $ SourceContext (TextLocation (sourceLine pos) (sourceColumn pos) l1 c1 n) pre post prag
src/Language/Lsl/Render.hs view
@@ -65,8 +65,9 @@ let render' [] = blank render' (v:vars) = renderChar ',' . renderVar v . render' vars in render' vars -renderFuncs = renderSequence renderFunc +renderFuncs = renderSequence renderCtxFunc +renderCtxFunc (Ctx _ func) = renderFunc func renderFunc (Func dec stmts) = renderFuncDec dec . renderString "{\n" . renderStatements 0 stmts . renderString "}\n" @@ -103,7 +104,7 @@ case val of Nothing -> renderString ";\n" Just expr -> renderString " = " . renderCtxExpr expr . renderString ";\n" -renderStatement' n (NullStmt) = blank +renderStatement' n (NullStmt) = blank . renderString "\n" renderStatement' n (Return Nothing) = renderString "return;\n" renderStatement' n (Return (Just expr)) = renderString "return " . renderCtxExpr expr . renderString ";\n"; renderStatement' n (StateChange name) = renderString "state " . renderString name . renderString ";\n"; @@ -122,7 +123,13 @@ renderExpression (IntLit i) = shows i renderExpression (FloatLit f) = shows f -renderExpression (StringLit s) = shows s +renderExpression (StringLit s) = renderString ('"':go s) + where go [] = "\"" + go ('\\':s) = '\\':'\\':go s + go ('\t':s) = '\\':'t':go s + go ('\n':s) = '\\':'n':go s + go ('"':s) = '\\':'"':go s + go (c:s) = c:go s renderExpression (KeyLit k) = shows k renderExpression (VecExpr x y z) = renderChar '<' . renderCtxExpr x . renderChar ',' .
src/Language/Lsl/Sim.hs view
@@ -75,7 +75,7 @@ import Text.Printf(PrintfType(..),printf) -- execute a predefined ('ll') function -doPredef :: Monad m => String -> ScriptInfo -> [LSLValue] -> WorldM m (EvalResult,LSLValue) +doPredef :: Monad m => String -> ScriptInfo Float -> [LSLValue Float] -> WorldM m (EvalResult,LSLValue Float) doPredef name info@(ScriptInfo oid pid sid pkey event) args = do predefs <- getPredefFuncs -- find the correct function to execute @@ -2566,7 +2566,7 @@ unimplementedFuncs = S.toList (S.difference (S.fromList allFuncs) (S.fromList implementedFuncs)) --------------------------------------------------------------------------------------------------- -logFromScript :: Monad m => ScriptInfo -> String -> WorldM m () +logFromScript :: Monad m => ScriptInfo Float -> String -> WorldM m () logFromScript scriptInfo msg = logAMessage LogInfo (infoToLogSource scriptInfo) msg infoToLogSource info = (scriptInfoPrimKey info ++ ": " ++ scriptInfoScriptName info) @@ -3496,7 +3496,7 @@ data SimStatus = SimEnded { simStatusMessage :: String, simStatusLog :: [LogMessage], simStatusState :: SimStateInfo } | SimInfo { simStatusEvents :: [SimEvent], simStatusLog :: [LogMessage], simStatusState :: SimStateInfo } | SimSuspended { simStatusEvents :: [SimEvent], - simStatusSuspendInfo :: ExecutionInfo, + simStatusSuspendInfo :: ExecutionInfo Float, simStatusLog :: [LogMessage], simStatusState :: SimStateInfo } deriving (Show) @@ -3521,7 +3521,7 @@ simInputEventName :: String, simInputEventDescription :: String, simInputEventParameters :: [SimParam], - simInputEventHandler :: String -> [LSLValue] -> WorldM m () } + simInputEventHandler :: String -> [LSLValue Float] -> WorldM m () } data SimParam = SimParam { simParamName :: String, simParamDescription :: String, simParamType :: SimParamType }
src/Language/Lsl/Syntax.hs view
@@ -17,6 +17,7 @@ LSLScript(..), Validity, Global(..), + TextLocation(..), SourceContext(..), Ctx(..), CompiledLSLScript(..), @@ -35,8 +36,6 @@ nullCtx, ctxVr2Vr, findFunc, - --validLSLScript, - --validLibrary, moduleFromScript, findState, predefFuncs, @@ -44,17 +43,20 @@ goodHandlers, libFromAugLib, isTextLocation, + isCastValid, compileLSLScript, compileLSLScript', compileLibrary, VState, - emptyValidationState) where + emptyValidationState, + rewriteCtxExpr) 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.Data(Data,Typeable) import Data.List(find,sort,sortBy,nub,foldl') @@ -69,15 +71,16 @@ import Debug.Trace --trace1 s v = trace (s ++ show v) v -data SourceContext = TextLocation { textLine0 :: Int, textColumn0 :: Int, textLine1 :: Int, textColumn1 :: Int, textName :: String } | - UnknownSourceContext +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 (TextLocation _ _ _ _ _) = True +isTextLocation (Just (TextLocation _ _ _ _ _)) = True isTextLocation _ = False -- | A wrapper that can associate a source code context with a value (e.g. a syntax value). -data Ctx a = Ctx { srcCtx :: SourceContext, ctxItem :: a } deriving (Show,Typeable,Data) +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) @@ -87,7 +90,7 @@ fromMCtx = fmap ctxItem nullCtx :: a -> Ctx a -nullCtx = Ctx UnknownSourceContext +nullCtx = Ctx Nothing funcNames = map (ctxItem.funcName) @@ -107,6 +110,9 @@ 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) @@ -118,7 +124,7 @@ type CtxExpr = Ctx Expr -- | An LSL expression. data Expr = IntLit Int - | FloatLit Float + | FloatLit Double | StringLit String | ListExpr [CtxExpr] | VecExpr CtxExpr CtxExpr CtxExpr @@ -188,10 +194,10 @@ deriving (Show,Typeable,Data) -- | A global definition (a function, a variable, or a module import statement). -data GlobDef = GV CtxVar (Maybe CtxExpr) | GF Func | GI CtxName [(String,String)] String +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 CtxName [CtxVar] [CtxStmt] +data Handler = Handler { handlerName :: CtxName, handlerParams :: [CtxVar], handlerStatements :: [CtxStmt] } deriving (Show,Typeable,Data) -- | The set of valid handlers supported by LSL. @@ -205,7 +211,7 @@ -- | An LSL script. data LSLScript = LSLScript [GlobDef] [State] deriving (Show,Typeable,Data) -type ModuleInfo = ([Global],[Func]) +type ModuleInfo = ([Global],[Ctx Func]) -- | A collection of modules. type Library = [(String,Validity LModule)] -- | A collection of mouldes, augmented with additional derived information. @@ -228,12 +234,12 @@ lookupModule :: String -> Library -> Validity LModule lookupModule name lib = case lookup name lib of - Nothing -> throwError [(UnknownSourceContext, "unknown module")] - Just (Left ((_,s):_)) -> throwError [(UnknownSourceContext, "invalid library (" ++ s ++ ")")] + 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 = (SourceContext,String) +type CodeErr = (Maybe SourceContext,String) ctxFromCodeErr = fst msgFromCodeErr = snd @@ -242,44 +248,16 @@ type Validity a = Either [CodeErr] a instance Error [CodeErr] where - noMsg = [(UnknownSourceContext,"")] - strMsg s = [(UnknownSourceContext,s)] + noMsg = [(Nothing,"")] + strMsg s = [(Nothing,s)] -incontext (ctx,s) (Left ((ctx',s'):_)) = - case ctx' of - UnknownSourceContext -> Left [(ctx,msg)] - _ -> Left [(ctx',msg)] - where msg = if null s then s' else s ++ ": " ++ s' -incontext _ v = v - -incontext' (ctx,s) (Left ((_,s'):_)) = Left [(ctx,s ++ ": " ++ s')] -incontext' _ v = v - -------------------- matchTypes LLFloat LLInteger = True matchTypes dest src = dest == src || (all (`elem` [LLKey,LLString]) [dest,src]) -typeGlob library prefix (vars,funcs) (GV (Ctx ctx (Var name t)) _) = return ((Var (prefix ++ name) t):vars,funcs) -typeGlob library prefix (vars,funcs) (GF (Func (FuncDec name t params) _)) = - return (vars,(FuncDec (fmap (prefix++) name) t params):funcs) -typeGlob library prefix v@(vars,funcs) (GI moduleName _ prefix') = - do (LModule globs _) <- lookupModule (ctxItem moduleName) library - foldM (typeGlob library (prefix++prefix')) (vars,funcs) globs -typeGlobs library gs = foldM (typeGlob library "") ([],[]) gs - -noDupVars :: [String] -> [CtxVar] -> Validity [String] -noDupVars used [] = return used -noDupVars used ((Ctx ctx (Var n t)):vs) = do - when (n `elem` used) $ throwError [(ctx, n ++ " already defined")] - noDupVars (n:used) vs - -checkName :: Maybe SourceContext -> String -> [String] -> Validity () -checkName (Just ctx) name names = - when (name `elem` names) $ throwError [(ctx, name ++ " is multiply defined")] - data CompiledLSLScript = CompiledLSLScript { scriptGlobals :: ![Global], - scriptFuncs :: ![Func], + scriptFuncs :: ![Ctx Func], scriptStates :: ![State]} deriving (Show) @@ -288,16 +266,15 @@ data ValidationState = ValidationState { vsLib :: !Library, - vsGlobalRegistry :: !(M.Map String SourceContext), - vsLocalRegistry :: ![M.Map String SourceContext], + vsGlobalRegistry :: !(M.Map String (Maybe SourceContext)), + vsLocalRegistry :: ![M.Map String (Maybe SourceContext)], vsLocalVars :: ![[Var]], vsRefs :: !(M.Map RefPos SourceContext), vsLabels :: ![[String]], vsModules :: ![String], - vsGlobDefs :: ![GlobDef], vsStates :: ![State], vsGlobals :: ![Global], - vsFuncs :: ![Func], + vsFuncs :: ![Ctx Func], vsErr :: ![CodeErr], vsWarn :: ![CodeErr], vsNamesUsed :: [String], @@ -308,7 +285,7 @@ vsBranchReturns :: !Bool, vsHandlersUsed :: ![String], vsImports :: ![(String,[(String,String)],String)], - vsContext :: [SourceContext] + vsContext :: [Maybe SourceContext] } emptyValidationState = ValidationState { @@ -319,7 +296,6 @@ vsRefs = M.empty, vsLabels = [], vsModules = [], - vsGlobDefs = [], vsStates = [], vsGlobals = [], vsFuncs = [], @@ -362,10 +338,10 @@ registry <- get'vsGlobalRegistry case M.lookup name registry of Nothing -> return () - Just UnknownSourceContext -> return () - Just ctx -> do + Just Nothing -> return () + Just (Just ctx) -> do refs <- get'vsRefs - put'vsRefs (M.insert (RefPos name (textLine0 ctx) (textColumn0 ctx)) ctx refs) + put'vsRefs (M.insert (RefPos name (textLine0 $ srcTextLocation ctx) (textColumn0 $ srcTextLocation ctx)) ctx refs) vsmAddRef name ctx = do lr <- get'vsLocalRegistry @@ -374,17 +350,17 @@ go (top:rest) = do case M.lookup name top of Nothing -> go rest - Just UnknownSourceContext -> return () - Just ctx -> do + Just Nothing -> return () + Just (Just ctx) -> do refs <- get'vsRefs - put'vsRefs (M.insert (RefPos name (textLine0 ctx) (textColumn0 ctx)) ctx refs) + 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 :: Func -> VState () +vsmAddFunc :: Ctx Func -> VState () vsmAddFunc func = get'vsFuncs >>= put'vsFuncs . (func :) vsmAddGlobal :: Global -> VState () @@ -393,7 +369,7 @@ vsmAddState :: State -> VState () vsmAddState state = get'vsStates >>= put'vsStates . (state :) -vsmAddLocal :: SourceContext -> Var -> VState () +vsmAddLocal :: Maybe SourceContext -> Var -> VState () vsmAddLocal ctx v@(Var name _) = do locals <- get'vsLocalVars case locals of @@ -442,7 +418,7 @@ vsmAddHandler handlerName = get'vsHandlersUsed >>= put'vsHandlersUsed . (handlerName:) -vsmWithContext :: SourceContext -> VState a -> VState a +vsmWithContext :: Maybe SourceContext -> VState a -> VState a vsmWithContext srcCtx action = do ctxs <- get'vsContext put'vsContext (srcCtx:ctxs) @@ -485,11 +461,6 @@ safeHead [] = Nothing safeHead (x:_) = Just x -vsmFirstErr :: VState (Maybe CodeErr) -vsmFirstErr = get'vsErr >>= \ l -> case l of - l@(_:_) -> return $ Just $ last l -- list is reversed... - _ -> return Nothing - compileLSLScript' :: Library ->LSLScript -> Validity CompiledLSLScript compileLSLScript' library script = evalState (compileLSLScript script) (emptyValidationState { vsLib = library }) @@ -500,7 +471,7 @@ mapM_ vsmAddGF predefFuncs mapM_ compileGlob globs mapM_ compileState states - err <- get'vsErr -- vsmFirstErr + err <- get'vsErr case err of [] -> do globals <- get'vsGlobals @@ -523,7 +494,7 @@ preprocessGlobDef :: String -> GlobDef -> VState () preprocessGlobDef prefix (GV (Ctx ctx v@(Var name t)) _) = vsmAddGV (Var (prefix ++ name) t) -preprocessGlobDef prefix (GF (Func (FuncDec name t params) _)) = vsmAddGF (FuncDec (fmap (prefix++) name) t params) +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 @@ -544,7 +515,7 @@ vsmRegisterGlobal v vsmAddToNamesUsed (varName v') vsmAddGlobal (GDecl v' (fmap ctxItem mexpr)) -compileGlob (GF f@(Func (FuncDec name t params) statements)) = +compileGlob (GF cf@(Ctx ctx f@(Func (FuncDec name t params) statements))) = vsmWithNewScope $ do compileParams params vsmInEntryPoint t False $ do @@ -553,7 +524,7 @@ when (not returns && t /= LLVoid) (vsmAddErr (srcCtx name, ctxItem name ++ ": not all code paths return a value")) vsmRegisterFunc f vsmAddToNamesUsed $ ctxItem name - vsmAddFunc f + vsmAddFunc cf compileGlob (GI (Ctx ctx name) bindings prefix) = vsmWithModule name $ do let imp = (name, sort bindings, prefix) @@ -572,7 +543,7 @@ vsmAddImport imp vsmWithContext ctx $ mapM_ (rewriteGlob' prefix renames (map ctxItem freevars ++ vars')) globs -rewriteGlob' prefix renames vars (GF (Func (FuncDec name t params) statements)) = +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 @@ -582,7 +553,7 @@ else let rewrittenFunc = (Func (FuncDec (Ctx (srcCtx name) name') t params) $ rewriteStatements 0 renames statements) in do vsmAddToNamesUsed name' vsmRegisterFunc rewrittenFunc - vsmAddFunc 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") @@ -798,7 +769,7 @@ return $ Just LLInteger compileCtxExpr (Ctx ctx (Neg expr)) = do mt <- compileCtxExpr expr - (mt `whenIsJust` (==LLList)) $ vsmAddErr (ctx, "operator not applicable to list type") + (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 @@ -1165,77 +1136,6 @@ Just t | t `elem` [LLVoid,LLList] -> vsmAddErr (ctx,"invalid list element") | otherwise -> return () -validLSLScript :: Library -> LSLScript -> Validity CompiledLSLScript -validLSLScript library (LSLScript globs states) = - do (typedVars,typedFuncs) <- typeGlobs library globs - let vars = reverse typedVars - let funcDecs = typedFuncs ++ predefFuncs - (globvars,funcs,_,_) <- foldM (validGlob library vars funcDecs) ([],[],[],[]) globs - validStates snames [] vars funcDecs states - return (CompiledLSLScript (reverse globvars) funcs states) - where snames = let sname (State cn _) = ctxItem cn in map sname states -validGlob _ vars funcDecs (globvars,funcs,imports,namesUsed) (GV v mexpr) = - do when (isConstant $ varName v') $ throwError [(srcCtx v, varName v' ++ " is a predefined constant")] - -- find the vars that are defined prior to this global variable -- only one of these - -- vars may be used to initialize the global variable. - when (varName v' `elem` namesUsed) $ throwError [(srcCtx v, varName v' ++ " is already defined")] - let (vars',_) = break (\ var -> varName var == varName v') vars - case mexpr of - Nothing -> return (GDecl v' Nothing:globvars,funcs,imports, (varName v'):namesUsed) - Just expr -> do - t <- validCtxSimple vars' expr - let vt = varType v' - when (not (matchTypes vt t)) $ throwError [(srcCtx expr, "expression not of the correct type")] - return ((GDecl v' $ Just (ctxItem expr)):globvars,funcs,imports, (varName v'):namesUsed) - where v' = ctxItem v -validGlob _ vars funcDecs (globvars,funcs,imports,namesUsed) (GF f@(Func (FuncDec name t params) statements)) = - do noDupVars [] params - when (ctxItem name `elem` namesUsed) $ throwError [(srcCtx name, ctxItem name ++ " is already defined")] - returns <- validStatements False [] funcDecs vars t [] [[],params'] statements - when (not returns && t /= LLVoid) $ - throwError [(srcCtx name, "function " ++ (ctxItem name) ++ ": not all code paths return a value")] - return (globvars,f:funcs,imports,(ctxItem name):namesUsed) - where params' = ctxItems params -validGlob library vars funcDecs vstate@(globvars,funcs,imports,namesUsed) (GI (Ctx ctx name) bindings prefix) = - let context = incontext' (ctx,"module " ++ name) in - do let imp = (name,sort bindings,prefix) - if imp `elem` imports - then return (globvars,funcs,imports,namesUsed) - else context $ do - (LModule globs freevars) <- context $ lookupModule name library - context $ validBindings vars freevars bindings - (vars',funcDecs') <- context $ typeGlobs library globs - let renames = bindings ++ (map (\ x -> (x,prefix ++ x)) ((map varName vars') ++ (funcNames funcDecs'))) - (gvs,fs,imports',namesUsed') <- foldM (rewriteGlob prefix library renames ((map ctxItem freevars) ++ vars')) vstate globs - return (gvs,fs,imp:imports',namesUsed') - -rewriteGlob _ _ renames vars (globvars,funcs,imports,namesUsed) (GF (Func (FuncDec name t params) statements)) = - do name' <- incontext (srcCtx name, "renaming function " ++ ctxItem name ++ ", " ++ show renames) $ lookupM (ctxItem name) renames - when (name' `elem` namesUsed) $ throwStrError (name' ++ " imported from module is already defined") - let rewrittenFunc = (Func (FuncDec (Ctx (srcCtx name) name') t params) $ rewriteStatements 0 renames statements) - return (globvars,rewrittenFunc:funcs,imports,name':namesUsed) -rewriteGlob _ _ renames vars (globvars,funcs,imports,namesUsed) (GV (Ctx ctx (Var name t)) mexpr) = - do name' <- incontext (ctx,"renaming variable " ++ name) $ lookupM name renames - when (name' `elem` namesUsed) $ throwStrError (name' ++ " imported from module is already defined") - let rewrittenGlobVar = GDecl (Var name' t) $ - case mexpr of - Nothing -> Nothing - Just expr -> Just $ (ctxItem (rewriteCtxExpr renames expr)) - return (rewrittenGlobVar:globvars,funcs,imports,name':namesUsed) -rewriteGlob prefix0 library renames vars vstate@(globvars,funcs,imports,namesUsed) (GI (Ctx ctx mName) bindings prefix) = - do (LModule globs freevars) <- incontext (ctx, "rewriting module " ++ mName) $ lookupModule mName library - incontext (ctx,"") $ validBindings vars freevars bindings - bindings' <- mapM rewriteBinding bindings - let imp = (mName,sort bindings',prefix0 ++ prefix) - if (imp `elem` imports) - then return (globvars,funcs,imports,namesUsed) - else do - (vars',funcDecs') <- typeGlobs library globs - let renames = bindings' ++ map (\ x -> (x,prefix0 ++ prefix ++ x)) (map varName vars' ++ map (ctxItem . funcName) funcDecs') - (gvs,fs,imports',namesUsed') <- foldM (rewriteGlob (prefix0 ++ prefix) library renames vars') vstate globs - return (gvs,fs,imp:imports',namesUsed') - where rewriteBinding (fv,rn) = lookupM rn renames >>= return . ((,) fv) - validBindings vars freevars bindings = if length freevars /= length bindings then throwStrError ("wrong number of bindings in import: " ++ (show $ length freevars) ++ " required") @@ -1248,457 +1148,12 @@ | otherwise -> f xys in f bindings -validState snames used vars funcs (State (Ctx ctx name) handlers) = - do when (name `elem` used) $ throwError [(ctx, name ++ " already used")] - incontext (ctx,"") $ validHandlers snames [] funcs vars handlers - return name - -validStates snames used vars funcs [] = return () -validStates snames used vars funcs (s:ss) = - do name <- validState snames used vars funcs s - validStates snames (name:used) vars funcs ss - -validCast t0 t1 = - let 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)] in - do when (t0 /= t1 && (t0,t1) `notElem` validCasts) $ throwStrError ("can't cast from " ++ (lslTypeString t0) ++ " to " ++ (lslTypeString t1)) - -validCtxSimple :: [Var] -> Ctx Expr -> Validity LSLType -validCtxSimple vars (Ctx ctx expr) = incontext (ctx,"") $ validSimple vars expr - -validSimple :: [Var] -> Expr -> Validity LSLType -validSimple vars (IntLit i) = return LLInteger -validSimple vars (FloatLit f) = return LLFloat -validSimple vars (StringLit s) = return LLString -validSimple vars (KeyLit k) = return LLKey -validSimple vars (Get (Ctx ctx name,All)) = - (do (Var _ t) <- incontext (ctx, "variable " ++ name) $ findM (\ v -> varName v == name) vars - return t) - `mplus` (findConstType name) -validSimple vars (Get (Ctx ctx name,_)) = throwError [(ctx,"can't access vector/rotation component in global variable initialization")] -validSimple vars (ListExpr []) = return LLList -validSimple vars (ListExpr (e:es)) = - do t <- validCtxSimple vars e - when (t == LLList) $ throwError [(srcCtx e,"lists cannot contain other lists")] - validSimple vars (ListExpr es) -validSimple vars (VecExpr e1 e2 e3) = validSimpleStructure vars LLVector [e1,e2,e3] -validSimple vars (RotExpr e1 e2 e3 e4) = validSimpleStructure vars LLRot [e1,e2,e3,e4] -validSimple vars (Neg e) = - do t <- validCtxSimple vars e - when (t `notElem` [LLFloat, LLInteger]) $ throwError [(srcCtx e,"operator only applicable to integers and floats in this context")] - return t -validSimple vars e = throwStrError ("expression is not valid in a static context.") - -validSimpleStructure vars t [] = return t -validSimpleStructure vars t (e:es) = - do t' <- validCtxSimple vars e - when (t' `notElem` [LLFloat,LLInteger]) $ throwError [(srcCtx e, "literal of type " ++ - (lslTypeString t') ++ " not a valid element of " ++ (lslTypeString t))] - validSimpleStructure vars t es - -validExpression :: Expr -> [FuncDec] -> [Var] -> [[Var]] -> Validity LSLType -validExpression (Cast t expr) funcs vars locals = - do t' <- validCtxExpr expr funcs vars locals - incontext (srcCtx expr, "") $ validCast t' t - return t -validExpression (Get ((Ctx ctx name),component)) funcs vars locals = - case (findType name (concat locals ++ vars) `mplus` findConstType name,component) of - (Nothing,_) -> throwError [(ctx, "undefined variable or constant: " ++ name)] - (Just LLRot,All) -> return LLRot - (Just LLRot,_) -> return LLFloat - (Just LLVector,All) -> return LLVector - (Just LLVector,S) -> throwError [(ctx,"s is not a valid component of a vector")] - (Just LLVector,_) -> return LLFloat - (Just t,All) -> return t - (Just t,_) -> throwError [(ctx,"only vectors and rotations have components")] -validExpression (Call name exprs) funcs vars locals = validCall funcs vars locals name exprs -validExpression (Not expr) funcs vars locals = - do t <- validCtxExpr expr funcs vars locals - when (t /= LLInteger) $ throwError [(srcCtx expr, "expression is not an integer expression, which is required for applying the Not operator")] - return t -validExpression (Neg expr) funcs vars locals = - do t <- validCtxExpr expr funcs vars locals - when (t == LLList) $ throwError [(srcCtx expr, "operator not applicable to list type")] - return t -validExpression (Inv expr) funcs vars locals = - do t <- validCtxExpr expr funcs vars locals - when (t /= LLInteger) $ throwError [(srcCtx expr, "expression is not an integer expression, which is required for applying the inverse operator")] - return t -validExpression plus@(Add expr1 expr2) funcs vars locals = - do (t1,t2) <- validEach (expr1,expr2) funcs vars locals - case (t1,t2) of - (LLInteger,LLInteger) -> return LLInteger - (LLInteger,LLFloat) -> return LLFloat - (LLFloat,LLInteger) -> return LLFloat - (LLFloat,LLFloat) -> return LLFloat - (LLVector,LLVector) -> return LLVector - (LLRot,LLRot) -> return LLRot - (LLString,LLString) -> return LLString - (LLList,LLList) -> return LLList - (t,LLList) -> return LLList - (LLList,t) -> return LLList - (t0,t1) -> incompatibleOperands plus t0 t1 -validExpression minus@(Sub expr1 expr2) funcs vars locals = - do (t1,t2) <- validEach (expr1,expr2) funcs vars locals - case (t1,t2) of - (LLInteger,LLInteger) -> return LLInteger - (LLInteger,LLFloat) -> return LLFloat - (LLFloat,LLInteger) -> return LLFloat - (LLFloat,LLFloat) -> return LLFloat - (LLVector,LLVector) -> return LLVector - (LLRot,LLRot) -> return LLRot - (t0,t1) -> incompatibleOperands minus t0 t1 -validExpression expr@(Mul expr1 expr2) funcs vars locals= - do (t1,t2) <- validEach (expr1,expr2) funcs vars locals - case (t1,t2) of - (LLInteger,LLInteger) -> return LLInteger - (LLInteger,LLFloat) -> return LLFloat - (LLFloat,LLInteger) -> return LLFloat - (LLFloat,LLFloat) -> return LLFloat - (LLVector,LLInteger) -> return LLVector - (LLVector,LLFloat) -> return LLVector - (LLFloat,LLVector) -> return LLVector - (LLInteger,LLVector) -> return LLVector - (LLVector,LLVector) -> return LLFloat - (LLVector,LLRot) -> return LLVector - (LLRot,LLRot) -> return LLRot - (t0,t1) -> incompatibleOperands expr t0 t1 -validExpression expr@(Div expr1 expr2) funcs vars locals = - do (t1,t2) <- validEach (expr1,expr2) funcs vars locals - case (t1,t2) of - (LLInteger,LLInteger) -> return LLInteger - (LLInteger,LLFloat) -> return LLFloat - (LLFloat,LLInteger) -> return LLFloat - (LLFloat,LLFloat) -> return LLFloat - (LLVector,LLInteger) -> return LLVector - (LLVector,LLFloat) -> return LLVector - (LLVector,LLRot) -> return LLVector - (LLRot,LLRot) -> return LLRot - (t0,t1) -> incompatibleOperands expr t0 t1 -validExpression expr@(Mod expr1 expr2) funcs vars locals = - do (t1,t2) <- validEach (expr1,expr2) funcs vars locals - case (t1,t2) of - (LLInteger,LLInteger) -> return LLInteger - (LLVector,LLVector) -> return LLVector - _ -> incompatibleOperands expr t1 t2 -validExpression e@(Equal expr1 expr2) funcs vars locals = - do (t1,t2) <- validEach (expr1,expr2) funcs vars locals - case (t1,t2) of - (LLInteger,LLFloat) -> return LLInteger - (LLFloat,LLInteger) -> return LLInteger - (LLString,LLKey) -> return LLInteger - (LLKey,LLString) -> return LLInteger - (t1,t2) | (t1 == t2) -> return LLInteger - | otherwise -> incompatibleOperands e t1 t2 -validExpression e@(NotEqual expr1 expr2) funcs vars locals = - do (t1,t2) <- validEach (expr1,expr2) funcs vars locals - case (t1,t2) of - (LLInteger,LLFloat) -> return LLInteger - (LLFloat,LLInteger) -> return LLInteger - (LLString,LLKey) -> return LLInteger - (LLKey,LLString) -> return LLInteger - (t1,t2) | (t1 == t2) -> return LLInteger - | otherwise -> incompatibleOperands e t1 t2 -validExpression e@(BAnd expr1 expr2) funcs vars locals = validBothInteger (expr1,expr2) funcs vars locals -validExpression e@(BOr expr1 expr2) funcs vars locals = validBothInteger (expr1,expr2) funcs vars locals -validExpression e@(Xor expr1 expr2) funcs vars locals = validBothInteger (expr1,expr2) funcs vars locals -validExpression e@(ShiftL expr1 expr2) funcs vars locals = validBothInteger (expr1,expr2) funcs vars locals -validExpression e@(ShiftR expr1 expr2) funcs vars locals = validBothInteger (expr1,expr2) funcs vars locals -validExpression e@(Gt expr1 expr2) funcs vars locals = validRelExpr (expr1,expr2) funcs vars locals -validExpression e@(Ge expr1 expr2) funcs vars locals = validRelExpr (expr1,expr2) funcs vars locals -validExpression e@(Le expr1 expr2) funcs vars locals = validRelExpr (expr1,expr2) funcs vars locals -validExpression e@(Lt expr1 expr2) funcs vars locals = validRelExpr (expr1, expr2) funcs vars locals -validExpression e@(And expr1 expr2) funcs vars locals = validBothInteger (expr1, expr2) funcs vars locals -validExpression e@(Or expr1 expr2) funcs vars locals = validBothInteger (expr1, expr2) funcs vars locals -validExpression e@(IncBy (name,All) expr) funcs vars locals = - do failIfNoModify name - (t1,t2) <- validNameExpr (name,expr) funcs vars locals - case (t1,t2) of - (LLInteger,LLInteger) -> return LLInteger - (LLFloat,LLInteger) -> return LLFloat - (LLFloat,LLFloat) -> return LLFloat - (LLVector,LLVector) -> return LLVector - (LLRot,LLRot) -> return LLRot - (LLString,LLString) -> return LLString - (LLList,LLList) -> return LLList - (LLList,t) -> return LLList - (t0,t1) -> incompatibleOperands e t0 t1 -validExpression e@(IncBy (name,_) expr) funcs vars locals = - do failIfNoModify name - (t1,t2) <- validNameExpr (name,expr) funcs vars locals - case (t1,t2) of - (t1,t2) | t1 `elem` [LLVector,LLRot] && t2 `elem` [LLFloat,LLInteger] -> return LLFloat - | otherwise -> incompatibleOperands e t1 t2 -validExpression e@(DecBy (name,All) expr) funcs vars locals = - do failIfNoModify name - (t1,t2) <- validNameExpr (name,expr) funcs vars locals - case (t1,t2) of - (LLInteger,LLInteger) -> return LLInteger - (LLFloat,LLInteger) -> return LLFloat - (LLFloat,LLFloat) -> return LLFloat - (LLVector,LLVector) -> return LLVector - (LLRot,LLRot) -> return LLRot - (t0,t1) -> incompatibleOperands e t0 t1 -validExpression e@(DecBy (name,_) expr) funcs vars locals = - do failIfNoModify name - (t1,t2) <- validNameExpr (name,expr) funcs vars locals - case (t1,t2) of - (t1,t2) | t1 `elem` [LLVector,LLRot] && t2 `elem` [LLFloat,LLInteger] -> return LLFloat - | otherwise -> incompatibleOperands e t1 t2 -validExpression e@(MulBy (name,All) expr) funcs vars locals = - do failIfNoModify name - (t1,t2) <- validNameExpr (name,expr) funcs vars locals - case (t1,t2) of - (LLInteger,LLInteger) -> return LLInteger - (LLFloat,LLInteger) -> return LLFloat - (LLFloat,LLFloat) -> return LLFloat - (LLVector,LLInteger) -> return LLVector - (LLVector,LLFloat) -> return LLVector - (LLVector,LLVector) -> return LLVector -- note: LSL compiles this, but it results in runtime error! - (LLVector,LLRot) -> return LLVector - (LLRot,LLRot) -> return LLRot - (t0,t1) -> incompatibleOperands e t0 t1 -validExpression e@(MulBy (name,_) expr) funcs vars locals = - do failIfNoModify name - (t1,t2) <- validNameExpr (name,expr) funcs vars locals - case (t1,t2) of - (t1,t2) | t1 `elem` [LLVector,LLRot] && t2 `elem` [LLFloat,LLInteger] -> return LLFloat - | otherwise -> incompatibleOperands e t1 t2 -validExpression e@(DivBy (name,All) expr) funcs vars locals = - do failIfNoModify name - (t1,t2) <- validNameExpr (name,expr) funcs vars locals - case (t1,t2) of - (LLInteger,LLInteger) -> return LLInteger - (LLFloat,LLInteger) -> return LLFloat - (LLFloat,LLFloat) -> return LLFloat - (LLVector,LLInteger) -> return LLVector - (LLVector,LLFloat) -> return LLVector - (LLVector,LLRot) -> return LLVector - (LLRot,LLRot) -> return LLRot - (t0,t1) -> incompatibleOperands e t0 t1 -validExpression e@(DivBy (name,_) expr) funcs vars locals = - do failIfNoModify name - (t1,t2) <- validNameExpr (name,expr) funcs vars locals - case (t1,t2) of - (t1,t2) | t1 `elem` [LLVector,LLRot] && t2 `elem` [LLFloat,LLInteger] -> return LLFloat - | otherwise -> incompatibleOperands e t1 t2 -validExpression e@(ModBy (name,All) expr) funcs vars locals = - do failIfNoModify name - (t1,t2) <- validNameExpr (name,expr) funcs vars locals - case (t1,t2) of - (LLInteger,LLInteger) -> return LLInteger - (LLVector,LLVector) -> return LLVector - (t0,t1) -> incompatibleOperands e t0 t1 -validExpression e@(ModBy (name,_) expr) funcs vars locals = - do failIfNoModify name - (t1,t2) <- validNameExpr (name,expr) funcs vars locals - case (t1,t2) of - (t1,t2) -> incompatibleOperands e t1 t2 -validExpression e@(PostInc var) funcs vars locals = validIncDecOp var vars locals "++" -validExpression e@(PostDec var) funcs vars locals = validIncDecOp var vars locals "--" -validExpression e@(PreInc var) funcs vars locals = validIncDecOp var vars locals "++" -validExpression e@(PreDec var) funcs vars locals = validIncDecOp var vars locals "++" -validExpression expr0@(Set (name,All) expr) funcs vars locals = - do failIfNoModify name - (t1,t2) <- validNameExpr (name,expr) funcs vars locals - case (t1,t2) of - (LLFloat,LLInteger) -> return LLFloat - (LLKey,LLString) -> return LLKey - (LLString,LLKey) -> return LLString - (t1,t2) | t1 == t2 -> return t1 - | otherwise -> incompatibleOperands expr0 t1 t2 -validExpression expr0@(Set (name,S) expr) funcs vars locals = - do failIfNoModify name - (t1,t2) <- validNameExpr (name,expr) funcs vars locals - case (t1,t2) of - (LLRot,LLFloat) -> return LLFloat - (LLRot,LLInteger) -> return LLFloat - (t0,t1) -> incompatibleOperands expr0 t0 t1 -validExpression expr0@(Set (name,_) expr) funcs vars locals = - do failIfNoModify name - (t1,t2) <- validNameExpr (name,expr) funcs vars locals - case (t1,t2) of - (LLVector,LLFloat) -> return LLFloat - (LLVector,LLInteger) -> return LLFloat - (LLRot,LLFloat) -> return LLFloat - (LLRot,LLInteger) -> return LLFloat - (t0,t1) -> incompatibleOperands expr0 t0 t1 -validExpression (IntLit i) _ _ _ = return LLInteger -validExpression (FloatLit _) _ _ _ = return LLFloat -validExpression (StringLit _) _ _ _ = return LLString -validExpression (KeyLit _) _ _ _ = return LLKey -validExpression (ListExpr es) fs vs ls = do - mapM (\ e -> validListExprElement e fs vs ls) es - return LLList -validExpression (VecExpr xExpr yExpr zExpr) funcs vars locals = - do xt <- validCtxExpr xExpr funcs vars locals - yt <- validCtxExpr yExpr funcs vars locals - zt <- validCtxExpr zExpr funcs vars locals - when (not (all (`elem` [LLInteger,LLFloat]) [xt,yt,zt])) $ throwStrError "invalid components for vector" - return LLVector -validExpression (RotExpr xExpr yExpr zExpr sExpr) funcs vars locals = - do xt <- validCtxExpr xExpr funcs vars locals - yt <- validCtxExpr yExpr funcs vars locals - zt <- validCtxExpr zExpr funcs vars locals - st <- validCtxExpr sExpr funcs vars locals - when (not (all (`elem` [LLInteger,LLFloat]) [xt,yt,zt,st])) $ throwStrError "invalid components for rotation" - return LLRot ---validExpression x funcs vars locals = error ("what to do with " ++ (show x)) - -validListExprElement (Ctx ctx e) funcs vars locals = do - t <- validExpression e funcs vars locals - when (t `elem` [LLVoid,LLList]) $ throwError [(ctx,"invalid type for list element")] - return () - -validMExpression Nothing funcs vars locals = return LLVoid -validMExpression (Just expr) funcs vars locals = validCtxExpr expr funcs vars locals - -validExpressions es funcs vars locals = mapM_ (\ e -> validCtxExpr e funcs vars locals) es - -validRelExpr (expr1,expr2) funcs vars locals = - do (t1,t2) <- validEach (expr1,expr2) funcs vars locals - case (t1,t2) of - (LLInteger,LLInteger) -> return LLInteger - (LLInteger,LLFloat) -> return LLInteger - (LLFloat, LLInteger) -> return LLInteger - (LLFloat, LLFloat) -> return LLInteger - (t0,t1) -> throwStrError ("operands are of incompatible types") -validBothInteger (expr1, expr2) funcs vars locals = - do (t1,t2) <- validEach (expr1,expr2) funcs vars locals - when (t1 /= LLInteger || t2 /= LLInteger) $ throwStrError ("operands are of incompatible types") - return LLInteger -validEach (expr1, expr2) funcs vars locals = - do t1 <- validCtxExpr expr1 funcs vars locals - t2 <- validCtxExpr expr2 funcs vars locals - return (t1,t2) - -validNameExpr (Ctx ctx name, expr) funcs vars locals = - case (findType name (concat locals ++ vars), - validCtxExpr expr funcs vars locals) of - (Just t1, Right t2) -> return (t1,t2) - (Nothing, _) -> throwError [(ctx, "variable " ++ name ++ " not defined")] - (_,Left s) -> throwError s -validCall funcs vars locals (Ctx ctx fname) exprs = - do (FuncDec _ t params) <- findFuncDec fname funcs - let vArg _ [] [] = return () - vArg _ (p:ps) [] = throwError [(ctx, "mismatch of arguments vs. formal paramters in call to function " ++ fname)] - vArg _ [] (a:as) = throwError [(ctx, "mismatch of arguments vs. formal paramters in call to function " ++ fname)] - vArg n (Var name t:ts) (arg:args) = - do t' <- validCtxExpr arg funcs vars locals - when (not (matchTypes t t')) $ throwError [(ctx, "argument " ++ (show n) ++ " in call to function (" ++ fname ++ ") is of wrong type:" ++ (lslTypeString t') ++ ", should be " ++ (lslTypeString t))] - vArg (n+1) ts args - vArg 1 (ctxItems params) exprs - return t - -validCtxExpr (Ctx ctx e) fs vs ls = incontext (ctx,"") $ validExpression e fs vs ls - -validIncDecOp (n@(Ctx ctx name),c) vars locals op = - do failIfNoModify n - case (findType name (concat locals ++ vars),c) of - (Nothing,_) -> throwError [(ctx, "variable " ++ name ++ " not found")] - (Just LLInteger,All) -> return LLInteger - (Just LLFloat,All) -> return LLFloat - (Just LLRot,S) -> return LLFloat - (Just LLVector,S) -> throwError [(ctx, "s is not a valid component of " ++ name)] - (Just t,All) -> throwError [(ctx, name ++ " is not a valid operand for " ++ op)] - (Just LLVector,_) -> return LLFloat - (Just LLRot,_) -> return LLFloat - _ -> throwError [(ctx, name ++ " is not a valid operand for " ++ op)] - -failIfNoModify (Ctx ctx name) = - when (isConstant name) $ throwError [(ctx,"cannot modify " ++ name ++ " because it is a constant")] - -incompatibleOperands expr t0 t1 = - throwStrError ("types of the operands aren't compatible (" ++ - (lslTypeString t0) ++ " vs. " ++ (lslTypeString t1) ++ ")") - defined :: String -> [Var] -> Bool defined n = any (\ (Var n' _) -> n == n') -validStatement _ _ funcs vars rtype labels locals@(scope:scopes) returns (Decl var@(Var name t) expr) = - do when (defined name $ concat locals) $ throwStrError ("variable " ++ name ++ " already defined") -- can't hide another local, even in a surrounding scope - when (isConstant name) $ throwStrError ("variable " ++ name ++ " is a predefined constant") - case expr of - Nothing -> return ((var:scope):scopes,returns) - Just expr' -> do t' <- validCtxExpr expr' funcs vars locals - when (not $ matchTypes t t') $ throwError [(srcCtx expr', "type of expression in declaration of " ++ name ++ " does not match " ++ lslTypeString t)] - return ((var:scope):scopes,returns) -validStatement scallow snames funcs vars rtype labels locals returns (While expr statement) = - do t <- validCtxExpr expr funcs vars locals - --when (t /= LLInteger) $ throwError [(srcCtx expr, "expression is not a valid loop condition")] - validStatement scallow snames funcs vars rtype labels locals False statement - return (locals,returns) -validStatement scallow snames funcs vars rtype labels locals returns (DoWhile statement expr) = - do t <- validCtxExpr expr funcs vars locals - --when (t /= LLInteger) $ throwError [(srcCtx expr, "expression is not a valid loop condition")] - validStatement scallow snames funcs vars rtype labels locals False statement - return (locals,returns) -validStatement scallow snames funcs vars rtype labels locals returns (For mexpr1 mexpr2 mexpr3 statement) = - do validExpressions mexpr1 funcs vars locals - validExpressions mexpr3 funcs vars locals - t <- validMExpression mexpr2 funcs vars locals - --when (t /= LLInteger) $ throwStrError ("expression is not a valid loop condition") - validStatement scallow snames funcs vars rtype labels locals False statement - return (locals,returns) -validStatement scallow snames funcs vars rtype labels locals returns (If expr thenStmt elseStmt) = - do t <- validCtxExpr expr funcs vars locals - --when (t /= LLInteger) $ throwError [(srcCtx expr, "expression is not a valid 'if' condition")] - (_,ret1) <- validStatement scallow snames funcs vars rtype labels locals False thenStmt - (_,ret2) <- validStatement scallow snames funcs vars rtype labels locals False elseStmt - return (locals,returns || ret1 && ret2) -validStatement _ _ _ _ _ _ locals returns NullStmt = return (locals,returns) -validStatement _ _ funcs vars rtype labels locals _ (Return Nothing) = - do when (rtype /= LLVoid) (throwStrError "function must return a value") - return (locals,True) -validStatement _ _ funcs vars rtype labels locals _ (Return (Just expr)) = - do t <- validCtxExpr expr funcs vars locals - when (t /= rtype && not (all (`elem` [LLString,LLKey]) [t,rtype])) (throwStrError "inappropriate return type for function/handler") - return (locals,True) -validStatement scallow snames funcs vars rtype labels locals returns (StateChange name) = do - when (not scallow) $ throwStrError "state changes not allowed from this context" - when (not (name `elem` snames)) $ throwStrError (name ++ " is not a valid state") - return (locals,returns) -validStatement _ _ funcs vars rtype labels locals returns (Do expr) = validCtxExpr expr funcs vars locals>>return (locals,returns) -validStatement scallow snames funcs vars rtype labels locals returns (Compound stmts) = - do returns' <- validStatements scallow snames funcs vars rtype labels ([]:locals) stmts - return (locals,returns || returns') -validStatement _ _ funcs vars rtype labels locals _ (Label _) = return (locals,False) -validStatement _ _ funcs vars rtype labels locals returns (Jump s) = - do when (s `notElem` concat labels) $ throwStrError ("no such label to jump to: " ++ s) - return (locals,returns) - -validStatement' scallow snames funcs vars rtype labels locals returns line (Ctx ctx stmt) = - incontext (ctx, "") $ validStatement scallow snames funcs vars rtype labels locals returns stmt - -validStatements :: Bool -> [String] -> [FuncDec] -> [Var] -> LSLType -> [[String]] -> [[Var]] -> [CtxStmt] -> Validity Bool -validStatements scallow snames funcs vars rtype labels locals stmts = - do let newLabels = map (\ (Label s) -> s) $ filter isLabel (ctxItems stmts) - (_,r') <- foldM (\ (l,r) (n, s) -> - validStatement' scallow snames funcs vars rtype (newLabels:labels) l r n s) (locals,False) $ zip ([1..]::[Int]) stmts - return r' - -validHandler snames used funcs vars (Handler (Ctx ctx name) args stmts) = - do when (name `elem` used) $ throwError [(ctx,name ++ " already used in state")] - types <- incontext (ctx,"handler: ") $ lookupM name goodHandlers - when (types /= map varType args') $ throwError [(ctx,"invalid argument types for handler " ++ name)] - when (length args /= (length $ nub $ map varName args')) $ throwError [(ctx,"not all argument names are unique for handler " ++ name)] - validStatements True snames funcs vars LLVoid [] [[],args'] stmts - return name - where args' = ctxItems args - -validHandlers _ _ _ _ [] = return () -validHandlers snames used funcs vars (h:hs) = - do name <- validHandler snames used funcs vars h - validHandlers snames (name:used) funcs vars hs - -- Validating a library of modules -compileModule :: LModule -> VState (Validity ([Global],[Func])) +compileModule :: LModule -> VState (Validity ([Global],[Ctx Func])) compileModule m@(LModule globs freevars) = do mapM_ (vsmAddGV . ctxItem) freevars preprocessGlobDefs_ "" globs @@ -1711,17 +1166,6 @@ funcs <- get'vsFuncs return $ Right $ (globals,funcs) _ -> return $ Left errs --- -validModule library m@(LModule globs freevars) = - do --used <- noDupGlobs Nothing "" [] library globs - (typedVars, typedFuncs) <- typeGlobs library globs - let used = (map varName typedVars) ++ (map (ctxItem . funcName) typedFuncs) - noDupVars used freevars - let vars = freevars' ++ reverse typedVars - let funcDecs = typedFuncs ++ predefFuncs - (vs,fs,_,_) <- foldM (validGlob library vars funcDecs) ([],[],[],[]) globs - return (vs,fs) - where freevars' = ctxItems freevars -- this function isn't partiuclarly efficient! moduleDependencies lib chain m = @@ -1768,20 +1212,6 @@ Right gs -> (name,Right (m,gs)):augLib in (foldl validate [] sorted) ++ (map (\ (n,s) -> (n,Left s)) bad) -validLibrary 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 validModule (libFromAugLib augLib) m 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) @@ -1825,7 +1255,7 @@ 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 $ Func (FuncDec combinedName LLVoid params) stmts +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 @@ -1870,5 +1300,3 @@ rewriteCtxExprs bindings ctxExprs = map (rewriteCtxExpr bindings) ctxExprs rewriteMExpression bindings = fmap (rewriteCtxExpr bindings) - -
src/Language/Lsl/UnitTest.hs view
@@ -13,9 +13,9 @@ import Language.Lsl.Internal.Exec(Binding(..)) import Language.Lsl.Internal.Util(removeLookup) -data FuncCallExpectations = FuncCallExpectations { +data FuncCallExpectations a = FuncCallExpectations { expectationMode :: ExpectationMode, - callList :: [((String, [Maybe LSLValue]),LSLValue)] } deriving (Show) + callList :: [((String, [Maybe (LSLValue a)]),LSLValue a)] } deriving (Show) data ExpectationMode = Nice | Normal | Exhaust | Strict deriving (Show,Eq) @@ -25,11 +25,11 @@ data LSLUnitTest = LSLUnitTest { unitTestName :: String, entryPoint :: EntryPoint, - initialGlobs :: [Binding], - arguments :: [LSLValue], - expectedCalls :: FuncCallExpectations, - expectedReturn :: Maybe LSLValue, - expectedGlobalVals :: [Binding], + initialGlobs :: [Binding Float], + arguments :: [LSLValue Float], + expectedCalls :: FuncCallExpectations Float, + expectedReturn :: Maybe (LSLValue Float), + expectedGlobalVals :: [Binding Float], expectedNewState :: Maybe String } deriving (Show) @@ -47,7 +47,7 @@ matchFail :: Monad m => m a matchFail = fail "no matching call" -expectedReturns :: (Monad m) => String -> [LSLValue] -> FuncCallExpectations -> m ((String,[Maybe LSLValue]),LSLValue) +expectedReturns :: (RealFloat a,Monad m) => String -> [LSLValue a] -> FuncCallExpectations a -> m ((String,[Maybe (LSLValue a)]),LSLValue a) expectedReturns name args (FuncCallExpectations Strict (match@((name',expectArgs),returns):_)) = if name /= name' || argsMatch expectArgs args == Nothing then matchFail else return match @@ -69,5 +69,5 @@ (Nothing,_) -> matchFail (_,e) -> return e -removeExpectation :: (String,[Maybe LSLValue]) -> FuncCallExpectations -> FuncCallExpectations +removeExpectation :: (RealFloat a) => (String,[Maybe (LSLValue a)]) -> FuncCallExpectations a -> FuncCallExpectations a removeExpectation m fce = fce { callList = removeLookup m (callList fce) }
src/Language/Lsl/UnitTestEnv.hs view
@@ -1,6 +1,7 @@ module Language.Lsl.UnitTestEnv( simStep, simFunc, + simSFunc, hasFunc, SimpleWorld, TestEvent(..), @@ -32,31 +33,31 @@ --trace1 v = trace ("->>" ++ (show v)) v -data SimpleWorld = SimpleWorld { +data SimpleWorld a = SimpleWorld { maxTick :: Int, tick :: Int, msgLog :: [(Int,String)], wScripts :: [(String,Validity CompiledLSLScript)], wLibrary :: [(String,Validity LModule)], - expectations :: FuncCallExpectations, + expectations :: FuncCallExpectations a, breakpointManager :: BreakpointManager } -type SimpleWorldM = ErrorT String (State SimpleWorld) +type SimpleWorldM a = ErrorT String (State (SimpleWorld a)) simpleWorldM = ErrorT . State -getTick :: SimpleWorldM Int +getTick :: SimpleWorldM a Int getTick = get >>= return . tick -getMaxTick :: SimpleWorldM Int +getMaxTick :: SimpleWorldM a Int getMaxTick = get >>= return . maxTick -getMsgLog :: SimpleWorldM [(Int,String)] +getMsgLog :: SimpleWorldM a [(Int,String)] getMsgLog = get >>= return . msgLog -getWScripts :: SimpleWorldM [(String, Validity CompiledLSLScript)] +getWScripts :: SimpleWorldM a [(String, Validity CompiledLSLScript)] getWScripts = get >>= return . wScripts -getWLibrary :: SimpleWorldM [(String, Validity LModule)] +getWLibrary :: SimpleWorldM a [(String, Validity LModule)] getWLibrary = get >>= return . wLibrary -getExpectations :: SimpleWorldM FuncCallExpectations +getExpectations :: SimpleWorldM a (FuncCallExpectations a) getExpectations = get >>= return . expectations -getBreakpointManager :: SimpleWorldM BreakpointManager +getBreakpointManager :: SimpleWorldM a BreakpointManager getBreakpointManager = get >>= return . breakpointManager setTick t = do w <- get; put (w { tick = t }) @@ -75,6 +76,7 @@ tick <- getTick modifyMsgLog ((tick,s):) +doPredef :: (Read a, RealFloat a) => String -> b -> [LSLValue a] -> ErrorT String (State (SimpleWorld a)) (EvalResult,LSLValue a) doPredef n i a = do logMsg $ "call: " ++ renderCall n a case lookup n internalLLFuncs of @@ -108,7 +110,28 @@ Nothing -> return (Left $ "No such script: " ++ name) Just (Left s) -> return $ Left $ "Invalid script: " ++ name Just (Right script) -> return $ Right script + +findValidScript scripts name = + case lookup name scripts of + Nothing -> Left $ "No such script: " ++ name + Just (Left s) -> Left $ "Invalid script: " ++ name + Just (Right script) -> Right script +convertEntryPoint' scripts _ (ScriptFunc scriptName funcName) = do + script <- findValidScript scripts scriptName + return (script,[funcName]) +convertEntryPoint' scripts _ (ScriptHandler scriptName stateName handlerName) = do + script <- findValidScript scripts scriptName + return (script,[stateName,handlerName]) +convertEntryPoint' _ modules (ModuleFunc moduleName funcName) = + case lookup moduleName modules of + Nothing -> Left $ "No such module: " ++ moduleName + Just (Left s) -> Left $ "Invalid module: " ++ moduleName + Just (Right lmodule) -> + case compileLSLScript' modules (mkScript lmodule) of + Left _ -> Left "Invalid entry point (internal error?)" + Right script -> Right (script,[funcName]) + convertEntryPoint (ScriptFunc scriptName funcName) = do script <- getValidScript scriptName return $ liftM2 (,) script (Right [funcName]) @@ -164,7 +187,7 @@ -------------------------------------------------- -- 'Interactive' testing -data TestEvent = TestComplete TestResult | TestSuspended ExecutionInfo | AllComplete +data TestEvent a = TestComplete TestResult | TestSuspended (ExecutionInfo a) | AllComplete data ExecCommand = ExecContinue [Breakpoint] | ExecStep [Breakpoint] | ExecStepOver [Breakpoint] | ExecStepOut [Breakpoint] @@ -179,27 +202,22 @@ case converted of Left s -> Left ("no such module: " ++ moduleName) Right (Left s) -> Left ("no such module: " ++ moduleName) - Right (Right (script,path)) -> Right $ isJust (findFunc functionName $ scriptFuncs script) + Right (Right (script,path)) -> Right $ isJust (findFunc functionName $ map ctxItem $ scriptFuncs script) where converted = evalState (runErrorT (convertEntryPoint ep)) world ep = ModuleFunc moduleName functionName world = SimpleWorld { maxTick = 10000, tick = 0, msgLog = [], wScripts = [], wLibrary = lib, expectations = FuncCallExpectations Nice [], breakpointManager = emptyBreakpointManager } - -simFunc :: [(String,Validity LModule)] -> (String,String) -> [(String,LSLValue)] -> [LSLValue] -> Either String (LSLValue,[(String,LSLValue)]) -simFunc lib (moduleName,functionName) globs args = - let world = SimpleWorld { maxTick = 10000, tick = 0, msgLog = [], wScripts = [], wLibrary = lib, + +simSFunc :: (Read a, RealFloat a) => (CompiledLSLScript,[String]) -> [(String,LSLValue a)] -> [LSLValue a] -> Either String (LSLValue a,[(String,LSLValue a)]) +simSFunc (script,path) globs args = + let world = SimpleWorld { maxTick = 10000, tick = 0, msgLog = [], wScripts = [], wLibrary = [], expectations = FuncCallExpectations Nice [], breakpointManager = emptyBreakpointManager } - ep = ModuleFunc moduleName functionName + exec = initStateSimple script doPredef logMsg getTick setTick checkBp init = runState (runErrorT ( - do converted <- convertEntryPoint ep - case converted of - Left s -> fail s - Right (script,path) -> - do result <- runEval (setupSimple path globs args) exec - case result of - (Left s, _) -> fail s - (Right (), exec') -> return exec' - where exec = initStateSimple script doPredef logMsg getTick setTick checkBp)) world + do result <- runEval (setupSimple path globs args) exec + case result of + (Left s, _) -> fail s + (Right (), exec') -> return exec')) world in case init of (Left s, world') -> Left s (Right exec,world') -> @@ -212,6 +230,42 @@ (Right (EvalComplete newState, _),_) -> Left "execution error" (Right (EvalIncomplete,_),_) -> Left "execution error: timeout" (Right _,_) -> Left "execution error" + +-- simFunc' lib (moduleName,functionName) globs args = +-- let ep = ModuleFunc moduleName functionName +simFunc :: (Read a, RealFloat a) => [(String,Validity LModule)] -> (String,String) -> [(String,LSLValue a)] -> [LSLValue a] -> Either String (LSLValue a,[(String,LSLValue a)]) +simFunc lib (moduleName,functionName) globs args = + case convertEntryPoint' [] lib (ModuleFunc moduleName functionName) of + Left s -> Left s + Right (script,path) -> simSFunc (script,path) globs args + +-- simFunc :: (Read a, RealFloat a) => [(String,Validity LModule)] -> (String,String) -> [(String,LSLValue a)] -> [LSLValue a] -> Either String (LSLValue a,[(String,LSLValue a)]) +-- simFunc lib (moduleName,functionName) globs args = +-- let world = SimpleWorld { maxTick = 10000, tick = 0, msgLog = [], wScripts = [], wLibrary = lib, +-- expectations = FuncCallExpectations Nice [], breakpointManager = emptyBreakpointManager } +-- ep = ModuleFunc moduleName functionName +-- init = runState (runErrorT ( +-- do converted <- convertEntryPoint ep +-- case converted of +-- Left s -> fail s +-- Right (script,path) -> +-- do result <- runEval (setupSimple path globs args) exec +-- case result of +-- (Left s, _) -> fail s +-- (Right (), exec') -> return exec' +-- where exec = initStateSimple script doPredef logMsg getTick setTick checkBp)) world +-- in case init of +-- (Left s, world') -> Left s +-- (Right exec,world') -> +-- case (runState $ runErrorT $ (runStateT $ runErrorT $ evalSimple 10000) exec) world of +-- (Left s,_) -> Left s +-- (Right r, _) -> +-- case r of +-- (Left s,_) -> Left s +-- (Right (EvalComplete newState, Just val), exec') -> Right (val,glob $ scriptImage exec') +-- (Right (EvalComplete newState, _),_) -> Left "execution error" +-- (Right (EvalIncomplete,_),_) -> Left "execution error: timeout" +-- (Right _,_) -> Left "execution error" simSome exec world = runState (runErrorT ( do maxTick <- getMaxTick
src/Language/Lsl/WorldDef.hs view
@@ -153,7 +153,7 @@ avatarCameraControlParams :: CameraParams, avatarActiveAnimations :: [(Maybe Int,String)], avatarAttachments :: IM.IntMap String, - avatarEventHandler :: !(Maybe (String,[(String,LSLValue)])), + avatarEventHandler :: !(Maybe (String,[(String,LSLValue Float)])), avatarControls :: !Int, avatarControlListener :: !(Maybe AvatarControlListener) } deriving (Show) @@ -213,7 +213,7 @@ primPhysicsBit :: Int primPhysicsBit = 0 -data InventoryItemData = InvScript { invScriptLibId :: String, invScriptState :: Maybe ScriptImage } +data InventoryItemData = InvScript { invScriptLibId :: String, invScriptState :: Maybe (ScriptImage Float) } | InvBodyPart | InvGesture | InvClothing @@ -434,13 +434,13 @@ regionParcels = [Parcel "parcel_0" "default parcel" (0,256,0,256) owner 0xffffffff [] []] }) ] -data Script = Script { scriptImage :: !ScriptImage, +data Script = Script { scriptImage :: !(ScriptImage Float), scriptActive :: Bool, scriptPermissions :: M.Map String Int, scriptLastPerm :: Maybe String, scriptStartTick :: Int, scriptLastResetTick :: Int, - scriptEventQueue :: [Event], + scriptEventQueue :: [Event Float], scriptStartParameter :: Int, scriptCollisionFilter :: !(String,String,Bool), scriptTargetIndex :: !Int,
+ src/Text/ParserCombinators/ParsecExtras/Language.hs view
@@ -0,0 +1,128 @@+----------------------------------------------------------------------------- +-- | +-- Module : Text.ParserCombinators.ParsecExtras.Language +-- Copyright : (c) Daan Leijen 1999-2001 +-- (c) Robert Greayer 2008 +-- License : BSD-style (see the file libraries/parsec/LICENSE) +-- +-- Maintainer : robgreayer@yahoo.com +-- Stability : provisional +-- Portability : non-portable (uses non-portable module Text.ParserCombinators.ParsecExtras.Token) +-- +-- A helper module that defines some language definitions that can be used +-- to instantiate a token parser (see "Text.ParserCombinators.Parsec.Token"). +-- The 'extras' version exists purely to provide better control over whitespace handling. +-- It is nearly a cut-n-paste copy of the Text.ParserCombinators.Parsec.Language module +-- +----------------------------------------------------------------------------- + +module Text.ParserCombinators.ParsecExtras.Language + ( haskellDef, haskell + , mondrianDef, mondrian + + , emptyDef + , haskellStyle + , javaStyle + , LanguageDef (..) + ) where +import Text.ParserCombinators.Parsec +import Text.ParserCombinators.ParsecExtras.Token + + +----------------------------------------------------------- +-- Styles: haskellStyle, javaStyle +----------------------------------------------------------- +haskellStyle :: LanguageDef st +haskellStyle= emptyDef + { commentStart = "{-" + , commentEnd = "-}" + , commentLine = "--" + , nestedComments = True + , identStart = letter + , identLetter = alphaNum <|> oneOf "_'" + , opStart = opLetter haskellStyle + , opLetter = oneOf ":!#$%&*+./<=>?@\\^|-~" + , reservedOpNames= [] + , reservedNames = [] + , caseSensitive = True + } + +javaStyle :: LanguageDef st +javaStyle = emptyDef + { commentStart = "/*" + , commentEnd = "*/" + , commentLine = "//" + , nestedComments = True + , identStart = letter + , identLetter = alphaNum <|> oneOf "_'" + , reservedNames = [] + , reservedOpNames= [] + , caseSensitive = False + } + +----------------------------------------------------------- +-- minimal language definition +----------------------------------------------------------- +emptyDef :: LanguageDef st +emptyDef = LanguageDef + { commentStart = "" + , commentEnd = "" + , commentLine = "" + , nestedComments = True + , identStart = letter <|> char '_' + , identLetter = alphaNum <|> oneOf "_'" + , opStart = opLetter emptyDef + , opLetter = oneOf ":!#$%&*+./<=>?@\\^|-~" + , reservedOpNames= [] + , reservedNames = [] + , caseSensitive = True + , custWhiteSpace = Nothing + } + + + +----------------------------------------------------------- +-- Haskell +----------------------------------------------------------- +haskell :: TokenParser st +haskell = makeTokenParser haskellDef + +haskellDef :: LanguageDef st +haskellDef = haskell98Def + { identLetter = identLetter haskell98Def <|> char '#' + , reservedNames = reservedNames haskell98Def ++ + ["foreign","import","export","primitive" + ,"_ccall_","_casm_" + ,"forall" + ] + } + +haskell98Def :: LanguageDef st +haskell98Def = haskellStyle + { reservedOpNames= ["::","..","=","\\","|","<-","->","@","~","=>"] + , reservedNames = ["let","in","case","of","if","then","else", + "data","type", + "class","default","deriving","do","import", + "infix","infixl","infixr","instance","module", + "newtype","where", + "primitive" + -- "as","qualified","hiding" + ] + } + + +----------------------------------------------------------- +-- Mondrian +----------------------------------------------------------- +mondrian :: TokenParser st +mondrian = makeTokenParser mondrianDef + +mondrianDef :: LanguageDef st +mondrianDef = javaStyle + { reservedNames = [ "case", "class", "default", "extends" + , "import", "in", "let", "new", "of", "package" + ] + , caseSensitive = True + } + +
+ src/Text/ParserCombinators/ParsecExtras/Token.hs view
@@ -0,0 +1,481 @@+{-# LANGUAGE PolymorphicComponents, ExistentialQuantification #-} +----------------------------------------------------------------------------- +-- | +-- Module : Text.ParserCombinators.ParsecExtras.Token +-- Copyright : (c) Daan Leijen 1999-2001 +-- : (c) Rob Greayer 2009 +-- License : BSD-style (see the file libraries/parsec/LICENSE) +-- +-- Maintainer : robgreayer@yahoo.com +-- Stability : provisional +-- Portability : non-portable (uses existentially quantified data constructors) +-- +-- A helper module to parse lexical elements (tokens). +-- A separate version to allow customizable whitespace handling +-- +----------------------------------------------------------------------------- + +module Text.ParserCombinators.ParsecExtras.Token + ( LanguageDef (..) + , TokenParser (..) + , makeTokenParser + ) where + +import Data.Char (isAlpha,toLower,toUpper,isSpace,digitToInt) +import Data.List (nub,sort) +import Data.Maybe (isJust,fromJust) +import Text.ParserCombinators.Parsec + +----------------------------------------------------------- +-- Language Definition +----------------------------------------------------------- +data LanguageDef st + = LanguageDef + { commentStart :: String + , commentEnd :: String + , commentLine :: String + , nestedComments :: Bool + , identStart :: CharParser st Char + , identLetter :: CharParser st Char + , opStart :: CharParser st Char + , opLetter :: CharParser st Char + , reservedNames :: [String] + , reservedOpNames:: [String] + , caseSensitive :: Bool + , custWhiteSpace :: Maybe (CharParser st String -> CharParser st String -> CharParser st String -> CharParser st ()) + } + +----------------------------------------------------------- +-- A first class module: TokenParser +----------------------------------------------------------- +data TokenParser st + = TokenParser{ identifier :: CharParser st String + , reserved :: String -> CharParser st () + , operator :: CharParser st String + , reservedOp :: String -> CharParser st () + + , charLiteral :: CharParser st Char + , stringLiteral :: CharParser st String + , natural :: CharParser st Integer + , integer :: CharParser st Integer + , float :: CharParser st Double + , naturalOrFloat :: CharParser st (Either Integer Double) + , decimal :: CharParser st Integer + , hexadecimal :: CharParser st Integer + , octal :: CharParser st Integer + + , symbol :: String -> CharParser st String + , lexeme :: forall a. CharParser st a -> CharParser st a + , whiteSpace :: CharParser st () + , simpleSpace :: CharParser st String + , multiLineComment :: CharParser st String + , oneLineComment :: CharParser st String + , parens :: forall a. CharParser st a -> CharParser st a + , braces :: forall a. CharParser st a -> CharParser st a + , angles :: forall a. CharParser st a -> CharParser st a + , brackets :: forall a. CharParser st a -> CharParser st a + -- "squares" is deprecated + , squares :: forall a. CharParser st a -> CharParser st a + + , semi :: CharParser st String + , comma :: CharParser st String + , colon :: CharParser st String + , dot :: CharParser st String + , semiSep :: forall a . CharParser st a -> CharParser st [a] + , semiSep1 :: forall a . CharParser st a -> CharParser st [a] + , commaSep :: forall a . CharParser st a -> CharParser st [a] + , commaSep1 :: forall a . CharParser st a -> CharParser st [a] + } + +----------------------------------------------------------- +-- Given a LanguageDef, create a token parser. +----------------------------------------------------------- +makeTokenParser :: LanguageDef st -> TokenParser st +makeTokenParser languageDef + = TokenParser{ identifier = identifier + , reserved = reserved + , operator = operator + , reservedOp = reservedOp + + , charLiteral = charLiteral + , stringLiteral = stringLiteral + , natural = natural + , integer = integer + , float = float + , naturalOrFloat = naturalOrFloat + , decimal = decimal + , hexadecimal = hexadecimal + , octal = octal + + , symbol = symbol + , lexeme = lexeme + , whiteSpace = whiteSpace + , simpleSpace = simpleSpace + , multiLineComment = multiLineComment + , oneLineComment = oneLineComment + + , parens = parens + , braces = braces + , angles = angles + , brackets = brackets + , squares = brackets + , semi = semi + , comma = comma + , colon = colon + , dot = dot + , semiSep = semiSep + , semiSep1 = semiSep1 + , commaSep = commaSep + , commaSep1 = commaSep1 + } + where + + ----------------------------------------------------------- + -- Bracketing + ----------------------------------------------------------- + parens p = between (symbol "(") (symbol ")") p + braces p = between (symbol "{") (symbol "}") p + angles p = between (symbol "<") (symbol ">") p + brackets p = between (symbol "[") (symbol "]") p + + semi = symbol ";" + comma = symbol "," + dot = symbol "." + colon = symbol ":" + + commaSep p = sepBy p comma + semiSep p = sepBy p semi + + commaSep1 p = sepBy1 p comma + semiSep1 p = sepBy1 p semi + + + ----------------------------------------------------------- + -- Chars & Strings + ----------------------------------------------------------- + -- charLiteral :: CharParser st Char + charLiteral = lexeme (between (char '\'') + (char '\'' <?> "end of character") + characterChar ) + <?> "character" + + characterChar = charLetter <|> charEscape + <?> "literal character" + + charEscape = do{ char '\\'; escapeCode } + charLetter = satisfy (\c -> (c /= '\'') && (c /= '\\') && (c > '\026')) + + + + -- stringLiteral :: CharParser st String + stringLiteral = lexeme ( + do{ str <- between (char '"') + (char '"' <?> "end of string") + (many stringChar) + ; return (foldr (maybe id (:)) "" str) + } + <?> "literal string") + + -- stringChar :: CharParser st (Maybe Char) + stringChar = do{ c <- stringLetter; return (Just c) } + <|> stringEscape + <?> "string character" + + stringLetter = satisfy (\c -> (c /= '"') && (c /= '\\') && (c > '\026')) + + stringEscape = do{ char '\\' + ; do{ escapeGap ; return Nothing } + <|> do{ escapeEmpty; return Nothing } + <|> do{ esc <- escapeCode; return (Just esc) } + } + + escapeEmpty = char '&' + escapeGap = do{ many1 space + ; char '\\' <?> "end of string gap" + } + + + + -- escape codes + escapeCode = charEsc <|> charNum <|> charAscii <|> charControl + <?> "escape code" + + -- charControl :: CharParser st Char + charControl = do{ char '^' + ; code <- upper + ; return (toEnum (fromEnum code - fromEnum 'A')) + } + + -- charNum :: CharParser st Char + charNum = do{ code <- decimal + <|> do{ char 'o'; number 8 octDigit } + <|> do{ char 'x'; number 16 hexDigit } + ; return (toEnum (fromInteger code)) + } + + charEsc = choice (map parseEsc escMap) + where + parseEsc (c,code) = do{ char c; return code } + + charAscii = choice (map parseAscii asciiMap) + where + parseAscii (asc,code) = try (do{ string asc; return code }) + + + -- escape code tables + escMap = zip ("abfnrtv\\\"\'") ("\a\b\f\n\r\t\v\\\"\'") + asciiMap = zip (ascii3codes ++ ascii2codes) (ascii3 ++ ascii2) + + ascii2codes = ["BS","HT","LF","VT","FF","CR","SO","SI","EM", + "FS","GS","RS","US","SP"] + ascii3codes = ["NUL","SOH","STX","ETX","EOT","ENQ","ACK","BEL", + "DLE","DC1","DC2","DC3","DC4","NAK","SYN","ETB", + "CAN","SUB","ESC","DEL"] + + ascii2 = ['\BS','\HT','\LF','\VT','\FF','\CR','\SO','\SI', + '\EM','\FS','\GS','\RS','\US','\SP'] + ascii3 = ['\NUL','\SOH','\STX','\ETX','\EOT','\ENQ','\ACK', + '\BEL','\DLE','\DC1','\DC2','\DC3','\DC4','\NAK', + '\SYN','\ETB','\CAN','\SUB','\ESC','\DEL'] + + + ----------------------------------------------------------- + -- Numbers + ----------------------------------------------------------- + -- naturalOrFloat :: CharParser st (Either Integer Double) + naturalOrFloat = lexeme (natFloat) <?> "number" + + float = lexeme floating <?> "float" + integer = lexeme int <?> "integer" + natural = lexeme nat <?> "natural" + + + -- floats + floating = do{ n <- decimal + ; fractExponent n + } + + + natFloat = do{ char '0' + ; zeroNumFloat + } + <|> decimalFloat + + zeroNumFloat = do{ n <- hexadecimal <|> octal + ; return (Left n) + } + <|> decimalFloat + <|> fractFloat 0 + <|> return (Left 0) + + decimalFloat = do{ n <- decimal + ; option (Left n) + (fractFloat n) + } + + fractFloat n = do{ f <- fractExponent n + ; return (Right f) + } + + fractExponent n = do{ fract <- fraction + ; expo <- option 1.0 exponent' + ; return ((fromInteger n + fract)*expo) + } + <|> + do{ expo <- exponent' + ; return ((fromInteger n)*expo) + } + + fraction = do{ char '.' + ; digits <- many1 digit <?> "fraction" + ; return (foldr op 0.0 digits) + } + <?> "fraction" + where + op d f = (f + fromIntegral (digitToInt d))/10.0 + + exponent' = do{ oneOf "eE" + ; f <- sign + ; e <- decimal <?> "exponent" + ; return (power (f e)) + } + <?> "exponent" + where + power e | e < 0 = 1.0/power(-e) + | otherwise = fromInteger (10^e) + + + -- integers and naturals + int = do{ f <- lexeme sign + ; n <- nat + ; return (f n) + } + + -- sign :: CharParser st (Integer -> Integer) + sign = (char '-' >> return negate) + <|> (char '+' >> return id) + <|> return id + + nat = zeroNumber <|> decimal + + zeroNumber = do{ char '0' + ; hexadecimal <|> octal <|> decimal <|> return 0 + } + <?> "" + + decimal = number 10 digit + hexadecimal = do{ oneOf "xX"; number 16 hexDigit } + octal = do{ oneOf "oO"; number 8 octDigit } + + -- number :: Integer -> CharParser st Char -> CharParser st Integer + number base baseDigit + = do{ digits <- many1 baseDigit + ; let n = foldl (\x d -> base*x + toInteger (digitToInt d)) 0 digits + ; seq n (return n) + } + + ----------------------------------------------------------- + -- Operators & reserved ops + ----------------------------------------------------------- + reservedOp name = + lexeme $ try $ + do{ string name + ; notFollowedBy (opLetter languageDef) <?> ("end of " ++ show name) + } + + operator = + lexeme $ try $ + do{ name <- oper + ; if (isReservedOp name) + then unexpected ("reserved operator " ++ show name) + else return name + } + + oper = + do{ c <- (opStart languageDef) + ; cs <- many (opLetter languageDef) + ; return (c:cs) + } + <?> "operator" + + isReservedOp name = + isReserved (sort (reservedOpNames languageDef)) name + + + ----------------------------------------------------------- + -- Identifiers & Reserved words + ----------------------------------------------------------- + reserved name = + lexeme $ try $ + do{ caseString name + ; notFollowedBy (identLetter languageDef) <?> ("end of " ++ show name) + } + + caseString name + | caseSensitive languageDef = string name + | otherwise = do{ walk name; return name } + where + walk [] = return () + walk (c:cs) = do{ caseChar c <?> msg; walk cs } + + caseChar c | isAlpha c = char (toLower c) <|> char (toUpper c) + | otherwise = char c + + msg = show name + + + identifier = + lexeme $ try $ + do{ name <- ident + ; if (isReservedName name) + then unexpected ("reserved word " ++ show name) + else return name + } + + + ident + = do{ c <- identStart languageDef + ; cs <- many (identLetter languageDef) + ; return (c:cs) + } + <?> "identifier" + + isReservedName name + = isReserved theReservedNames caseName + where + caseName | caseSensitive languageDef = name + | otherwise = map toLower name + + + isReserved names name + = scan names + where + scan [] = False + scan (r:rs) = case (compare r name) of + LT -> scan rs + EQ -> True + GT -> False + + theReservedNames + | caseSensitive languageDef = sortedNames + | otherwise = map (map toLower) sortedNames + where + sortedNames = sort (reservedNames languageDef) + + + + ----------------------------------------------------------- + -- White space & symbols + ----------------------------------------------------------- + symbol name + = lexeme (string name) + + lexeme p + = do{ x <- p; whiteSpace; return x } + + + --whiteSpace + whiteSpace + | isJust (custWhiteSpace languageDef) = (fromJust (custWhiteSpace languageDef)) simpleSpace oneLineComment multiLineComment + | noLine && noMulti = skipMany (simpleSpace <?> "") + | noLine = skipMany (simpleSpace <|> multiLineComment <?> "") + | noMulti = skipMany (simpleSpace <|> oneLineComment <?> "") + | otherwise = skipMany (simpleSpace <|> oneLineComment <|> multiLineComment <?> "") + where + noLine = null (commentLine languageDef) + noMulti = null (commentStart languageDef) + + + simpleSpace = many1 (satisfy isSpace) + + oneLineComment = + do{ try (string (commentLine languageDef)) + ; many (satisfy (/= '\n')) + } + + multiLineComment = + do { try (string (commentStart languageDef)) + ; inComment + } + + inComment + | nestedComments languageDef = inCommentMulti + | otherwise = inCommentSingle + + inCommentMulti + = do{ try (string (commentEnd languageDef)) ; return "" } + <|> do{ cmt <- multiLineComment ; rest <- inCommentMulti; return (cmt ++ rest) } + <|> do{ txt <- many1 (noneOf startEnd) ; rest <- inCommentMulti; return (txt ++ rest) } + <|> do{ ch <- oneOf startEnd ; rest <- inCommentMulti; return (ch:rest) } + <?> "end of comment" + where + startEnd = nub (commentEnd languageDef ++ commentStart languageDef) + + inCommentSingle + = do{ try (string (commentEnd languageDef)); return (commentEnd languageDef) } + <|> do{ txt <- many1 (noneOf startEnd) ; rest <- inCommentSingle; return (txt ++ rest) } + <|> do{ c <- oneOf startEnd ; rest <- inCommentSingle; return (c:rest) } + <?> "end of comment" + where + startEnd = nub (commentEnd languageDef ++ commentStart languageDef) +