process/guide.md: Add TOC; fix heading levels
[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 {
821930db
JM
34 case Symbol("def!") :: a1 :: a2 :: Nil => {
35 return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env))
36 }
37 case Symbol("let*") :: a1 :: a2 :: Nil => {
38 val let_env = new Env(env)
a816262a 39 for (g <- a1.asInstanceOf[MalList].value.grouped(2)) {
821930db
JM
40 let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env))
41 }
42 env = let_env
43 ast = a2 // continue loop (TCO)
44 }
45 case Symbol("do") :: rest => {
a816262a
JM
46 eval_ast(_list(rest.slice(0,rest.length-1):_*), env)
47 ast = ast.asInstanceOf[MalList].value.last // continue loop (TCO)
821930db
JM
48 }
49 case Symbol("if") :: a1 :: a2 :: rest => {
50 val cond = EVAL(a1, env)
51 if (cond == null || cond == false) {
52 if (rest.length == 0) return null
53 ast = rest(0) // continue loop (TCO)
54 } else {
55 ast = a2 // continue loop (TCO)
56 }
57 }
58 case Symbol("fn*") :: a1 :: a2 :: Nil => {
a816262a 59 return new MalFunction(a2, env, a1.asInstanceOf[MalList],
821930db
JM
60 (args: List[Any]) => {
61 EVAL(a2, new Env(env, types._toIter(a1), args.iterator))
62 }
63 )
64 }
65 case _ => {
66 // function call
a816262a 67 eval_ast(ast, env).asInstanceOf[MalList].value match {
821930db
JM
68 case f :: el => {
69 f match {
a816262a 70 case fn: MalFunction => {
821930db
JM
71 env = fn.gen_env(el)
72 ast = fn.ast // continue loop (TCO)
73 }
a816262a 74 case fn: Func => {
821930db
JM
75 return fn(el)
76 }
77 case _ => {
a816262a 78 throw new Exception("attempt to call non-function: " + f)
821930db
JM
79 }
80 }
81 }
82 case _ => throw new Exception("invalid apply")
83 }
84 }
85 }
86 }
87 }
88
89 // print
90 def PRINT(exp: Any): String = {
91 printer._pr_str(exp, true)
92 }
93
94 // repl
95 def main(args: Array[String]) = {
96 val repl_env: Env = new Env()
97 val REP = (str: String) => PRINT(EVAL(READ(str), repl_env))
98
99 // core.scala: defined using scala
a816262a
JM
100 core.ns.map{case (k: String,v: Any) => {
101 repl_env.set(Symbol(k), new Func(v))
102 }}
821930db
JM
103
104 // core.mal: defined using the language itself
105 REP("(def! not (fn* (a) (if a false true)))")
106
107 var line:String = null
108 while ({line = readLine("user> "); line != null}) {
109 try {
110 println(REP(line))
111 } catch {
112 case e : Throwable => {
113 println("Error: " + e.getMessage)
114 println(" " + e.getStackTrace.mkString("\n "))
115 }
116 }
117 }
118 }
119}
120
121// vim: ts=2:sw=2