runtest: set INPUTRC to /dev/null
[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
40 # apply list
41 a0,a1,a2,a3 = ast
42 case a0
43 when :def!
44 return env.set(a1, EVAL(a2, env))
45 when :"let*"
46 let_env = Env.new(env)
47 a1.each_slice(2) do |a,e|
48 let_env.set(a, EVAL(e, let_env))
49 end
50 env = let_env
51 ast = a2 # Continue loop (TCO)
52 when :do
53 eval_ast(ast[1..-2], env)
54 ast = ast.last # Continue loop (TCO)
55 when :if
56 cond = EVAL(a1, env)
57 if not cond
58 return nil if a3 == nil
59 ast = a3 # Continue loop (TCO)
60 else
61 ast = a2 # Continue loop (TCO)
62 end
63 when :"fn*"
64 return Function.new(a2, env, a1) {|*args|
65 EVAL(a2, Env.new(env, a1, args))
66 }
67 else
68 el = eval_ast(ast, env)
69 f = el[0]
70 if f.class == Function
71 ast = f.ast
72 env = f.gen_env(el.drop(1)) # Continue loop (TCO)
73 else
74 return f[*el.drop(1)]
75 end
76 end
77
78 end
79 end
80
81 # print
82 def PRINT(exp)
83 return _pr_str(exp, true)
84 end
85
86 # repl
87 repl_env = Env.new
88 RE = lambda {|str| EVAL(READ(str), repl_env) }
89 REP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) }
90
91 # core.rb: defined using ruby
92 $core_ns.each do |k,v| repl_env.set(k,v) end
93
94 # core.mal: defined using the language itself
95 RE["(def! not (fn* (a) (if a false true)))"]
96
97 # repl loop
98 while line = _readline("user> ")
99 begin
100 puts REP[line]
101 rescue Exception => e
102 puts "Error: #{e}"
103 puts "\t#{e.backtrace.join("\n\t")}"
104 end
105 end