diff options
| -rw-r--r-- | src/Builtins.hs | 124 | ||||
| -rw-r--r-- | src/Interpreter.hs | 49 | ||||
| -rw-r--r-- | src/Utils.hs | 21 |
3 files changed, 95 insertions, 99 deletions
diff --git a/src/Builtins.hs b/src/Builtins.hs index f68cc78..281c6aa 100644 --- a/src/Builtins.hs +++ b/src/Builtins.hs @@ -55,108 +55,108 @@ argError3 fn arg1 arg2 arg3 = reservedKeyword :: String -> (String, AST) reservedKeyword name = (name, makeNonsenseAST $ ASTFunction fn1) where - fn1 _ ast1 = throwL (astPos ast1) $ "unreachable: " ++ name ++ " is a reserved word" + fn1 ast1 = throwL (astPos ast1) $ "unreachable: " ++ name ++ " is a reserved word" -- BUILTINS builtinAdd2 :: (String, AST) builtinAdd2 = (name, makeNonsenseAST $ ASTFunction fn1) where name = "+" - fn1 _ ast1@AST { astNode = ASTInteger a } = + fn1 ast1@AST { astNode = ASTInteger a } = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ AST { astNode = ASTInteger b } = + fn2 AST { astNode = ASTInteger b } = return $ makeNonsenseAST $ ASTInteger $ a + b - fn2 _ ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 - fn1 _ ast1@AST { astNode = ASTDouble a } = + fn2 ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 + fn1 ast1@AST { astNode = ASTDouble a } = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ AST { astNode = ASTDouble b } = + fn2 AST { astNode = ASTDouble b } = return $ makeNonsenseAST $ ASTDouble $ a + b - fn2 _ ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 - fn1 _ ast1 = throwL (astPos ast1) $ argError1 name ast1 + fn2 ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 + fn1 ast1 = throwL (astPos ast1) $ argError1 name ast1 builtinSubtract2 :: (String, AST) builtinSubtract2 = (name, makeNonsenseAST $ ASTFunction fn1) where name = "-" - fn1 _ ast1@AST { astNode = ASTInteger a } = + fn1 ast1@AST { astNode = ASTInteger a } = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ AST { astNode = ASTInteger b } = + fn2 AST { astNode = ASTInteger b } = return $ makeNonsenseAST $ ASTInteger $ a - b - fn2 _ ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 - fn1 _ ast1@AST { astNode = ASTDouble a } = + fn2 ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 + fn1 ast1@AST { astNode = ASTDouble a } = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ AST { astNode = ASTDouble b } = + fn2 AST { astNode = ASTDouble b } = return $ makeNonsenseAST $ ASTDouble $ a - b - fn2 _ ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 - fn1 _ ast1 = throwL (astPos ast1) $ argError1 name ast1 + fn2 ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 + fn1 ast1 = throwL (astPos ast1) $ argError1 name ast1 builtinMultiply2 :: (String, AST) builtinMultiply2 = (name, makeNonsenseAST $ ASTFunction fn1) where name = "*" - fn1 _ ast1@AST { astNode = ASTInteger a } = + fn1 ast1@AST { astNode = ASTInteger a } = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ AST { astNode = ASTInteger b } = + fn2 AST { astNode = ASTInteger b } = return $ makeNonsenseAST $ ASTInteger $ a * b - fn2 _ ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 - fn1 _ ast1@AST { astNode = ASTDouble a } = + fn2 ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 + fn1 ast1@AST { astNode = ASTDouble a } = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ AST { astNode = ASTDouble b } = + fn2 AST { astNode = ASTDouble b } = return $ makeNonsenseAST $ ASTDouble $ a * b - fn2 _ ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 - fn1 _ ast1 = throwL (astPos ast1) $ argError1 name ast1 + fn2 ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 + fn1 ast1 = throwL (astPos ast1) $ argError1 name ast1 builtinDivide2 :: (String, AST) builtinDivide2 = (name, makeNonsenseAST $ ASTFunction fn1) where name = "/" - fn1 _ ast1@AST { astNode = ASTInteger a } = + fn1 ast1@AST { astNode = ASTInteger a } = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ ast2@AST { astNode = ASTInteger b } = + fn2 ast2@AST { astNode = ASTInteger b } = do when (b == 0) $ throwL (astPos ast2) $ "division by zero" return $ makeNonsenseAST $ ASTInteger $ a `div` b - fn2 _ ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 - fn1 _ ast1@AST { astNode = ASTDouble a } = + fn2 ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 + fn1 ast1@AST { astNode = ASTDouble a } = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ ast2@AST { astNode = ASTDouble b } = + fn2 ast2@AST { astNode = ASTDouble b } = do when (b == 0) $ throwL (astPos ast2) $ "division by zero" return $ makeNonsenseAST $ ASTDouble $ a / b - fn2 _ ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 - fn1 _ ast1 = throwL (astPos ast1) $ argError1 name ast1 + fn2 ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 + fn1 ast1 = throwL (astPos ast1) $ argError1 name ast1 builtinEq2 :: (String, AST) builtinEq2 = (name, makeNonsenseAST $ ASTFunction fn1) where name = "eq?" - fn1 _ ast1 = + fn1 ast1 = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ ast2 = return $ makeNonsenseAST $ ASTBoolean $ ast1 == ast2 + fn2 ast2 = return $ makeNonsenseAST $ ASTBoolean $ ast1 == ast2 builtinLt2 :: (String, AST) builtinLt2 = (name, makeNonsenseAST $ ASTFunction fn1) where name = "lt?" - fn1 _ ast1 = + fn1 ast1 = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ ast2 = return $ makeNonsenseAST $ ASTBoolean $ ast1 <= ast2 + fn2 ast2 = return $ makeNonsenseAST $ ASTBoolean $ ast1 <= ast2 builtinFloor :: (String, AST) builtinFloor = (name, makeNonsenseAST $ ASTFunction fn1) where name = "floor" - fn1 _ AST { astNode = ASTDouble dbl } = return $ makeNonsenseAST $ ASTInteger $ floor dbl - fn1 _ ast1 = throwL (astPos ast1) $ argError1 name ast1 + fn1 AST { astNode = ASTDouble dbl } = return $ makeNonsenseAST $ ASTInteger $ floor dbl + fn1 ast1 = throwL (astPos ast1) $ argError1 name ast1 builtinToDouble :: (String, AST) builtinToDouble = (name, makeNonsenseAST $ ASTFunction fn1) where name = "to-double" - fn1 _ AST { astNode = ASTInteger int } = return $ makeNonsenseAST $ ASTDouble $ fromIntegral int - fn1 _ ast1 = throwL (astPos ast1) $ argError1 name ast1 + fn1 AST { astNode = ASTInteger int } = return $ makeNonsenseAST $ ASTDouble $ fromIntegral int + fn1 ast1 = throwL (astPos ast1) $ argError1 name ast1 builtinFmt :: (String, AST) builtinFmt = (name, makeNonsenseAST $ ASTFunction fn1) where name = "fmt" - fn1 _ ast1@AST { astNode = ASTString str } = + fn1 ast1@AST { astNode = ASTString str } = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ AST { astNode = ASTVector replacements } = + fn2 AST { astNode = ASTVector replacements } = return $ makeNonsenseAST $ ASTString $ T.unpack $ replaceAll (0 :: Int) replacements (T.pack str) - fn2 _ ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 - fn1 _ ast1 = throwL (astPos ast1) $ argError1 name ast1 + fn2 ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 + fn1 ast1 = throwL (astPos ast1) $ argError1 name ast1 replaceAll _ [] text = text replaceAll n (x:xs) text = let text' = T.replace (T.pack $ "{" ++ show n ++ "}") (T.pack $ show x) text @@ -165,63 +165,63 @@ builtinFmt = (name, makeNonsenseAST $ ASTFunction fn1) where builtinHead :: (String, AST) builtinHead = (name, makeNonsenseAST $ ASTFunction fn1) where name = "head" - fn1 _ ast1@AST { astNode = ASTVector vec } = + fn1 ast1@AST { astNode = ASTVector vec } = do when (length vec == 0) $ throwL (astPos ast1) $ name ++ " of empty vector" return $ head vec - fn1 _ ast1 = throwL (astPos ast1) $ argError1 name ast1 + fn1 ast1 = throwL (astPos ast1) $ argError1 name ast1 builtinTail :: (String, AST) builtinTail = (name, makeNonsenseAST $ ASTFunction fn1) where name = "tail" - fn1 _ ast1@AST { astNode = ASTVector vec } = + fn1 ast1@AST { astNode = ASTVector vec } = do when (length vec == 0) $ throwL (astPos ast1) $ name ++ " of empty vector" return $ makeNonsenseAST $ ASTVector $ tail vec - fn1 _ ast1 = throwL (astPos ast1) $ argError1 name ast1 + fn1 ast1 = throwL (astPos ast1) $ argError1 name ast1 builtinSubstr :: (String, AST) builtinSubstr = (name, makeNonsenseAST $ ASTFunction fn1) where name = "substr" - fn1 _ ast1@AST { astNode = ASTInteger at } = + fn1 ast1@AST { astNode = ASTInteger at } = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ ast2@AST { astNode = ASTInteger len } = + fn2 ast2@AST { astNode = ASTInteger len } = return $ makeNonsenseAST $ ASTFunction $ fn3 where - fn3 _ AST { astNode = ASTString str } = + fn3 AST { astNode = ASTString str } = return $ makeNonsenseAST $ ASTString $ drop at .> take len $ str - fn3 _ ast3 = throwL (astPos ast3) $ argError3 name ast1 ast2 ast3 - fn2 _ ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 - fn1 _ ast1 = throwL (astPos ast1) $ argError1 name ast1 + fn3 ast3 = throwL (astPos ast3) $ argError3 name ast1 ast2 ast3 + fn2 ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 + fn1 ast1 = throwL (astPos ast1) $ argError1 name ast1 builtinPrepend :: (String, AST) builtinPrepend = (name, makeNonsenseAST $ ASTFunction fn1) where name = "prepend" - fn1 _ ast1 = + fn1 ast1 = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ AST { astNode = ASTVector vec } = + fn2 AST { astNode = ASTVector vec } = return $ makeNonsenseAST $ ASTVector $ ast1 : vec - fn2 _ ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 + fn2 ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 builtinPrint :: (String, AST) builtinPrint = (name, makeNonsenseAST $ ASTFunction fn1) where name = "print!" - fn1 _ AST { astNode = ASTString str } = + fn1 AST { astNode = ASTString str } = do liftIO $ putStr $ str return $ makeNonsenseAST ASTUnit - fn1 _ ast1 = throwL (astPos ast1) $ argError1 name ast1 + fn1 ast1 = throwL (astPos ast1) $ argError1 name ast1 builtinConcat :: (String, AST) builtinConcat = (name, makeNonsenseAST $ ASTFunction fn1) where name = "concat" - fn1 _ ast1@AST { astNode = ASTString str1 } = + fn1 ast1@AST { astNode = ASTString str1 } = return $ makeNonsenseAST $ ASTFunction $ fn2 where - fn2 _ AST { astNode = ASTString str2 } = + fn2 AST { astNode = ASTString str2 } = return $ makeNonsenseAST $ ASTString $ str1 ++ str2 - fn2 _ ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 - fn1 _ ast1 = throwL (astPos ast1) $ argError1 name ast1 + fn2 ast2 = throwL (astPos ast2) $ argError2 name ast1 ast2 + fn1 ast1 = throwL (astPos ast1) $ argError1 name ast1 builtinFatal :: (String, AST) builtinFatal = (name, makeNonsenseAST $ ASTFunction fn1) where name = "fatal!" - fn1 _ ast1@AST { astNode = ASTString str } = + fn1 ast1@AST { astNode = ASTString str } = throwL (astPos ast1) $ str - fn1 _ ast1 = + fn1 ast1 = throwL (astPos ast1) $ argError1 name ast1 diff --git a/src/Interpreter.hs b/src/Interpreter.hs index e649464..b745ff2 100644 --- a/src/Interpreter.hs +++ b/src/Interpreter.hs @@ -11,26 +11,24 @@ import Data.Function ( on ) import Control.Monad.State import Control.Monad.Except import Utils --- import Debug.Trace import Builtins import Tokenizer ( tokenize ) import Parser ( parse ) type Depth = Int -_curryCall :: Env -> [AST] -> LFunction -> LContext AST -_curryCall _ [] f = return $ (makeNonsenseAST $ ASTFunction f) -_curryCall env (arg:[]) f = f env arg -_curryCall env (arg:rest) f = do - g <- _curryCall env rest f +_curryCall :: [AST] -> LFunction -> LContext AST +_curryCall [] f = return $ (makeNonsenseAST $ ASTFunction f) +_curryCall (arg:[]) f = f arg +_curryCall (arg:rest) f = do + g <- _curryCall rest f case astNode g of - ASTFunction f' -> f' env arg + ASTFunction f' -> f' arg other -> throwL (astPos g) $ "cannot call value " ++ show other ++ " as a function" --- todo remove Env param, use State monad -curryCall :: Env -> [AST] -> LFunction -> LContext AST -curryCall env [] f = f env (makeNonsenseAST ASTUnit) -curryCall env args f = _curryCall env args f +curryCall :: [AST] -> LFunction -> LContext AST +curryCall [] f = f (makeNonsenseAST ASTUnit) +curryCall args f = _curryCall args f traverseAndReplace :: String -> AST -> AST -> AST traverseAndReplace param arg ast@AST { astNode = ASTSymbol sym } @@ -68,7 +66,7 @@ letArgsToSymValPairs d args = defineUserFunction :: Depth -> AST -> [AST] -> LContext LFunction defineUserFunction d AST { astNode = ASTSymbol param } exprs = return fn where fn :: LFunction - fn env arg = do + fn arg = do let replacedExprs = map (traverseAndReplace param arg) exprs let letExprs = take (length exprs - 1) replacedExprs letSymValPairs <- letExprs @@ -91,7 +89,7 @@ defineUserFunctionWithLetExprs d (param:[]) exprs = defineUserFunction d param exprs defineUserFunctionWithLetExprs d (AST { astNode = ASTSymbol param }:rest) exprs = return fn where fn :: LFunction - fn _ arg = do + fn arg = do let newExprs = map (traverseAndReplace param arg) exprs ret <- defineUserFunctionWithLetExprs d rest newExprs -- the returned AST will not have the correct position info, but that's fine @@ -171,8 +169,7 @@ evaluateLet d asts = do (symbol, value) <- letArgsToSymValPairs d args env <- getEnv when (M.member symbol env) $ throwL (astPos letAst) $ "symbol already defined: " ++ symbol - let newEnv = M.insert symbol value env - putEnv newEnv + insertEnv symbol value return $ letAst { astNode = ASTUnit } evaluateEnv :: Depth -> [AST] -> LContext AST @@ -242,8 +239,7 @@ evaluateUserFunction d children = do evaledArgs <- mapM (evaluate d) args doubleEvaledArgs <- mapM (evaluate d) evaledArgs - env <- getEnv - result <- curryCall env (reverse doubleEvaledArgs) fn + result <- curryCall (reverse doubleEvaledArgs) fn -- todo: maybe remove double eval here? can't remember why it was added return $ fnAst { astNode = astNode result } @@ -289,25 +285,6 @@ runScriptFile fileName = do src <- liftIO $ readFile fileName runInlineScript fileName src -getEnv :: LContext Env -getEnv = do - s <- get - return $ stateEnv s - -getConfig :: LContext Config -getConfig = do - s <- get - return $ stateConfig s - -putEnv :: Env -> LContext () -putEnv env = do - modify (\s -> s { stateEnv = env }) - -insertEnv :: String -> AST -> LContext () -insertEnv k v = do - env <- getEnv - putEnv $ M.insert k v env - getBuiltinState :: LContext LState getBuiltinState = do config <- getConfig diff --git a/src/Utils.hs b/src/Utils.hs index 6354a87..8ee2056 100644 --- a/src/Utils.hs +++ b/src/Utils.hs @@ -34,6 +34,25 @@ type LContext a = StateT LState (ExceptT LException IO) a runL :: LState -> LContext a -> IO (Either LException (a, LState)) runL s lc = runExceptT $ (flip runStateT) s lc +getEnv :: LContext Env +getEnv = do + s <- get + return $ stateEnv s + +getConfig :: LContext Config +getConfig = do + s <- get + return $ stateConfig s + +putEnv :: Env -> LContext () +putEnv env = do + modify (\s -> s { stateEnv = env }) + +insertEnv :: String -> AST -> LContext () +insertEnv k v = do + env <- getEnv + putEnv $ M.insert k v env + data Token = Token { tokenContent :: String, tokenRow :: Int, @@ -47,7 +66,7 @@ instance (Eq Token) where instance (Show Token) where show token = show $ tokenContent token -type LFunction = (Env -> AST -> LContext AST) +type LFunction = AST -> LContext AST data ASTNode = ASTInteger Int |
