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