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