1
1
mirror of https://github.com/kanaka/mal.git synced 2024-10-26 22:28:26 +03:00
mal/impls/sml/stepA_mal.sml

160 lines
7.1 KiB
Standard ML
Raw Normal View History

2021-04-05 22:22:32 +03:00
fun read s =
readStr s
2021-04-06 20:50:25 +03:00
fun eval e ast = eval' e (expandMacro e [ast])
2021-04-05 22:22:32 +03:00
2021-04-06 20:50:25 +03:00
and eval' e (LIST (a::args, _)) = (case specialEval a of SOME special => special e args | _ => evalApply e (eval e a) args)
| eval' e (SYMBOL s) = evalSymbol e s
| eval' e (VECTOR (v,_)) = VECTOR (map (eval e) v, NO_META)
| eval' e (MAP (m,_)) = MAP (List.map (fn (k, v) => (k, eval e v)) m, NO_META)
2021-04-06 20:50:25 +03:00
| eval' e ast = ast
2021-04-05 22:22:32 +03:00
and specialEval (SYMBOL "def!") = SOME evalDef
| specialEval (SYMBOL "let*") = SOME evalLet
| specialEval (SYMBOL "do") = SOME evalDo
| specialEval (SYMBOL "if") = SOME evalIf
| specialEval (SYMBOL "fn*") = SOME evalFn
| specialEval (SYMBOL "quote") = SOME evalQuote
| specialEval (SYMBOL "quasiquote") = SOME evalQuasiquote
| specialEval (SYMBOL "quasiquoteexpand") = SOME (fn _ => expandQuasiquote)
| specialEval (SYMBOL "defmacro!") = SOME evalDefmacro
| specialEval (SYMBOL "macroexpand") = SOME expandMacro
| specialEval (SYMBOL "try*") = SOME evalTry
| specialEval _ = NONE
and evalDef e [SYMBOL s, ast] = let val v = eval e ast in (def s v e; v) end
2021-04-06 20:50:25 +03:00
| evalDef _ _ = raise NotApplicable "def! needs a symbol and a form to evaluate"
2021-04-05 22:22:32 +03:00
2021-04-06 20:50:25 +03:00
and evalLet e [LIST (bs,_), ast] = eval (bindLet bs (inside e)) ast
| evalLet e [VECTOR (bs,_), ast] = eval (bindLet bs (inside e)) ast
| evalLet _ _ = raise NotApplicable "let* needs a list of bindings and a form to evaluate"
2021-04-05 22:22:32 +03:00
and evalDo e (x::xs) = foldl (fn (x, _) => eval e x) (eval e x) xs
2021-04-06 20:50:25 +03:00
| evalDo _ _ = raise NotApplicable "do needs at least one argument"
2021-04-05 22:22:32 +03:00
and evalIf e [c,a,b] = if truthy (eval e c) then eval e a else eval e b
| evalIf e [c,a] = evalIf e [c,a,NIL]
2021-04-06 20:50:25 +03:00
| evalIf _ _ = raise NotApplicable "if needs two or three arguments"
2021-04-05 22:22:32 +03:00
2021-04-06 20:50:25 +03:00
and evalFn e [(LIST (binds,_)),body] = makeFn e binds body
| evalFn e [(VECTOR (binds,_)),body] = makeFn e binds body
| evalFn _ _ = raise NotApplicable "fn* needs a list of bindings and a body"
and makeFn e binds body = FN (fn (exprs) => eval (bind (interleave binds exprs) (inside e)) body, NO_META)
2021-04-05 22:22:32 +03:00
and evalQuote e [x] = x
2021-04-06 20:50:25 +03:00
| evalQuote _ _ = raise NotApplicable "quote needs one argument"
2021-04-05 22:22:32 +03:00
and evalQuasiquote e args = eval e (expandQuasiquote args)
2021-04-06 20:50:25 +03:00
and expandQuasiquote [LIST ([SYMBOL "unquote", x],_)] = x
2021-04-07 12:46:12 +03:00
| expandQuasiquote [LIST (l,_)] = malList (foldr quasiFolder [] l)
| expandQuasiquote [VECTOR (v,_)] = malList [SYMBOL "vec", malList (foldr quasiFolder [] v)]
| expandQuasiquote [m as MAP _] = malList [SYMBOL "quote", m]
| expandQuasiquote [s as SYMBOL _] = malList [SYMBOL "quote", s]
2021-04-05 22:22:32 +03:00
| expandQuasiquote [x] = x
2021-04-06 20:50:25 +03:00
| expandQuasiquote _ = raise NotApplicable "quasiquote needs one argument"
2021-04-07 12:46:12 +03:00
and quasiFolder (LIST ([SYMBOL "splice-unquote", x],_), acc) = [SYMBOL "concat", x, malList acc]
| quasiFolder (x, acc) = [SYMBOL "cons", expandQuasiquote [x], malList acc]
2021-04-05 22:22:32 +03:00
2021-04-06 22:52:26 +03:00
and evalDefmacro e [SYMBOL s, ast] = defMacro e s (eval e ast)
| evalDefmacro _ _ = raise NotApplicable "defmacro! needs a name, and a fn*"
and defMacro e s (FN (f,_)) = let val m = MACRO f in (def s m e; m) end
| defMacro _ _ _ = raise NotApplicable "defmacro! needs a name, and a fn*"
2021-04-05 22:22:32 +03:00
2021-04-06 20:50:25 +03:00
and expandMacro e [(ast as LIST (SYMBOL s::args, _))] = (case lookup e s of SOME (MACRO m) => m args | _ => ast)
| expandMacro _ [ast] = ast
2021-04-05 22:22:32 +03:00
| expandMacro _ _ = raise NotApplicable "macroexpand needs one argument"
2021-04-06 20:50:25 +03:00
and evalTry e [a, LIST ([SYMBOL "catch*", b, c],_)] = (eval e a handle ex => evalCatch (inside e) b ex c)
| evalTry e [a] = eval e a
2021-04-05 22:22:32 +03:00
| evalTry _ _ = raise NotApplicable "try* needs a form to evaluate"
and evalCatch e b ex body = eval (bind [b, exnVal ex] e) body
and exnVal (MalException x) = x
| exnVal (NotDefined msg) = STRING msg
| exnVal (NotApplicable msg) = STRING msg
| exnVal (OutOfBounds msg) = STRING msg
| exnVal exn = STRING (exnMessage exn)
2021-04-06 20:50:25 +03:00
and evalApply e (FN (f,_)) args = f (map (eval e) args)
2021-04-07 12:46:12 +03:00
| evalApply _ x args = raise NotApplicable (prStr x ^ " is not applicable on " ^ prStr (malList args))
2021-04-05 22:22:32 +03:00
and evalSymbol e s = valOrElse (lookup e s)
(fn _ => raise NotDefined ("'" ^ s ^ "' not found"))
and bindLet args e = bind' (eval e) args e
and bind args e = bind' identity args e
2021-04-07 12:46:12 +03:00
and bind' evl (SYMBOL "&"::v::(SYMBOL s)::vs) e = (def s (malList (map evl (v::vs))) e; e)
| bind' _ [SYMBOL "&", SYMBOL s] e = (def s (malList []) e; e)
2021-04-05 22:22:32 +03:00
| bind' evl (SYMBOL s::v::rest) e = (def s (evl v) e; bind' evl rest e)
| bind' _ [] e = e
| bind' _ _ _ = raise NotApplicable "bindings must be a list of symbol/form pairs"
fun print f =
prReadableStr f
fun rep e s =
s |> read |> eval e |> print
handle Nothing => ""
| SyntaxError msg => "SYNTAX ERROR: " ^ msg
| NotApplicable msg => "CANNOT APPLY: " ^ msg
| NotDefined msg => "NOT DEFINED: " ^ msg
| MalException e => "ERROR: " ^ (prStr e)
| e => "ERROR: " ^ (exnMessage e)
2021-04-05 22:47:32 +03:00
val replEnv = ENV (NS (ref [])) |> bind coreNs
2021-04-05 22:22:32 +03:00
fun repl e =
let open TextIO
in (
print("user> ");
case inputLine(stdIn) of
SOME(line) =>
let val s = rep e line
val _ = print(s ^ "\n")
in
repl e
end
| NONE => ()
) end
val prelude = " \
\\
\(def! not (fn* (a) (if a false true))) \
\\
\(def! \
\ load-file \
\ (fn* (f) \
\ (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))\
\\
\(defmacro! \
\ cond \
\ (fn* (& xs) \
\ (if (> (count xs) 0) \
\ (list 'if (first xs) \
\ (if (> (count xs) 1) \
\ (nth xs 1) \
\ (throw \"odd number of forms to cond\")) \
\ (cons 'cond (rest (rest xs)))))))"
fun main () = (
2021-04-07 13:28:58 +03:00
def "*host-language*" (STRING "sml") replEnv;
2021-04-05 22:22:32 +03:00
bind [
SYMBOL "eval",
2021-04-05 22:47:32 +03:00
FN (fn ([x]) => eval replEnv x
2021-04-06 20:50:25 +03:00
| _ => raise NotApplicable "'eval' requires one argument", NO_META)
2021-04-05 22:47:32 +03:00
] replEnv;
rep replEnv ("(do " ^ prelude ^ " nil)");
2021-04-05 22:22:32 +03:00
case CommandLine.arguments () of
prog::args => (
2021-04-07 12:46:12 +03:00
def "*ARGV*" (malList (map STRING args)) replEnv;
2021-04-05 22:47:32 +03:00
rep replEnv ("(load-file \"" ^ prog ^ "\")");
2021-04-05 22:22:32 +03:00
()
)
| args => (
2021-04-07 12:46:12 +03:00
def "*ARGV*" (malList (map STRING args)) replEnv;
2021-04-05 22:47:32 +03:00
rep replEnv "(println (str \"Mal [\" *host-language* \"]\"))";
repl replEnv
2021-04-05 22:22:32 +03:00
)
)