diff options
author | Akshay <[email protected]> | 2020-10-16 14:24:46 +0100 |
---|---|---|
committer | Akshay <[email protected]> | 2020-10-16 14:24:46 +0100 |
commit | b19a4a35db4cd951c52e179f3340518c9e2dcc1e (patch) | |
tree | 6d3d840a995d0467577ca645f5838a16d78aa3d6 /src | |
parent | 684e09298b8453ee56571d8a225e11d7c57e3746 (diff) |
add initial support for variable definition
Diffstat (limited to 'src')
-rw-r--r-- | src/Environment.hs | 69 | ||||
-rw-r--r-- | src/Error/Base.hs | 24 | ||||
-rw-r--r-- | src/Error/Pretty.hs | 9 | ||||
-rw-r--r-- | src/Evaluator.hs | 38 | ||||
-rw-r--r-- | src/Operators.hs | 4 | ||||
-rw-r--r-- | src/Parser.hs | 2 |
6 files changed, 113 insertions, 33 deletions
diff --git a/src/Environment.hs b/src/Environment.hs new file mode 100644 index 0000000..7b3f365 --- /dev/null +++ b/src/Environment.hs | |||
@@ -0,0 +1,69 @@ | |||
1 | module Environment ( Env | ||
2 | , setVar | ||
3 | , getVar | ||
4 | , defineVar | ||
5 | , manyBindings | ||
6 | , newEnv | ||
7 | , liftLispResult | ||
8 | , IOResult | ||
9 | ) where | ||
10 | |||
11 | import Control.Monad (liftM, mapM) | ||
12 | import Control.Monad.Except | ||
13 | import Data.IORef | ||
14 | import Data.Maybe (isJust) | ||
15 | import Error.Base (LispError (..), LispResult (..), unwrap) | ||
16 | import Parser (Expr (..)) | ||
17 | |||
18 | type Env = IORef [(String, IORef Expr)] | ||
19 | |||
20 | newEnv :: IO Env | ||
21 | newEnv = newIORef [] | ||
22 | |||
23 | type IOResult = ExceptT LispError IO | ||
24 | |||
25 | liftLispResult :: LispResult a -> IOResult a | ||
26 | liftLispResult (Left err) = throwError err | ||
27 | liftLispResult (Right val) = return val | ||
28 | |||
29 | isBound :: Env -> String -> IO Bool | ||
30 | isBound env var = do | ||
31 | ptr <- readIORef env | ||
32 | return $ isJust $ lookup var ptr | ||
33 | |||
34 | -- env modifiers | ||
35 | |||
36 | getVar :: Env -> String -> IOResult Expr | ||
37 | getVar env var = do | ||
38 | ptr <- liftIO $ readIORef env | ||
39 | maybe (throwError $ UnboundVariable var) | ||
40 | (liftIO . readIORef) | ||
41 | $ lookup var ptr | ||
42 | |||
43 | setVar :: Env -> String -> Expr -> IOResult () | ||
44 | setVar env var val = do | ||
45 | ptr <- liftIO $ readIORef env | ||
46 | maybe (throwError $ UnboundVariable var) | ||
47 | (liftIO . (flip writeIORef val)) | ||
48 | $ lookup var ptr | ||
49 | |||
50 | defineVar :: Env -> String -> Expr -> IOResult () | ||
51 | defineVar env var val = do | ||
52 | alreadyBound <- liftIO $ isBound env var | ||
53 | if alreadyBound | ||
54 | then setVar env var val | ||
55 | else liftIO $ do | ||
56 | newRef <- newIORef val | ||
57 | ptr <- readIORef env | ||
58 | writeIORef env $ (var, newRef):ptr | ||
59 | |||
60 | makeBind :: (String, Expr) -> IO (String, IORef Expr) | ||
61 | makeBind (var, val) = do | ||
62 | n <- newIORef val | ||
63 | return (var, n) | ||
64 | |||
65 | manyBindings :: Env -> [(String, Expr)] -> IO Env | ||
66 | manyBindings env binds = do | ||
67 | ptr <- readIORef env | ||
68 | extendedEnv <- liftM (++ ptr) $ mapM makeBind binds | ||
69 | newIORef extendedEnv | ||
diff --git a/src/Error/Base.hs b/src/Error/Base.hs index d7b685c..ef32d52 100644 --- a/src/Error/Base.hs +++ b/src/Error/Base.hs | |||
@@ -1,8 +1,7 @@ | |||
1 | module Error.Base ( | 1 | module Error.Base ( LispError (..) |
2 | LispError (..) | 2 | , LispResult (..) |
3 | , LispResult (..) | 3 | , unwrap |
4 | , unwrap | 4 | ) where |
5 | ) where | ||
6 | 5 | ||
7 | import Control.Monad.Except | 6 | import Control.Monad.Except |
8 | import Parser | 7 | import Parser |
@@ -15,19 +14,24 @@ data LispError = Parse ParseError | |||
15 | | ArgCount String Int [Expr] | 14 | | ArgCount String Int [Expr] |
16 | | UnknownFunction String | 15 | | UnknownFunction String |
17 | | TypeMismatch String Expr | 16 | | TypeMismatch String Expr |
17 | | UnboundVariable String | ||
18 | 18 | ||
19 | unwordsList :: [Expr] -> String | 19 | unwordsList :: [Expr] -> String |
20 | unwordsList = unwords . map show | 20 | unwordsList = unwords . map show |
21 | 21 | ||
22 | literal :: String -> String | ||
23 | literal v = "`" <> v <> "`" | ||
24 | |||
22 | instance Show LispError where | 25 | instance Show LispError where |
23 | show (Parse e) = "Parser Error: " ++ show e | 26 | show (Parse e) = "Parser Error: " ++ show e |
24 | show (BadForm s expr) = "Bad Form: " ++ s ++ ": " ++ show expr | 27 | show (BadForm s expr) = "Bad Form: " ++ literal s ++ ": " ++ show expr |
25 | -- TODO: clean this up | 28 | -- TODO: clean this up |
26 | show (ArgCount fn n es) | 29 | show (ArgCount fn n es) |
27 | | null es = "Invalid arity, `" ++ fn ++ "` expects " ++ show n ++ " or more expression(s)!" | 30 | | null es = "Invalid arity, " ++ literal fn ++ " expects " ++ show n ++ " or more expression(s)!" |
28 | | otherwise = "Invalid arity, `" ++ fn ++ "` expects " ++ show n ++ " or more expression(s), got value(s): " ++ unwordsList es | 31 | | otherwise = "Invalid arity, " ++ literal fn ++ " expects " ++ show n ++ " or more expression(s), got value(s): " ++ unwordsList es |
29 | show (UnknownFunction fn) = "Cannot apply function: " ++ fn | 32 | show (UnknownFunction fn) = "Cannot apply function: " ++ literal fn |
30 | show (TypeMismatch msg got) = "Type mismatch, expected " ++ msg ++ ", got: " ++ show got | 33 | show (TypeMismatch msg got) = "Type mismatch, expected " ++ literal msg ++ ", got: " ++ show got |
34 | show (UnboundVariable name) = "Possibly unbound variable: " ++ literal name | ||
31 | 35 | ||
32 | type LispResult = Either LispError | 36 | type LispResult = Either LispError |
33 | 37 | ||
diff --git a/src/Error/Pretty.hs b/src/Error/Pretty.hs index 44601d6..a90131b 100644 --- a/src/Error/Pretty.hs +++ b/src/Error/Pretty.hs | |||
@@ -1,8 +1,7 @@ | |||
1 | module Error.Pretty ( | 1 | module Error.Pretty ( showError |
2 | showError | 2 | , Options (..) |
3 | , Options (..) | 3 | , defaults |
4 | , defaults | 4 | ) where |
5 | ) where | ||
6 | 5 | ||
7 | import Data.List (intercalate, nub) | 6 | import Data.List (intercalate, nub) |
8 | import Error.Base (LispError (..)) | 7 | import Error.Base (LispError (..)) |
diff --git a/src/Evaluator.hs b/src/Evaluator.hs index 8a5274f..e3bf697 100644 --- a/src/Evaluator.hs +++ b/src/Evaluator.hs | |||
@@ -1,8 +1,7 @@ | |||
1 | module Evaluator ( | 1 | module Evaluator (eval) where |
2 | eval | ||
3 | ) where | ||
4 | 2 | ||
5 | import Control.Monad.Except | 3 | import Control.Monad.Except |
4 | import Environment | ||
6 | import Error.Base (LispError (..), LispResult (..), | 5 | import Error.Base (LispError (..), LispResult (..), |
7 | unwrap) | 6 | unwrap) |
8 | import Operators | 7 | import Operators |
@@ -15,18 +14,27 @@ apply fn args = maybe | |||
15 | ($ args) | 14 | ($ args) |
16 | (lookup fn primitives) | 15 | (lookup fn primitives) |
17 | 16 | ||
18 | eval :: Expr -> LispResult Expr | 17 | eval :: Env -> Expr -> IOResult Expr |
19 | eval v@(StringLiteral s) = return v | 18 | eval _ v@(StringLiteral s) = return v |
20 | eval v@(IntLiteral i) = return v | 19 | eval _ v@(IntLiteral i) = return v |
21 | eval v@(BoolLiteral b) = return v | 20 | eval _ v@(BoolLiteral b) = return v |
22 | eval v@(FloatLiteral f) = return v | 21 | eval env (Id l) = getVar env l |
23 | eval v@(Vector xs) = liftM Vector $ mapM eval xs | 22 | eval _ v@(FloatLiteral f) = return v |
24 | -- handle quotes as literals | 23 | eval env v@(Vector xs) = liftM Vector $ mapM (eval env) xs |
25 | eval (List[Id "quote", val]) = return val | 24 | eval env (List[Id "quote", val]) = return val |
26 | eval (List[Id "quasiquote", val]) = undefined | 25 | eval env (List[Id "quasiquote", val]) = undefined |
27 | eval (List[Id "unquote", val]) = undefined | 26 | eval env (List[Id "unquote", val]) = eval env val |
28 | eval (List (Id fn : args)) = mapM eval args >>= apply fn | 27 | eval env (List [Id "set!", Id var, val]) = do |
28 | e <- eval env val | ||
29 | setVar env var e | ||
30 | return e | ||
31 | eval env (List [Id "define", Id var, val]) = do | ||
32 | e <- eval env val | ||
33 | defineVar env var e | ||
34 | return e | ||
35 | eval env (List (Id fn : args)) = mapM (eval env) args >>= liftLispResult . apply fn | ||
36 | eval env NoReturn = throwError $ BadForm "Invalid usage of non-returning expression" NoReturn | ||
29 | 37 | ||
30 | -- handle bad forms | 38 | -- handle bad forms |
31 | eval invalidForm = throwError $ BadForm "lisk can't recognize this form" invalidForm | 39 | eval env invalidForm = throwError $ BadForm "lisk can't recognize this form" invalidForm |
32 | 40 | ||
diff --git a/src/Operators.hs b/src/Operators.hs index c5812ea..fa39e23 100644 --- a/src/Operators.hs +++ b/src/Operators.hs | |||
@@ -1,6 +1,4 @@ | |||
1 | module Operators ( | 1 | module Operators (primitives) where |
2 | primitives | ||
3 | ) where | ||
4 | 2 | ||
5 | import Control.Monad.Except | 3 | import Control.Monad.Except |
6 | import Error.Base (LispError (..), LispResult (..)) | 4 | import Error.Base (LispError (..), LispResult (..)) |
diff --git a/src/Parser.hs b/src/Parser.hs index 115203b..a7f5571 100644 --- a/src/Parser.hs +++ b/src/Parser.hs | |||
@@ -22,6 +22,7 @@ data Expr = List [Expr] | |||
22 | | FloatLiteral Double | 22 | | FloatLiteral Double |
23 | | BoolLiteral Bool | 23 | | BoolLiteral Bool |
24 | | Id String | 24 | | Id String |
25 | | NoReturn | ||
25 | deriving (Eq) | 26 | deriving (Eq) |
26 | 27 | ||
27 | -- backslash double quote escapes a quote inside strings | 28 | -- backslash double quote escapes a quote inside strings |
@@ -127,3 +128,4 @@ instance Show Expr where | |||
127 | show (BoolLiteral True) = "#t" | 128 | show (BoolLiteral True) = "#t" |
128 | show (BoolLiteral False) = "#f" | 129 | show (BoolLiteral False) = "#f" |
129 | show (Id i) = i | 130 | show (Id i) = i |
131 | show NoReturn = ";;; environment extension" | ||