Merge pull request #358 from bjh21/bjh21-extra-tests
[jackhill/mal.git] / scala / step5_tco.scala
CommitLineData
a816262a
JM
1import types.{MalList, _list, _list_Q, MalVector, MalHashMap,
2 Func, MalFunction}
821930db
JM
3import env.Env
4
5object step5_tco {
6 // read
7 def READ(str: String): Any = {
8 reader.read_str(str)
9 }
10
11 // eval
12 def eval_ast(ast: Any, env: Env): Any = {
13 ast match {
14 case s : Symbol => env.get(s)
a816262a
JM
15 case v: MalVector => v.map(EVAL(_, env))
16 case l: MalList => l.map(EVAL(_, env))
17 case m: MalHashMap => {
fef22d1c 18 m.map{case (k,v) => (k, EVAL(v, env))}
821930db
JM
19 }
20 case _ => ast
21 }
22 }
23
24 def EVAL(orig_ast: Any, orig_env: Env): Any = {
25 var ast = orig_ast; var env = orig_env;
26 while (true) {
27
28 //println("EVAL: " + printer._pr_str(ast,true))
a816262a 29 if (!_list_Q(ast))
821930db
JM
30 return eval_ast(ast, env)
31
32 // apply list
a816262a 33 ast.asInstanceOf[MalList].value match {
eb243cd5
DM
34 case Nil => {
35 return ast
36 }
821930db
JM
37 case Symbol("def!") :: a1 :: a2 :: Nil => {
38 return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env))
39 }
40 case Symbol("let*") :: a1 :: a2 :: Nil => {
41 val let_env = new Env(env)
a816262a 42 for (g <- a1.asInstanceOf[MalList].value.grouped(2)) {
821930db
JM
43 let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env))
44 }
45 env = let_env
46 ast = a2 // continue loop (TCO)
47 }
48 case Symbol("do") :: rest => {
a816262a
JM
49 eval_ast(_list(rest.slice(0,rest.length-1):_*), env)
50 ast = ast.asInstanceOf[MalList].value.last // continue loop (TCO)
821930db
JM
51 }
52 case Symbol("if") :: a1 :: a2 :: rest => {
53 val cond = EVAL(a1, env)
54 if (cond == null || cond == false) {
55 if (rest.length == 0) return null
56 ast = rest(0) // continue loop (TCO)
57 } else {
58 ast = a2 // continue loop (TCO)
59 }
60 }
61 case Symbol("fn*") :: a1 :: a2 :: Nil => {
a816262a 62 return new MalFunction(a2, env, a1.asInstanceOf[MalList],
821930db
JM
63 (args: List[Any]) => {
64 EVAL(a2, new Env(env, types._toIter(a1), args.iterator))
65 }
66 )
67 }
68 case _ => {
69 // function call
a816262a 70 eval_ast(ast, env).asInstanceOf[MalList].value match {
821930db
JM
71 case f :: el => {
72 f match {
a816262a 73 case fn: MalFunction => {
821930db
JM
74 env = fn.gen_env(el)
75 ast = fn.ast // continue loop (TCO)
76 }
a816262a 77 case fn: Func => {
821930db
JM
78 return fn(el)
79 }
80 case _ => {
a816262a 81 throw new Exception("attempt to call non-function: " + f)
821930db
JM
82 }
83 }
84 }
85 case _ => throw new Exception("invalid apply")
86 }
87 }
88 }
89 }
90 }
91
92 // print
93 def PRINT(exp: Any): String = {
94 printer._pr_str(exp, true)
95 }
96
97 // repl
98 def main(args: Array[String]) = {
99 val repl_env: Env = new Env()
100 val REP = (str: String) => PRINT(EVAL(READ(str), repl_env))
101
102 // core.scala: defined using scala
a816262a
JM
103 core.ns.map{case (k: String,v: Any) => {
104 repl_env.set(Symbol(k), new Func(v))
105 }}
821930db
JM
106
107 // core.mal: defined using the language itself
108 REP("(def! not (fn* (a) (if a false true)))")
109
110 var line:String = null
111 while ({line = readLine("user> "); line != null}) {
112 try {
113 println(REP(line))
114 } catch {
115 case e : Throwable => {
116 println("Error: " + e.getMessage)
117 println(" " + e.getStackTrace.mkString("\n "))
118 }
119 }
120 }
121 }
122}
123
124// vim: ts=2:sw=2