Merge pull request #49 from keith-rollin/swift
[jackhill/mal.git] / scala / step7_quote.scala
CommitLineData
a816262a
JM
1import types.{MalList, _list, _list_Q, MalVector, MalHashMap,
2 Func, MalFunction}
821930db
JM
3import env.Env
4
5object 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 => {
45 m.map{case (k: String,v: Any) => (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 // print
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