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