Commit | Line | Data |
---|---|---|
50a964ce DM |
1 | source readline.vim |
2 | source types.vim | |
3 | source reader.vim | |
4 | source printer.vim | |
5 | source env.vim | |
6 | source core.vim | |
7 | ||
8 | let MalExceptionObj = "" | |
9 | ||
10 | function READ(str) | |
11 | return ReadStr(a:str) | |
12 | endfunction | |
13 | ||
14 | function PairQ(obj) | |
15 | return SequentialQ(a:obj) && !EmptyQ(a:obj) | |
16 | endfunction | |
17 | ||
18 | function Quasiquote(ast) | |
19 | if !PairQ(a:ast) | |
20 | return ListNew([SymbolNew("quote"), a:ast]) | |
21 | endif | |
22 | let a0 = ListFirst(a:ast) | |
23 | if SymbolQ(a0) && ObjValue(a0) == "unquote" | |
24 | return ListNth(a:ast, 1) | |
25 | elseif PairQ(a0) && SymbolQ(ListFirst(a0)) && ObjValue(ListFirst(a0)) == "splice-unquote" | |
26 | return ListNew([SymbolNew("concat"), ListNth(a0, 1), Quasiquote(ListRest(a:ast))]) | |
27 | else | |
28 | return ListNew([SymbolNew("cons"), Quasiquote(a0), Quasiquote(ListRest(a:ast))]) | |
29 | end | |
30 | endfunction | |
31 | ||
32 | function IsMacroCall(ast, env) | |
33 | if !ListQ(a:ast) | |
34 | return 0 | |
35 | endif | |
36 | let a0 = ListFirst(a:ast) | |
37 | if !SymbolQ(a0) | |
38 | return 0 | |
39 | endif | |
40 | let macroname = ObjValue(a0) | |
41 | if empty(a:env.find(macroname)) | |
42 | return 0 | |
43 | endif | |
44 | return MacroQ(a:env.get(macroname)) | |
45 | endfunction | |
46 | ||
47 | function MacroExpand(ast, env) | |
48 | let ast = a:ast | |
49 | while IsMacroCall(ast, a:env) | |
50 | let macroobj = a:env.get(ObjValue(ListFirst(ast))) | |
51 | let macroargs = ListRest(ast) | |
52 | let ast = FuncInvoke(macroobj, macroargs) | |
53 | endwhile | |
54 | return ast | |
55 | endfunction | |
56 | ||
57 | function EvalAst(ast, env) | |
58 | if SymbolQ(a:ast) | |
59 | let varname = ObjValue(a:ast) | |
60 | return a:env.get(varname) | |
61 | elseif ListQ(a:ast) | |
62 | let ret = [] | |
63 | for e in ObjValue(a:ast) | |
64 | call add(ret, EVAL(e, a:env)) | |
65 | endfor | |
66 | return ListNew(ret) | |
67 | elseif VectorQ(a:ast) | |
68 | let ret = [] | |
69 | for e in ObjValue(a:ast) | |
70 | call add(ret, EVAL(e, a:env)) | |
71 | endfor | |
72 | return VectorNew(ret) | |
73 | elseif HashQ(a:ast) | |
74 | let ret = {} | |
75 | for [k,v] in items(ObjValue(a:ast)) | |
76 | let keyobj = HashParseKey(k) | |
77 | let newkey = EVAL(keyobj, a:env) | |
78 | let newval = EVAL(v, a:env) | |
79 | let keystring = HashMakeKey(newkey) | |
80 | let ret[keystring] = newval | |
81 | endfor | |
82 | return HashNew(ret) | |
83 | else | |
84 | return a:ast | |
85 | end | |
86 | endfunction | |
87 | ||
88 | function GetCatchClause(ast) | |
89 | if ListCount(a:ast) < 3 | |
90 | return "" | |
91 | end | |
92 | let catch_clause = ListNth(a:ast, 2) | |
93 | if ListFirst(catch_clause) == SymbolNew("catch*") | |
94 | return catch_clause | |
95 | else | |
96 | return "" | |
97 | end | |
98 | endfunction | |
99 | ||
100 | function EVAL(ast, env) | |
101 | let ast = a:ast | |
102 | let env = a:env | |
103 | ||
104 | while 1 | |
105 | if !ListQ(ast) | |
106 | return EvalAst(ast, env) | |
107 | end | |
108 | ||
109 | let ast = MacroExpand(ast, env) | |
110 | if !ListQ(ast) | |
6c94cd3e | 111 | return EvalAst(ast, env) |
50a964ce DM |
112 | end |
113 | ||
114 | let first = ListFirst(ast) | |
115 | let first_symbol = SymbolQ(first) ? ObjValue(first) : "" | |
116 | if first_symbol == "def!" | |
117 | let a1 = ObjValue(ast)[1] | |
118 | let a2 = ObjValue(ast)[2] | |
119 | return env.set(ObjValue(a1), EVAL(a2, env)) | |
120 | elseif first_symbol == "let*" | |
121 | let a1 = ObjValue(ast)[1] | |
122 | let a2 = ObjValue(ast)[2] | |
123 | let env = NewEnv(env) | |
124 | let let_binds = ObjValue(a1) | |
125 | let i = 0 | |
126 | while i < len(let_binds) | |
127 | call env.set(ObjValue(let_binds[i]), EVAL(let_binds[i+1], env)) | |
128 | let i = i + 2 | |
129 | endwhile | |
130 | let ast = a2 | |
131 | " TCO | |
132 | elseif first_symbol == "quote" | |
133 | return ListNth(ast, 1) | |
134 | elseif first_symbol == "quasiquote" | |
135 | let ast = Quasiquote(ListNth(ast, 1)) | |
136 | " TCO | |
137 | elseif first_symbol == "defmacro!" | |
138 | let a1 = ListNth(ast, 1) | |
139 | let a2 = ListNth(ast, 2) | |
140 | let macro = MarkAsMacro(EVAL(a2, env)) | |
141 | return env.set(ObjValue(a1), macro) | |
142 | elseif first_symbol == "macroexpand" | |
143 | return MacroExpand(ListNth(ast, 1), env) | |
144 | elseif first_symbol == "if" | |
145 | let condvalue = EVAL(ObjValue(ast)[1], env) | |
146 | if FalseQ(condvalue) || NilQ(condvalue) | |
147 | if len(ObjValue(ast)) < 4 | |
148 | return g:MalNil | |
149 | else | |
150 | let ast = ObjValue(ast)[3] | |
151 | endif | |
152 | else | |
153 | let ast = ObjValue(ast)[2] | |
154 | endif | |
155 | " TCO | |
156 | elseif first_symbol == "try*" | |
157 | try | |
158 | return EVAL(ListNth(ast, 1), env) | |
159 | catch | |
160 | let catch_clause = GetCatchClause(ast) | |
161 | if empty(catch_clause) | |
162 | throw v:exception | |
163 | endif | |
164 | ||
165 | let exc_var = ObjValue(ListNth(catch_clause, 1)) | |
166 | if v:exception == "__MalException__" | |
167 | let exc_value = g:MalExceptionObj | |
168 | else | |
169 | let exc_value = StringNew(v:exception) | |
170 | endif | |
171 | let catch_env = NewEnvWithBinds(env, ListNew([SymbolNew(exc_var)]), ListNew([exc_value])) | |
172 | return EVAL(ListNth(catch_clause, 2), catch_env) | |
173 | endtry | |
174 | elseif first_symbol == "do" | |
175 | let astlist = ObjValue(ast) | |
176 | call EvalAst(ListNew(astlist[1:-2]), env) | |
177 | let ast = astlist[-1] | |
178 | " TCO | |
179 | elseif first_symbol == "fn*" | |
180 | let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1)) | |
181 | return fn | |
182 | elseif first_symbol == "eval" | |
183 | let ast = EVAL(ListNth(ast, 1), env) | |
184 | let env = env.root() | |
185 | " TCO | |
186 | else | |
187 | " apply list | |
188 | let el = EvalAst(ast, env) | |
189 | let funcobj = ListFirst(el) | |
190 | let args = ListRest(el) | |
191 | if NativeFunctionQ(funcobj) | |
192 | return NativeFuncInvoke(funcobj, args) | |
193 | elseif FunctionQ(funcobj) | |
194 | let fn = ObjValue(funcobj) | |
195 | let ast = fn.ast | |
196 | let env = NewEnvWithBinds(fn.env, fn.params, args) | |
197 | " TCO | |
198 | else | |
199 | throw "Not a function" | |
200 | endif | |
201 | endif | |
202 | endwhile | |
203 | endfunction | |
204 | ||
205 | function PRINT(exp) | |
206 | return PrStr(a:exp, 1) | |
207 | endfunction | |
208 | ||
209 | function RE(str, env) | |
210 | return EVAL(READ(a:str), a:env) | |
211 | endfunction | |
212 | ||
213 | function REP(str, env) | |
214 | return PRINT(EVAL(READ(a:str), a:env)) | |
215 | endfunction | |
216 | ||
217 | function GetArgvList() | |
218 | let args = argv() | |
219 | let list = [] | |
220 | for arg in args[1:] | |
221 | call add(list, StringNew(arg)) | |
222 | endfor | |
223 | return ListNew(list) | |
224 | endfunction | |
225 | ||
226 | set maxfuncdepth=10000 | |
227 | let repl_env = NewEnv("") | |
228 | ||
229 | for [k, v] in items(CoreNs) | |
230 | call repl_env.set(k, v) | |
231 | endfor | |
232 | ||
233 | call repl_env.set("*ARGV*", GetArgvList()) | |
234 | ||
235 | call RE("(def! *host-language* \"vimscript\")", repl_env) | |
236 | call RE("(def! not (fn* (a) (if a false true)))", repl_env) | |
237 | call RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) | |
238 | 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) | |
48572759 DM |
239 | call RE("(def! *gensym-counter* (atom 0))", repl_env) |
240 | call RE("(def! gensym (fn* [] (symbol (str \"G__\" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))", repl_env) | |
241 | call RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", repl_env) | |
50a964ce DM |
242 | |
243 | if !empty(argv()) | |
244 | try | |
245 | call RE('(load-file "' . argv(0) . '")', repl_env) | |
246 | catch | |
247 | call PrintLn("Error: " . v:exception) | |
248 | endtry | |
249 | qall! | |
250 | endif | |
251 | ||
252 | call REP("(println (str \"Mal [\" *host-language* \"]\"))", repl_env) | |
253 | ||
254 | while 1 | |
255 | let [eof, line] = Readline("user> ") | |
256 | if eof | |
257 | break | |
258 | endif | |
259 | if line == "" | |
260 | continue | |
261 | endif | |
262 | try | |
263 | call PrintLn(REP(line, repl_env)) | |
264 | catch | |
265 | call PrintLn("Error: " . v:exception) | |
266 | endtry | |
267 | endwhile | |
268 | qall! |