DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / haskell / step3_env.hs
CommitLineData
5400d4bf 1import System.IO (hFlush, stdout)
87cb47ec 2import Control.Monad ((<=<))
53db2d63 3import Control.Monad.Except (runExceptT)
5400d4bf 4import Control.Monad.Trans (liftIO)
b76aa73b 5
219f15b7 6import Readline (addHistory, readline, load_history)
b76aa73b
JM
7import Types
8import Reader (read_str)
9import Printer (_pr_str)
52371c3e 10import Env (env_new, env_get, env_set)
b76aa73b
JM
11
12-- read
6116c2d5 13
5400d4bf 14mal_read :: String -> IOThrows MalVal
6116c2d5 15mal_read = read_str
b76aa73b
JM
16
17-- eval
6116c2d5
NB
18
19-- eval_ast is replaced with pattern matching.
20
21let_bind :: Env -> [MalVal] -> IOThrows ()
22let_bind _ [] = return ()
23let_bind env (MalSymbol b : e : xs) = do
24 liftIO . env_set env b =<< eval env e
b76aa73b 25 let_bind env xs
6116c2d5
NB
26let_bind _ _ = throwStr "invalid let*"
27
28apply_ast :: [MalVal] -> Env -> IOThrows MalVal
b76aa73b 29
6116c2d5 30apply_ast [] _ = return $ toList []
b76aa73b 31
6116c2d5
NB
32apply_ast [MalSymbol "def!", MalSymbol a1, a2] env = do
33 evd <- eval env a2
34 liftIO $ env_set env a1 evd
35 return evd
36apply_ast (MalSymbol "def!" : _) _ = throwStr "invalid def!"
37
38apply_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
42apply_ast (MalSymbol "let*" : _) _ = throwStr "invalid let*"
43
44apply_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
50eval :: Env -> MalVal -> IOThrows MalVal
51eval 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
56eval env (MalSeq _ (Vect False) xs) = apply_ast xs env
57eval env (MalSeq m (Vect True) xs) = MalSeq m (Vect True) <$> mapM (eval env) xs
58eval env (MalHashMap m xs) = MalHashMap m <$> mapM (eval env) xs
59eval _ ast = return ast
b76aa73b
JM
60
61-- print
6116c2d5 62
87cb47ec
NB
63mal_print :: MalVal -> IOThrows String
64mal_print = liftIO. Printer._pr_str True
b76aa73b
JM
65
66-- repl
6116c2d5
NB
67
68add :: Fn
c150ec41 69add [MalNumber a, MalNumber b] = return $ MalNumber $ a + b
5400d4bf 70add _ = throwStr $ "illegal arguments to +"
6116c2d5
NB
71
72sub :: Fn
c150ec41 73sub [MalNumber a, MalNumber b] = return $ MalNumber $ a - b
5400d4bf 74sub _ = throwStr $ "illegal arguments to -"
6116c2d5
NB
75
76mult :: Fn
c150ec41 77mult [MalNumber a, MalNumber b] = return $ MalNumber $ a * b
5400d4bf 78mult _ = throwStr $ "illegal arguments to *"
6116c2d5
NB
79
80divd :: Fn
c150ec41 81divd [MalNumber a, MalNumber b] = return $ MalNumber $ a `div` b
5400d4bf 82divd _ = throwStr $ "illegal arguments to /"
b76aa73b 83
5400d4bf 84rep :: Env -> String -> IOThrows String
87cb47ec 85rep env = mal_print <=< eval env <=< mal_read
b76aa73b
JM
86
87repl_loop :: Env -> IO ()
88repl_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
103defBuiltIn :: Env -> String -> Fn -> IO ()
104defBuiltIn env sym f =
105 env_set env sym $ MalFunction {fn=f, f_ast=Nil, f_params=[], macro=False, meta=Nil}
106
107main :: IO ()
b76aa73b 108main = 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