Commit | Line | Data |
---|---|---|
a9cd6543 JM |
1 | import reader |
2 | import printer | |
3 | import types | |
4 | import types.MalException | |
5 | import types.MalSymbol | |
6 | import types.MalFunc | |
7 | import env.Env | |
8 | import core | |
9 | ||
10 | // READ | |
11 | READ = { str -> | |
12 | reader.read_str str | |
13 | } | |
14 | ||
15 | // EVAL | |
16 | macro_Q = { ast, env -> | |
17 | if (types.list_Q(ast) && | |
18 | ast[0].class == MalSymbol && | |
19 | env.find(ast[0])) { | |
20 | def obj = env.get(ast[0]) | |
21 | if (obj instanceof MalFunc && obj.ismacro) { | |
22 | return true | |
23 | } | |
24 | } | |
25 | return false | |
26 | } | |
27 | macroexpand = { ast, env -> | |
28 | while (macro_Q(ast, env)) { | |
29 | def mac = env.get(ast[0]) | |
30 | ast = mac(ast.drop(1)) | |
31 | } | |
32 | return ast | |
33 | } | |
34 | ||
35 | pair_Q = { ast -> types.sequential_Q(ast) && ast.size() > 0} | |
36 | quasiquote = { ast -> | |
37 | if (! pair_Q(ast)) { | |
38 | [new MalSymbol("quote"), ast] | |
c963be7a JM |
39 | } else if (ast[0] != null && |
40 | ast[0].class == MalSymbol && | |
a9cd6543 JM |
41 | ast[0].value == "unquote") { |
42 | ast[1] | |
43 | } else if (pair_Q(ast[0]) && ast[0][0].class == MalSymbol && | |
44 | ast[0][0].value == "splice-unquote") { | |
45 | [new MalSymbol("concat"), ast[0][1], quasiquote(ast.drop(1))] | |
46 | } else { | |
47 | [new MalSymbol("cons"), quasiquote(ast[0]), quasiquote(ast.drop(1))] | |
48 | } | |
49 | } | |
50 | ||
51 | eval_ast = { ast, env -> | |
52 | switch (ast) { | |
53 | case MalSymbol: return env.get(ast); | |
54 | case List: return types.vector_Q(ast) ? | |
55 | types.vector(ast.collect { EVAL(it,env) }) : | |
56 | ast.collect { EVAL(it,env) } | |
57 | case Map: def new_hm = [:] | |
58 | ast.each { k,v -> | |
59 | new_hm[EVAL(k, env)] = EVAL(v, env) | |
60 | } | |
61 | return new_hm | |
62 | default: return ast | |
63 | } | |
64 | } | |
65 | ||
66 | EVAL = { ast, env -> | |
67 | while (true) { | |
68 | //println("EVAL: ${printer.pr_str(ast,true)}") | |
69 | if (! types.list_Q(ast)) return eval_ast(ast, env) | |
70 | ||
71 | ast = macroexpand(ast, env) | |
44aef1f4 | 72 | if (! types.list_Q(ast)) return eval_ast(ast, env) |
a9cd6543 JM |
73 | |
74 | switch (ast[0]) { | |
75 | case { it instanceof MalSymbol && it.value == "def!" }: | |
76 | return env.set(ast[1], EVAL(ast[2], env)) | |
77 | case { it instanceof MalSymbol && it.value == "let*" }: | |
78 | def let_env = new Env(env) | |
79 | for (int i=0; i < ast[1].size(); i += 2) { | |
80 | let_env.set(ast[1][i], EVAL(ast[1][i+1], let_env)) | |
81 | } | |
82 | env = let_env | |
83 | ast = ast[2] | |
84 | break // TCO | |
85 | case { it instanceof MalSymbol && it.value == "quote" }: | |
86 | return ast[1] | |
87 | case { it instanceof MalSymbol && it.value == "quasiquote" }: | |
88 | ast = quasiquote(ast[1]) | |
89 | break // TCO | |
90 | case { it instanceof MalSymbol && it.value == "defmacro!" }: | |
91 | def f = EVAL(ast[2], env) | |
92 | f.ismacro = true | |
93 | return env.set(ast[1], f) | |
94 | case { it instanceof MalSymbol && it.value == "macroexpand" }: | |
95 | return macroexpand(ast[1], env) | |
96 | case { it instanceof MalSymbol && it.value == "try*" }: | |
97 | try { | |
98 | return EVAL(ast[1], env) | |
99 | } catch(exc) { | |
100 | if (ast.size() > 2 && | |
101 | ast[2][0] instanceof MalSymbol && | |
102 | ast[2][0].value == "catch*") { | |
103 | def e = null | |
104 | if (exc instanceof MalException) { | |
105 | e = exc.obj | |
106 | } else { | |
107 | e = exc.message | |
108 | } | |
109 | return EVAL(ast[2][2], new Env(env, [ast[2][1]], [e])) | |
110 | } else { | |
111 | throw exc | |
112 | } | |
113 | } | |
114 | case { it instanceof MalSymbol && it.value == "do" }: | |
115 | ast.size() > 2 ? eval_ast(ast[1..-2], env) : null | |
116 | ast = ast[-1] | |
117 | break // TCO | |
118 | case { it instanceof MalSymbol && it.value == "if" }: | |
119 | def cond = EVAL(ast[1], env) | |
120 | if (cond == false || cond == null) { | |
121 | if (ast.size > 3) { | |
122 | ast = ast[3] | |
123 | break // TCO | |
124 | } else { | |
125 | return null | |
126 | } | |
127 | } else { | |
128 | ast = ast[2] | |
129 | break // TCO | |
130 | } | |
131 | case { it instanceof MalSymbol && it.value == "fn*" }: | |
132 | return new MalFunc(EVAL, ast[2], env, ast[1]) | |
133 | default: | |
134 | def el = eval_ast(ast, env) | |
135 | def (f, args) = [el[0], el.drop(1)] | |
136 | if (f instanceof MalFunc) { | |
137 | env = new Env(f.env, f.params, args) | |
138 | ast = f.ast | |
139 | break // TCO | |
140 | } else { | |
141 | return f(args) | |
142 | } | |
143 | } | |
144 | } | |
145 | } | |
146 | ||
147 | ||
148 | PRINT = { exp -> | |
149 | printer.pr_str exp, true | |
150 | } | |
151 | ||
152 | // REPL | |
153 | repl_env = new Env(); | |
154 | REP = { str -> | |
155 | PRINT(EVAL(READ(str), repl_env)) | |
156 | } | |
157 | ||
158 | // core.EXT: defined using Groovy | |
159 | core.ns.each { k,v -> | |
160 | repl_env.set(new MalSymbol(k), v) | |
161 | } | |
162 | repl_env.set(new MalSymbol("eval"), { a -> EVAL(a[0], repl_env)}) | |
163 | repl_env.set(new MalSymbol("*ARGV*"), this.args as List) | |
164 | ||
165 | // core.mal: defined using mal itself | |
166 | REP("(def! not (fn* (a) (if a false true)))") | |
167 | REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") | |
168 | REP("(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)))))))"); | |
169 | REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); | |
170 | ||
171 | ||
172 | if (this.args.size() > 0) { | |
173 | repl_env.set(new MalSymbol("*ARGV*"), this.args.drop(1) as List) | |
174 | REP("(load-file \"${this.args[0]}\")") | |
175 | System.exit(0) | |
176 | } | |
177 | ||
178 | while (true) { | |
179 | line = System.console().readLine 'user> ' | |
180 | if (line == null) { | |
181 | break; | |
182 | } | |
183 | try { | |
184 | println REP(line) | |
185 | } catch(MalException ex) { | |
186 | println "Error: ${ex.message}" | |
187 | } catch(StackOverflowError ex) { | |
188 | println "Error: ${ex}" | |
189 | } catch(ex) { | |
190 | println "Error: $ex" | |
191 | ex.printStackTrace() | |
192 | } | |
193 | } |