Misc fixes and TODO updates.
[jackhill/mal.git] / ruby / step5_tco.rb
1 require_relative "mal_readline"
2 require_relative "types"
3 require_relative "reader"
4 require_relative "printer"
5 require_relative "env"
6 require_relative "core"
7
8 # read
9 def READ(str)
10 return read_str(str)
11 end
12
13 # eval
14 def eval_ast(ast, env)
15 return case ast
16 when Symbol
17 env.get(ast)
18 when List
19 List.new ast.map{|a| EVAL(a, env)}
20 when Vector
21 Vector.new ast.map{|a| EVAL(a, env)}
22 when Hash
23 new_hm = {}
24 ast.each{|k,v| new_hm[EVAL(k,env)] = EVAL(v, env)}
25 new_hm
26 else
27 ast
28 end
29 end
30
31 def EVAL(ast, env)
32 while true
33
34 #puts "EVAL: #{_pr_str(ast, true)}"
35
36 if not ast.is_a? List
37 return eval_ast(ast, env)
38 end
39 if ast.empty?
40 return ast
41 end
42
43 # apply list
44 a0,a1,a2,a3 = ast
45 case a0
46 when :def!
47 return env.set(a1, EVAL(a2, env))
48 when :"let*"
49 let_env = Env.new(env)
50 a1.each_slice(2) do |a,e|
51 let_env.set(a, EVAL(e, let_env))
52 end
53 env = let_env
54 ast = a2 # Continue loop (TCO)
55 when :do
56 eval_ast(ast[1..-2], env)
57 ast = ast.last # Continue loop (TCO)
58 when :if
59 cond = EVAL(a1, env)
60 if not cond
61 return nil if a3 == nil
62 ast = a3 # Continue loop (TCO)
63 else
64 ast = a2 # Continue loop (TCO)
65 end
66 when :"fn*"
67 return Function.new(a2, env, a1) {|*args|
68 EVAL(a2, Env.new(env, a1, List.new(args)))
69 }
70 else
71 el = eval_ast(ast, env)
72 f = el[0]
73 if f.class == Function
74 ast = f.ast
75 env = f.gen_env(el.drop(1)) # Continue loop (TCO)
76 else
77 return f[*el.drop(1)]
78 end
79 end
80
81 end
82 end
83
84 # print
85 def PRINT(exp)
86 return _pr_str(exp, true)
87 end
88
89 # repl
90 repl_env = Env.new
91 RE = lambda {|str| EVAL(READ(str), repl_env) }
92 REP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) }
93
94 # core.rb: defined using ruby
95 $core_ns.each do |k,v| repl_env.set(k,v) end
96
97 # core.mal: defined using the language itself
98 RE["(def! not (fn* (a) (if a false true)))"]
99
100 # repl loop
101 while line = _readline("user> ")
102 begin
103 puts REP[line]
104 rescue Exception => e
105 puts "Error: #{e}"
106 puts "\t#{e.backtrace[0..100].join("\n\t")}"
107 end
108 end