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 | pair_Q = { ast -> types.sequential_Q(ast) && ast.size() > 0} | |
17 | quasiquote = { ast -> | |
18 | if (! pair_Q(ast)) { | |
19 | [new MalSymbol("quote"), ast] | |
c963be7a JM |
20 | } else if (ast[0] != null && |
21 | ast[0].class == MalSymbol && | |
a9cd6543 JM |
22 | ast[0].value == "unquote") { |
23 | ast[1] | |
24 | } else if (pair_Q(ast[0]) && ast[0][0].class == MalSymbol && | |
25 | ast[0][0].value == "splice-unquote") { | |
26 | [new MalSymbol("concat"), ast[0][1], quasiquote(ast.drop(1))] | |
27 | } else { | |
28 | [new MalSymbol("cons"), quasiquote(ast[0]), quasiquote(ast.drop(1))] | |
29 | } | |
30 | } | |
31 | ||
32 | eval_ast = { ast, env -> | |
33 | switch (ast) { | |
34 | case MalSymbol: return env.get(ast); | |
35 | case List: return types.vector_Q(ast) ? | |
36 | types.vector(ast.collect { EVAL(it,env) }) : | |
37 | ast.collect { EVAL(it,env) } | |
38 | case Map: def new_hm = [:] | |
39 | ast.each { k,v -> | |
40 | new_hm[EVAL(k, env)] = EVAL(v, env) | |
41 | } | |
42 | return new_hm | |
43 | default: return ast | |
44 | } | |
45 | } | |
46 | ||
47 | EVAL = { ast, env -> | |
48 | while (true) { | |
49 | //println("EVAL: ${printer.pr_str(ast,true)}") | |
50 | if (! types.list_Q(ast)) return eval_ast(ast, env) | |
51 | ||
52 | switch (ast[0]) { | |
53 | case { it instanceof MalSymbol && it.value == "def!" }: | |
54 | return env.set(ast[1], EVAL(ast[2], env)) | |
55 | case { it instanceof MalSymbol && it.value == "let*" }: | |
56 | def let_env = new Env(env) | |
57 | for (int i=0; i < ast[1].size(); i += 2) { | |
58 | let_env.set(ast[1][i], EVAL(ast[1][i+1], let_env)) | |
59 | } | |
60 | env = let_env | |
61 | ast = ast[2] | |
62 | break // TCO | |
63 | case { it instanceof MalSymbol && it.value == "quote" }: | |
64 | return ast[1] | |
65 | case { it instanceof MalSymbol && it.value == "quasiquote" }: | |
66 | ast = quasiquote(ast[1]) | |
67 | break // TCO | |
68 | case { it instanceof MalSymbol && it.value == "do" }: | |
69 | ast.size() > 2 ? eval_ast(ast[1..-2], env) : null | |
70 | ast = ast[-1] | |
71 | break // TCO | |
72 | case { it instanceof MalSymbol && it.value == "if" }: | |
73 | def cond = EVAL(ast[1], env) | |
74 | if (cond == false || cond == null) { | |
75 | if (ast.size > 3) { | |
76 | ast = ast[3] | |
77 | break // TCO | |
78 | } else { | |
79 | return null | |
80 | } | |
81 | } else { | |
82 | ast = ast[2] | |
83 | break // TCO | |
84 | } | |
85 | case { it instanceof MalSymbol && it.value == "fn*" }: | |
86 | return new MalFunc(EVAL, ast[2], env, ast[1]) | |
87 | default: | |
88 | def el = eval_ast(ast, env) | |
89 | def (f, args) = [el[0], el.drop(1)] | |
90 | if (f instanceof MalFunc) { | |
91 | env = new Env(f.env, f.params, args) | |
92 | ast = f.ast | |
93 | break // TCO | |
94 | } else { | |
95 | return f(args) | |
96 | } | |
97 | } | |
98 | } | |
99 | } | |
100 | ||
101 | ||
102 | PRINT = { exp -> | |
103 | printer.pr_str exp, true | |
104 | } | |
105 | ||
106 | // REPL | |
107 | repl_env = new Env(); | |
108 | REP = { str -> | |
109 | PRINT(EVAL(READ(str), repl_env)) | |
110 | } | |
111 | ||
112 | // core.EXT: defined using Groovy | |
113 | core.ns.each { k,v -> | |
114 | repl_env.set(new MalSymbol(k), v) | |
115 | } | |
116 | repl_env.set(new MalSymbol("eval"), { a -> EVAL(a[0], repl_env)}) | |
117 | repl_env.set(new MalSymbol("*ARGV*"), this.args as List) | |
118 | ||
119 | // core.mal: defined using mal itself | |
120 | REP("(def! not (fn* (a) (if a false true)))") | |
121 | REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") | |
122 | ||
123 | if (this.args.size() > 0) { | |
124 | repl_env.set(new MalSymbol("*ARGV*"), this.args.drop(1) as List) | |
125 | REP("(load-file \"${this.args[0]}\")") | |
126 | System.exit(0) | |
127 | } | |
128 | ||
129 | while (true) { | |
130 | line = System.console().readLine 'user> ' | |
131 | if (line == null) { | |
132 | break; | |
133 | } | |
134 | try { | |
135 | println REP(line) | |
136 | } catch(MalException ex) { | |
137 | println "Error: ${ex.message}" | |
138 | } catch(StackOverflowError ex) { | |
139 | println "Error: ${ex}" | |
140 | } catch(ex) { | |
141 | println "Error: $ex" | |
142 | ex.printStackTrace() | |
143 | } | |
144 | } |