Commit | Line | Data |
---|---|---|
107d9694 IJ |
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" | |
7d2dad89 JM |
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)} | |
3e8a088f JM |
22 | when Hash |
23 | new_hm = {} | |
24 | ast.each{|k,v| new_hm[EVAL(k,env)] = EVAL(v, env)} | |
25 | new_hm | |
7d2dad89 JM |
26 | else |
27 | ast | |
28 | end | |
29 | end | |
30 | ||
31 | def EVAL(ast, env) | |
32 | while true | |
33 | ||
3e8a088f JM |
34 | #puts "EVAL: #{_pr_str(ast, true)}" |
35 | ||
7d2dad89 JM |
36 | if not ast.is_a? List |
37 | return eval_ast(ast, env) | |
38 | end | |
f53183a3 DM |
39 | if ast.empty? |
40 | return ast | |
41 | end | |
7d2dad89 JM |
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 | |
6301e0b6 JM |
53 | env = let_env |
54 | ast = a2 # Continue loop (TCO) | |
7d2dad89 JM |
55 | when :do |
56 | eval_ast(ast[1..-2], env) | |
6301e0b6 | 57 | ast = ast.last # Continue loop (TCO) |
7d2dad89 JM |
58 | when :if |
59 | cond = EVAL(a1, env) | |
60 | if not cond | |
61 | return nil if a3 == nil | |
6301e0b6 | 62 | ast = a3 # Continue loop (TCO) |
7d2dad89 | 63 | else |
6301e0b6 | 64 | ast = a2 # Continue loop (TCO) |
7d2dad89 JM |
65 | end |
66 | when :"fn*" | |
67 | return Function.new(a2, env, a1) {|*args| | |
79859c62 | 68 | EVAL(a2, Env.new(env, a1, List.new(args))) |
7d2dad89 JM |
69 | } |
70 | else | |
71 | el = eval_ast(ast, env) | |
72 | f = el[0] | |
73 | if f.class == Function | |
74 | ast = f.ast | |
6301e0b6 | 75 | env = f.gen_env(el.drop(1)) # Continue loop (TCO) |
7d2dad89 JM |
76 | else |
77 | return f[*el.drop(1)] | |
78 | end | |
79 | end | |
80 | ||
81 | end | |
82 | end | |
83 | ||
84 | ||
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)) } | |
7d2dad89 | 93 | |
8cb5cda4 JM |
94 | # core.rb: defined using ruby |
95 | $core_ns.each do |k,v| repl_env.set(k,v) end | |
7d2dad89 | 96 | |
8cb5cda4 | 97 | # core.mal: defined using the language itself |
7d2dad89 JM |
98 | RE["(def! not (fn* (a) (if a false true)))"] |
99 | ||
86b689f3 | 100 | # repl loop |
718887c3 | 101 | while line = _readline("user> ") |
7d2dad89 JM |
102 | begin |
103 | puts REP[line] | |
104 | rescue Exception => e | |
105 | puts "Error: #{e}" | |
e17aef04 | 106 | puts "\t#{e.backtrace[0..100].join("\n\t")}" |
7d2dad89 JM |
107 | end |
108 | end |