Commit | Line | Data |
---|---|---|
a816262a JM |
1 | import types.{MalList, _list, _list_Q, MalVector, MalHashMap, |
2 | Func, MalFunction} | |
821930db JM |
3 | import env.Env |
4 | ||
5 | object step7_quote { | |
6 | // read | |
7 | def READ(str: String): Any = { | |
8 | reader.read_str(str) | |
9 | } | |
10 | ||
11 | // eval | |
12 | def is_pair(x: Any): Boolean = { | |
a816262a | 13 | types._sequential_Q(x) && x.asInstanceOf[MalList].value.length > 0 |
821930db JM |
14 | } |
15 | ||
16 | def quasiquote(ast: Any): Any = { | |
17 | if (!is_pair(ast)) { | |
a816262a | 18 | return _list(Symbol("quote"), ast) |
821930db | 19 | } else { |
a816262a | 20 | val a0 = ast.asInstanceOf[MalList](0) |
821930db JM |
21 | if (types._symbol_Q(a0) && |
22 | a0.asInstanceOf[Symbol].name == "unquote") { | |
a816262a | 23 | return ast.asInstanceOf[MalList](1) |
821930db | 24 | } else if (is_pair(a0)) { |
a816262a | 25 | val a00 = a0.asInstanceOf[MalList](0) |
821930db JM |
26 | if (types._symbol_Q(a00) && |
27 | a00.asInstanceOf[Symbol].name == "splice-unquote") { | |
a816262a JM |
28 | return _list(Symbol("concat"), |
29 | a0.asInstanceOf[MalList](1), | |
30 | quasiquote(ast.asInstanceOf[MalList].drop(1))) | |
821930db JM |
31 | } |
32 | } | |
a816262a JM |
33 | return _list(Symbol("cons"), |
34 | quasiquote(a0), | |
35 | quasiquote(ast.asInstanceOf[MalList].drop(1))) | |
821930db JM |
36 | } |
37 | } | |
38 | ||
39 | def eval_ast(ast: Any, env: Env): Any = { | |
40 | ast match { | |
41 | case s : Symbol => env.get(s) | |
a816262a JM |
42 | case v: MalVector => v.map(EVAL(_, env)) |
43 | case l: MalList => l.map(EVAL(_, env)) | |
44 | case m: MalHashMap => { | |
fef22d1c | 45 | m.map{case (k,v) => (k, EVAL(v, env))} |
821930db JM |
46 | } |
47 | case _ => ast | |
48 | } | |
49 | } | |
50 | ||
51 | def EVAL(orig_ast: Any, orig_env: Env): Any = { | |
52 | var ast = orig_ast; var env = orig_env; | |
53 | while (true) { | |
54 | ||
55 | //println("EVAL: " + printer._pr_str(ast,true)) | |
a816262a | 56 | if (!_list_Q(ast)) |
821930db JM |
57 | return eval_ast(ast, env) |
58 | ||
59 | // apply list | |
a816262a | 60 | ast.asInstanceOf[MalList].value match { |
821930db JM |
61 | case Symbol("def!") :: a1 :: a2 :: Nil => { |
62 | return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env)) | |
63 | } | |
64 | case Symbol("let*") :: a1 :: a2 :: Nil => { | |
65 | val let_env = new Env(env) | |
a816262a | 66 | for (g <- a1.asInstanceOf[MalList].value.grouped(2)) { |
821930db JM |
67 | let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env)) |
68 | } | |
69 | env = let_env | |
70 | ast = a2 // continue loop (TCO) | |
71 | } | |
72 | case Symbol("quote") :: a1 :: Nil => { | |
73 | return a1 | |
74 | } | |
75 | case Symbol("quasiquote") :: a1 :: Nil => { | |
76 | ast = quasiquote(a1) // continue loop (TCO) | |
77 | } | |
78 | case Symbol("do") :: rest => { | |
a816262a JM |
79 | eval_ast(_list(rest.slice(0,rest.length-1):_*), env) |
80 | ast = ast.asInstanceOf[MalList].value.last // continue loop (TCO) | |
821930db JM |
81 | } |
82 | case Symbol("if") :: a1 :: a2 :: rest => { | |
83 | val cond = EVAL(a1, env) | |
84 | if (cond == null || cond == false) { | |
85 | if (rest.length == 0) return null | |
86 | ast = rest(0) // continue loop (TCO) | |
87 | } else { | |
88 | ast = a2 // continue loop (TCO) | |
89 | } | |
90 | } | |
91 | case Symbol("fn*") :: a1 :: a2 :: Nil => { | |
a816262a | 92 | return new MalFunction(a2, env, a1.asInstanceOf[MalList], |
821930db JM |
93 | (args: List[Any]) => { |
94 | EVAL(a2, new Env(env, types._toIter(a1), args.iterator)) | |
95 | } | |
96 | ) | |
97 | } | |
98 | case _ => { | |
99 | // function call | |
a816262a | 100 | eval_ast(ast, env).asInstanceOf[MalList].value match { |
821930db JM |
101 | case f :: el => { |
102 | f match { | |
a816262a | 103 | case fn: MalFunction => { |
821930db JM |
104 | env = fn.gen_env(el) |
105 | ast = fn.ast // continue loop (TCO) | |
106 | } | |
a816262a | 107 | case fn: Func => { |
821930db JM |
108 | return fn(el) |
109 | } | |
110 | case _ => { | |
111 | throw new Exception("attempt to call non-function: " + f) | |
112 | } | |
113 | } | |
114 | } | |
115 | case _ => throw new Exception("invalid apply") | |
116 | } | |
117 | } | |
118 | } | |
119 | } | |
120 | } | |
121 | ||
122 | ||
123 | def PRINT(exp: Any): String = { | |
124 | printer._pr_str(exp, true) | |
125 | } | |
126 | ||
127 | // repl | |
128 | def main(args: Array[String]) = { | |
129 | val repl_env: Env = new Env() | |
130 | val REP = (str: String) => PRINT(EVAL(READ(str), repl_env)) | |
131 | ||
132 | // core.scala: defined using scala | |
a816262a JM |
133 | core.ns.map{case (k: String,v: Any) => { |
134 | repl_env.set(Symbol(k), new Func(v)) | |
135 | }} | |
136 | repl_env.set(Symbol("eval"), new Func((a: List[Any]) => EVAL(a(0), repl_env))) | |
137 | repl_env.set(Symbol("*ARGV*"), _list(args.slice(1,args.length):_*)) | |
821930db JM |
138 | |
139 | // core.mal: defined using the language itself | |
140 | REP("(def! not (fn* (a) (if a false true)))") | |
141 | REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") | |
142 | ||
143 | if (args.length > 0) { | |
144 | REP("(load-file \"" + args(0) + "\")") | |
145 | System.exit(0) | |
146 | } | |
147 | ||
148 | // repl loop | |
149 | var line:String = null | |
150 | while ({line = readLine("user> "); line != null}) { | |
151 | try { | |
152 | println(REP(line)) | |
153 | } catch { | |
154 | case e : Throwable => { | |
155 | println("Error: " + e.getMessage) | |
156 | println(" " + e.getStackTrace.mkString("\n ")) | |
157 | } | |
158 | } | |
159 | } | |
160 | } | |
161 | } | |
162 | ||
163 | // vim: ts=2:sw=2 |