Haxe: step7-A, hash-maps, metadata, self-hosting.
[jackhill/mal.git] / groovy / step7_quote.groovy
CommitLineData
a9cd6543
JM
1import reader
2import printer
3import types
4import types.MalException
5import types.MalSymbol
6import types.MalFunc
7import env.Env
8import core
9
10// READ
11READ = { str ->
12 reader.read_str str
13}
14
15// EVAL
16pair_Q = { ast -> types.sequential_Q(ast) && ast.size() > 0}
17quasiquote = { 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
32eval_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
47EVAL = { 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// PRINT
102PRINT = { exp ->
103 printer.pr_str exp, true
104}
105
106// REPL
107repl_env = new Env();
108REP = { str ->
109 PRINT(EVAL(READ(str), repl_env))
110}
111
112// core.EXT: defined using Groovy
113core.ns.each { k,v ->
114 repl_env.set(new MalSymbol(k), v)
115}
116repl_env.set(new MalSymbol("eval"), { a -> EVAL(a[0], repl_env)})
117repl_env.set(new MalSymbol("*ARGV*"), this.args as List)
118
119// core.mal: defined using mal itself
120REP("(def! not (fn* (a) (if a false true)))")
121REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
122
123if (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
129while (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}