Merge pull request #377 from asarhaddon/fix-runtests-pre-eval
[jackhill/mal.git] / haskell / step2_eval.hs
CommitLineData
5400d4bf
JM
1import System.IO (hFlush, stdout)
2import Control.Monad (mapM)
53db2d63 3import Control.Monad.Except (runExceptT)
b76aa73b
JM
4import qualified Data.Map as Map
5import qualified Data.Traversable as DT
6
fa9a9758 7import Readline (readline, load_history)
b76aa73b
JM
8import Types
9import Reader (read_str)
10import Printer (_pr_str)
11
12-- read
5400d4bf 13mal_read :: String -> IOThrows MalVal
b76aa73b
JM
14mal_read str = read_str str
15
16-- eval
5400d4bf 17eval_ast :: MalVal -> (Map.Map String MalVal) -> IOThrows MalVal
b76aa73b
JM
18eval_ast (MalSymbol sym) env = do
19 case Map.lookup sym env of
5400d4bf 20 Nothing -> throwStr $ "'" ++ sym ++ "' not found"
b76aa73b 21 Just v -> return v
c150ec41 22eval_ast ast@(MalList lst m) env = do
b76aa73b 23 new_lst <- mapM (\x -> (eval x env)) lst
c150ec41
JM
24 return $ MalList new_lst m
25eval_ast ast@(MalVector lst m) env = do
b76aa73b 26 new_lst <- mapM (\x -> (eval x env)) lst
c150ec41
JM
27 return $ MalVector new_lst m
28eval_ast ast@(MalHashMap lst m) env = do
b76aa73b 29 new_hm <- DT.mapM (\x -> (eval x env)) lst
c150ec41 30 return $ MalHashMap new_hm m
b76aa73b
JM
31eval_ast ast env = return ast
32
5400d4bf 33apply_ast :: MalVal -> (Map.Map String MalVal) -> IOThrows MalVal
efa2daef
JM
34apply_ast ast@(MalList [] _) env = do
35 return ast
c150ec41 36apply_ast ast@(MalList _ _) env = do
b76aa73b
JM
37 el <- eval_ast ast env
38 case el of
c150ec41 39 (MalList ((Func (Fn f) _) : rest) _) ->
fa9a9758 40 f $ rest
b76aa73b 41 el ->
5400d4bf 42 throwStr $ "invalid apply: " ++ (show el)
b76aa73b 43
5400d4bf 44eval :: MalVal -> (Map.Map String MalVal) -> IOThrows MalVal
b76aa73b
JM
45eval ast env = do
46 case ast of
c150ec41 47 (MalList _ _) -> apply_ast ast env
b76aa73b
JM
48 _ -> eval_ast ast env
49
50
51-- print
52mal_print :: MalVal -> String
53mal_print exp = show exp
54
55-- repl
c150ec41 56add [MalNumber a, MalNumber b] = return $ MalNumber $ a + b
5400d4bf 57add _ = throwStr $ "illegal arguments to +"
c150ec41 58sub [MalNumber a, MalNumber b] = return $ MalNumber $ a - b
5400d4bf 59sub _ = throwStr $ "illegal arguments to -"
c150ec41 60mult [MalNumber a, MalNumber b] = return $ MalNumber $ a * b
5400d4bf 61mult _ = throwStr $ "illegal arguments to *"
c150ec41 62divd [MalNumber a, MalNumber b] = return $ MalNumber $ a `div` b
5400d4bf 63divd _ = throwStr $ "illegal arguments to /"
b76aa73b
JM
64
65repl_env :: Map.Map String MalVal
fa9a9758
JM
66repl_env = Map.fromList [("+", _func add),
67 ("-", _func sub),
68 ("*", _func mult),
69 ("/", _func divd)]
b76aa73b 70
5400d4bf 71rep :: String -> IOThrows String
b76aa73b
JM
72rep line = do
73 ast <- mal_read line
74 exp <- eval ast repl_env
75 return $ mal_print exp
76
77repl_loop :: IO ()
78repl_loop = do
fa9a9758
JM
79 line <- readline "user> "
80 case line of
81 Nothing -> return ()
82 Just "" -> repl_loop
83 Just str -> do
53db2d63 84 res <- runExceptT $ rep str
5400d4bf
JM
85 out <- case res of
86 Left (StringError str) -> return $ "Error: " ++ str
87 Left (MalValError mv) -> return $ "Error: " ++ (show mv)
88 Right val -> return val
fa9a9758 89 putStrLn out
5400d4bf 90 hFlush stdout
fa9a9758 91 repl_loop
b76aa73b 92
fa9a9758
JM
93main = do
94 load_history
95 repl_loop