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" | |
46dbc0d8 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 | |
46dbc0d8 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 | ||
46dbc0d8 JM |
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 | |
6301e0b6 JM |
50 | env = let_env |
51 | ast = a2 # Continue loop (TCO) | |
46dbc0d8 JM |
52 | when :do |
53 | eval_ast(ast[1..-2], env) | |
6301e0b6 | 54 | ast = ast.last # Continue loop (TCO) |
46dbc0d8 JM |
55 | when :if |
56 | cond = EVAL(a1, env) | |
57 | if not cond | |
58 | return nil if a3 == nil | |
6301e0b6 | 59 | ast = a3 # Continue loop (TCO) |
46dbc0d8 | 60 | else |
6301e0b6 | 61 | ast = a2 # Continue loop (TCO) |
46dbc0d8 JM |
62 | end |
63 | when :"fn*" | |
64 | return Function.new(a2, env, a1) {|*args| | |
79859c62 | 65 | EVAL(a2, Env.new(env, a1, List.new(args))) |
46dbc0d8 JM |
66 | } |
67 | else | |
68 | el = eval_ast(ast, env) | |
69 | f = el[0] | |
70 | if f.class == Function | |
71 | ast = f.ast | |
6301e0b6 | 72 | env = f.gen_env(el.drop(1)) # Continue loop (TCO) |
46dbc0d8 JM |
73 | else |
74 | return f[*el.drop(1)] | |
75 | end | |
76 | end | |
77 | ||
78 | end | |
79 | end | |
80 | ||
81 | ||
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)) } | |
46dbc0d8 | 90 | |
8cb5cda4 JM |
91 | # core.rb: defined using ruby |
92 | $core_ns.each do |k,v| repl_env.set(k,v) end | |
93 | repl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)}) | |
86b689f3 | 94 | repl_env.set(:"*ARGV*", List.new(ARGV.slice(1,ARGV.length) || [])) |
46dbc0d8 | 95 | |
8cb5cda4 | 96 | # core.mal: defined using the language itself |
46dbc0d8 JM |
97 | RE["(def! not (fn* (a) (if a false true)))"] |
98 | RE["(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"] | |
99 | ||
46dbc0d8 | 100 | if ARGV.size > 0 |
86b689f3 | 101 | RE["(load-file \"" + ARGV[0] + "\")"] |
46dbc0d8 JM |
102 | exit 0 |
103 | end | |
86b689f3 JM |
104 | |
105 | # repl loop | |
718887c3 | 106 | while line = _readline("user> ") |
46dbc0d8 JM |
107 | begin |
108 | puts REP[line] | |
109 | rescue Exception => e | |
110 | puts "Error: #{e}" | |
111 | puts "\t#{e.backtrace.join("\n\t")}" | |
112 | end | |
113 | end |