Commit | Line | Data |
---|---|---|
5400d4bf | 1 | import System.IO (hFlush, stdout) |
87cb47ec | 2 | import Control.Monad ((<=<)) |
53db2d63 | 3 | import Control.Monad.Except (runExceptT) |
5400d4bf | 4 | import Control.Monad.Trans (liftIO) |
b76aa73b | 5 | |
219f15b7 | 6 | import Readline (addHistory, readline, load_history) |
b76aa73b JM |
7 | import Types |
8 | import Reader (read_str) | |
9 | import Printer (_pr_str) | |
52371c3e | 10 | import Env (env_new, env_get, env_set) |
b76aa73b JM |
11 | |
12 | -- read | |
6116c2d5 | 13 | |
5400d4bf | 14 | mal_read :: String -> IOThrows MalVal |
6116c2d5 | 15 | mal_read = read_str |
b76aa73b JM |
16 | |
17 | -- eval | |
6116c2d5 NB |
18 | |
19 | -- eval_ast is replaced with pattern matching. | |
20 | ||
21 | let_bind :: Env -> [MalVal] -> IOThrows () | |
22 | let_bind _ [] = return () | |
23 | let_bind env (MalSymbol b : e : xs) = do | |
24 | liftIO . env_set env b =<< eval env e | |
b76aa73b | 25 | let_bind env xs |
6116c2d5 NB |
26 | let_bind _ _ = throwStr "invalid let*" |
27 | ||
28 | apply_ast :: [MalVal] -> Env -> IOThrows MalVal | |
b76aa73b | 29 | |
6116c2d5 | 30 | apply_ast [] _ = return $ toList [] |
b76aa73b | 31 | |
6116c2d5 NB |
32 | apply_ast [MalSymbol "def!", MalSymbol a1, a2] env = do |
33 | evd <- eval env a2 | |
34 | liftIO $ env_set env a1 evd | |
35 | return evd | |
36 | apply_ast (MalSymbol "def!" : _) _ = throwStr "invalid def!" | |
37 | ||
38 | apply_ast [MalSymbol "let*", MalSeq _ _ params, a2] env = do | |
39 | let_env <- liftIO $ env_new env | |
40 | let_bind let_env params | |
41 | eval let_env a2 | |
42 | apply_ast (MalSymbol "let*" : _) _ = throwStr "invalid let*" | |
43 | ||
44 | apply_ast ast env = do | |
45 | evd <- mapM (eval env) ast | |
46 | case evd of | |
47 | MalFunction {fn=f} : args -> f args | |
87cb47ec | 48 | _ -> throwStr . (++) "invalid apply: " =<< liftIO (Printer._pr_str True (toList ast)) |
6116c2d5 NB |
49 | |
50 | eval :: Env -> MalVal -> IOThrows MalVal | |
51 | eval env (MalSymbol sym) = do | |
52 | maybeVal <- liftIO $ env_get env sym | |
53 | case maybeVal of | |
54 | Nothing -> throwStr $ "'" ++ sym ++ "' not found" | |
55 | Just val -> return val | |
56 | eval env (MalSeq _ (Vect False) xs) = apply_ast xs env | |
57 | eval env (MalSeq m (Vect True) xs) = MalSeq m (Vect True) <$> mapM (eval env) xs | |
58 | eval env (MalHashMap m xs) = MalHashMap m <$> mapM (eval env) xs | |
59 | eval _ ast = return ast | |
b76aa73b JM |
60 | |
61 | ||
6116c2d5 | 62 | |
87cb47ec NB |
63 | mal_print :: MalVal -> IOThrows String |
64 | mal_print = liftIO. Printer._pr_str True | |
b76aa73b JM |
65 | |
66 | -- repl | |
6116c2d5 NB |
67 | |
68 | add :: Fn | |
c150ec41 | 69 | add [MalNumber a, MalNumber b] = return $ MalNumber $ a + b |
5400d4bf | 70 | add _ = throwStr $ "illegal arguments to +" |
6116c2d5 NB |
71 | |
72 | sub :: Fn | |
c150ec41 | 73 | sub [MalNumber a, MalNumber b] = return $ MalNumber $ a - b |
5400d4bf | 74 | sub _ = throwStr $ "illegal arguments to -" |
6116c2d5 NB |
75 | |
76 | mult :: Fn | |
c150ec41 | 77 | mult [MalNumber a, MalNumber b] = return $ MalNumber $ a * b |
5400d4bf | 78 | mult _ = throwStr $ "illegal arguments to *" |
6116c2d5 NB |
79 | |
80 | divd :: Fn | |
c150ec41 | 81 | divd [MalNumber a, MalNumber b] = return $ MalNumber $ a `div` b |
5400d4bf | 82 | divd _ = throwStr $ "illegal arguments to /" |
b76aa73b | 83 | |
5400d4bf | 84 | rep :: Env -> String -> IOThrows String |
87cb47ec | 85 | rep env = mal_print <=< eval env <=< mal_read |
b76aa73b JM |
86 | |
87 | repl_loop :: Env -> IO () | |
88 | repl_loop env = do | |
fa9a9758 JM |
89 | line <- readline "user> " |
90 | case line of | |
91 | Nothing -> return () | |
92 | Just "" -> repl_loop env | |
93 | Just str -> do | |
219f15b7 | 94 | addHistory str |
53db2d63 | 95 | res <- runExceptT $ rep env str |
5400d4bf | 96 | out <- case res of |
87cb47ec | 97 | Left mv -> (++) "Error: " <$> liftIO (Printer._pr_str True mv) |
5400d4bf | 98 | Right val -> return val |
fa9a9758 | 99 | putStrLn out |
5400d4bf | 100 | hFlush stdout |
fa9a9758 | 101 | repl_loop env |
b76aa73b | 102 | |
6116c2d5 NB |
103 | defBuiltIn :: Env -> String -> Fn -> IO () |
104 | defBuiltIn env sym f = | |
105 | env_set env sym $ MalFunction {fn=f, f_ast=Nil, f_params=[], macro=False, meta=Nil} | |
106 | ||
107 | main :: IO () | |
b76aa73b | 108 | main = do |
fa9a9758 | 109 | load_history |
c150ec41 | 110 | |
6116c2d5 NB |
111 | repl_env <- env_new [] |
112 | ||
113 | defBuiltIn repl_env "+" add | |
114 | defBuiltIn repl_env "-" sub | |
115 | defBuiltIn repl_env "*" mult | |
116 | defBuiltIn repl_env "/" divd | |
117 | ||
b76aa73b | 118 | repl_loop repl_env |