Commit | Line | Data |
---|---|---|
5400d4bf JM |
1 | import System.IO (hFlush, stdout) |
2 | import Control.Monad (mapM) | |
53db2d63 | 3 | import Control.Monad.Except (runExceptT) |
b76aa73b JM |
4 | import qualified Data.Map as Map |
5 | import qualified Data.Traversable as DT | |
6 | ||
fa9a9758 | 7 | import Readline (readline, load_history) |
b76aa73b JM |
8 | import Types |
9 | import Reader (read_str) | |
10 | import Printer (_pr_str) | |
11 | ||
12 | -- read | |
5400d4bf | 13 | mal_read :: String -> IOThrows MalVal |
b76aa73b JM |
14 | mal_read str = read_str str |
15 | ||
16 | -- eval | |
5400d4bf | 17 | eval_ast :: MalVal -> (Map.Map String MalVal) -> IOThrows MalVal |
b76aa73b JM |
18 | eval_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 | 22 | eval_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 |
25 | eval_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 |
28 | eval_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 |
31 | eval_ast ast env = return ast |
32 | ||
5400d4bf | 33 | apply_ast :: MalVal -> (Map.Map String MalVal) -> IOThrows MalVal |
efa2daef JM |
34 | apply_ast ast@(MalList [] _) env = do |
35 | return ast | |
c150ec41 | 36 | apply_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 | 44 | eval :: MalVal -> (Map.Map String MalVal) -> IOThrows MalVal |
b76aa73b JM |
45 | eval ast env = do |
46 | case ast of | |
c150ec41 | 47 | (MalList _ _) -> apply_ast ast env |
b76aa73b JM |
48 | _ -> eval_ast ast env |
49 | ||
50 | ||
51 | ||
52 | mal_print :: MalVal -> String | |
53 | mal_print exp = show exp | |
54 | ||
55 | -- repl | |
c150ec41 | 56 | add [MalNumber a, MalNumber b] = return $ MalNumber $ a + b |
5400d4bf | 57 | add _ = throwStr $ "illegal arguments to +" |
c150ec41 | 58 | sub [MalNumber a, MalNumber b] = return $ MalNumber $ a - b |
5400d4bf | 59 | sub _ = throwStr $ "illegal arguments to -" |
c150ec41 | 60 | mult [MalNumber a, MalNumber b] = return $ MalNumber $ a * b |
5400d4bf | 61 | mult _ = throwStr $ "illegal arguments to *" |
c150ec41 | 62 | divd [MalNumber a, MalNumber b] = return $ MalNumber $ a `div` b |
5400d4bf | 63 | divd _ = throwStr $ "illegal arguments to /" |
b76aa73b JM |
64 | |
65 | repl_env :: Map.Map String MalVal | |
fa9a9758 JM |
66 | repl_env = Map.fromList [("+", _func add), |
67 | ("-", _func sub), | |
68 | ("*", _func mult), | |
69 | ("/", _func divd)] | |
b76aa73b | 70 | |
5400d4bf | 71 | rep :: String -> IOThrows String |
b76aa73b JM |
72 | rep line = do |
73 | ast <- mal_read line | |
74 | exp <- eval ast repl_env | |
75 | return $ mal_print exp | |
76 | ||
77 | repl_loop :: IO () | |
78 | repl_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 |
93 | main = do |
94 | load_history | |
95 | repl_loop |