1 import System
.IO (hFlush, stdout)
2 import Control
.Monad
((<=<))
3 import Control
.Monad
.Except
(runExceptT
)
4 import Control
.Monad
.Trans
(liftIO
)
5 import Data
.Foldable
(foldlM
)
7 import Readline
(addHistory
, readline
, load_history
)
9 import Reader
(read_str
)
10 import Printer
(_pr_str
)
11 import Env
(env_new
, env_bind
, env_get
, env_set
)
16 mal_read
:: String -> IOThrows MalVal
21 -- eval_ast is replaced with pattern matching.
23 let_bind
:: Env
-> [MalVal
] -> IOThrows
()
24 let_bind _
[] = return ()
25 let_bind env
(MalSymbol b
: e
: xs
) = do
26 liftIO
. env_set env b
=<< eval env e
28 let_bind _ _
= throwStr
"invalid let*"
30 unWrapSymbol
:: MalVal
-> IOThrows
String
31 unWrapSymbol
(MalSymbol s
) = return s
32 unWrapSymbol _
= throwStr
"fn* parameter must be symbols"
34 newFunction
:: MalVal
-> Env
-> [String] -> MalVal
35 newFunction a env p
= MalFunction
{f_ast
=a
, f_params
=p
, macro
=False, meta
=Nil
,
37 fn_env
<- liftIO
$ env_new env
38 ok
<- liftIO
$ env_bind fn_env p args
41 False -> throwStr
$ "actual parameters do not match signature " ++ show p
)}
43 apply_ast
:: [MalVal
] -> Env
-> IOThrows MalVal
45 apply_ast
[] _
= return $ toList
[]
47 apply_ast
[MalSymbol
"def!", MalSymbol a1
, a2
] env
= do
49 liftIO
$ env_set env a1 evd
51 apply_ast
(MalSymbol
"def!" : _
) _
= throwStr
"invalid def!"
53 apply_ast
[MalSymbol
"let*", MalSeq _ _ params
, a2
] env
= do
54 let_env
<- liftIO
$ env_new env
55 let_bind let_env params
57 apply_ast
(MalSymbol
"let*" : _
) _
= throwStr
"invalid let*"
59 apply_ast
(MalSymbol
"do" : args
) env
= foldlM
(const $ eval env
) Nil args
61 apply_ast
[MalSymbol
"if", a1
, a2
, a3
] env
= do
63 eval env
$ case cond
of
65 MalBoolean
False -> a3
67 apply_ast
[MalSymbol
"if", a1
, a2
] env
= do
71 MalBoolean
False -> return Nil
73 apply_ast
(MalSymbol
"if" : _
) _
= throwStr
"invalid if"
75 apply_ast
[MalSymbol
"fn*", MalSeq _ _ params
, ast
] env
= newFunction ast env
<$> mapM unWrapSymbol params
76 apply_ast
(MalSymbol
"fn*" : _
) _
= throwStr
"invalid fn*"
78 apply_ast ast env
= do
79 evd
<- mapM (eval env
) ast
81 MalFunction
{fn
=f
} : args
-> f args
82 _
-> throwStr
. (++) "invalid apply: " =<< liftIO
(Printer
._pr_str
True (toList ast
))
84 eval
:: Env
-> MalVal
-> IOThrows MalVal
85 eval env
(MalSymbol sym
) = do
86 maybeVal
<- liftIO
$ env_get env sym
88 Nothing
-> throwStr
$ "'" ++ sym
++ "' not found"
89 Just val
-> return val
90 eval env
(MalSeq _
(Vect
False) xs
) = apply_ast xs env
91 eval env
(MalSeq m
(Vect
True) xs
) = MalSeq m
(Vect
True) <$> mapM (eval env
) xs
92 eval env
(MalHashMap m xs
) = MalHashMap m
<$> mapM (eval env
) xs
93 eval _ ast
= return ast
97 mal_print
:: MalVal
-> IOThrows
String
98 mal_print
= liftIO
. Printer
._pr_str
True
102 rep
:: Env
-> String -> IOThrows
String
103 rep env
= mal_print
<=< eval env
<=< mal_read
105 repl_loop
:: Env
-> IO ()
107 line
<- readline
"user> "
110 Just
"" -> repl_loop env
113 res
<- runExceptT
$ rep env str
115 Left mv
-> (++) "Error: " <$> liftIO
(Printer
._pr_str
True mv
)
116 Right val
-> return val
121 -- Read and evaluate a line. Ignore successful results, but crash in
122 -- case of error. This is intended for the startup procedure.
123 re
:: Env
-> String -> IO ()
124 re repl_env line
= do
125 res
<- runExceptT
$ eval repl_env
=<< mal_read line
127 Left mv
-> error . (++) "Startup failed: " <$> Printer
._pr_str
True mv
130 defBuiltIn
:: Env
-> (String, Fn
) -> IO ()
131 defBuiltIn env
(sym
, f
) =
132 env_set env sym
$ MalFunction
{fn
=f
, f_ast
=Nil
, f_params
=[], macro
=False, meta
=Nil
}
138 repl_env
<- env_new
[]
140 -- core.hs: defined using Haskell
141 mapM_ (defBuiltIn repl_env
) Core
.ns
143 -- core.mal: defined using the language itself
144 re repl_env
"(def! not (fn* (a) (if a false true)))"