8 let MalExceptionObj = ""
15 return SequentialQ(a:obj) && !EmptyQ(a:obj)
18 function Quasiquote(ast)
20 return ListNew([SymbolNew("quote"), a:ast])
22 let a0 = ListFirst(a:ast)
23 if SymbolQ(a0) && a0.val == "unquote"
24 return ListNth(a:ast, 1)
25 elseif PairQ(a0) && SymbolQ(ListFirst(a0)) && ListFirst(a0).val == "splice-unquote"
26 return ListNew([SymbolNew("concat"), ListNth(a0, 1), Quasiquote(ListRest(a:ast))])
28 return ListNew([SymbolNew("cons"), Quasiquote(a0), Quasiquote(ListRest(a:ast))])
32 function IsMacroCall(ast, env)
36 let a0 = ListFirst(a:ast)
40 let macroname = a0.val
41 if empty(a:env.find(macroname))
44 return MacroQ(a:env.get(macroname))
47 function MacroExpand(ast, env)
49 while IsMacroCall(ast, a:env)
50 let macroobj = a:env.get(ListFirst(ast).val)
51 let macroargs = ListRest(ast)
52 let ast = FuncInvoke(macroobj, macroargs)
57 function EvalAst(ast, env)
59 let varname = a:ast.val
60 return a:env.get(varname)
62 return ListNew(map(copy(a:ast.val), {_, e -> EVAL(e, a:env)}))
64 return VectorNew(map(copy(a:ast.val), {_, e -> EVAL(e, a:env)}))
67 for [k,v] in items(a:ast.val)
68 let keyobj = HashParseKey(k)
69 let newkey = EVAL(keyobj, a:env)
70 let newval = EVAL(v, a:env)
71 let keystring = HashMakeKey(newkey)
72 let ret[keystring] = newval
80 function GetCatchClause(ast)
81 if ListCount(a:ast) < 3
84 let catch_clause = ListNth(a:ast, 2)
85 if ListFirst(catch_clause) == SymbolNew("catch*")
92 function EVAL(ast, env)
98 return EvalAst(ast, env)
101 let ast = MacroExpand(ast, env)
103 return EvalAst(ast, env)
109 let first = ListFirst(ast)
110 let first_symbol = SymbolQ(first) ? first.val : ""
111 if first_symbol == "def!"
114 return env.set(a1.val, EVAL(a2, env))
115 elseif first_symbol == "let*"
118 let env = NewEnv(env)
119 let let_binds = a1.val
121 while i < len(let_binds)
122 call env.set(let_binds[i].val, EVAL(let_binds[i+1], env))
127 elseif first_symbol == "quote"
128 return ListNth(ast, 1)
129 elseif first_symbol == "quasiquote"
130 let ast = Quasiquote(ListNth(ast, 1))
132 elseif first_symbol == "defmacro!"
133 let a1 = ListNth(ast, 1)
134 let a2 = ListNth(ast, 2)
135 let macro = MarkAsMacro(EVAL(a2, env))
136 return env.set(a1.val, macro)
137 elseif first_symbol == "macroexpand"
138 return MacroExpand(ListNth(ast, 1), env)
139 elseif first_symbol == "if"
140 let condvalue = EVAL(ast.val[1], env)
141 if FalseQ(condvalue) || NilQ(condvalue)
151 elseif first_symbol == "try*"
153 return EVAL(ListNth(ast, 1), env)
155 let catch_clause = GetCatchClause(ast)
156 if empty(catch_clause)
160 let exc_var = ListNth(catch_clause, 1).val
161 if v:exception == "__MalException__"
162 let exc_value = g:MalExceptionObj
164 let exc_value = StringNew(v:exception)
166 let catch_env = NewEnvWithBinds(env, ListNew([SymbolNew(exc_var)]), ListNew([exc_value]))
167 return EVAL(ListNth(catch_clause, 2), catch_env)
169 elseif first_symbol == "do"
170 let astlist = ast.val
171 call EvalAst(ListNew(astlist[1:-2]), env)
172 let ast = astlist[-1]
174 elseif first_symbol == "fn*"
175 let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1))
177 elseif first_symbol == "eval"
178 let ast = EVAL(ListNth(ast, 1), env)
183 let el = EvalAst(ast, env)
184 let funcobj = ListFirst(el)
185 let args = ListRest(el)
186 if NativeFunctionQ(funcobj)
187 return NativeFuncInvoke(funcobj, args)
188 elseif FunctionQ(funcobj)
191 let env = NewEnvWithBinds(fn.env, fn.params, args)
194 throw "Not a function"
201 return PrStr(a:exp, 1)
204 function RE(str, env)
205 return EVAL(READ(a:str), a:env)
208 function REP(str, env)
209 return PRINT(EVAL(READ(a:str), a:env))
212 function GetArgvList()
213 return ListNew(map(copy(argv()[1:]), {_, arg -> StringNew(arg)}))
216 set maxfuncdepth=10000
217 let repl_env = NewEnv("")
219 for [k, v] in items(CoreNs)
220 call repl_env.set(k, v)
223 call repl_env.set("*ARGV*", GetArgvList())
225 call RE("(def! *host-language* \"vimscript\")", repl_env)
226 call RE("(def! not (fn* (a) (if a false true)))", repl_env)
227 call RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env)
228 call RE("(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)))))))", repl_env)
232 call RE('(load-file "' . argv(0) . '")', repl_env)
234 call PrintLn("Error: " . v:exception)
239 call REP("(println (str \"Mal [\" *host-language* \"]\"))", repl_env)
242 let [eof, line] = Readline("user> ")
250 call PrintLn(REP(line, repl_env))
252 if v:exception == "__MalException__"
253 call PrintLn("Error: " . PrStr(g:MalExceptionObj, 1))
255 call PrintLn("Error: " . v:exception)