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" | |
01e25489 JM |
7 | |
8 | # read | |
9 | def READ(str) | |
10 | return read_str(str) | |
11 | end | |
12 | ||
13 | # eval | |
14 | def pair?(x) | |
15 | return sequential?(x) && x.size > 0 | |
16 | end | |
17 | ||
18 | def quasiquote(ast) | |
19 | if not pair?(ast) | |
20 | return List.new [:quote, ast] | |
21 | elsif ast[0] == :unquote | |
22 | return ast[1] | |
23 | elsif pair?(ast[0]) && ast[0][0] == :"splice-unquote" | |
24 | return List.new [:concat, ast[0][1], quasiquote(ast.drop(1))] | |
25 | else | |
26 | return List.new [:cons, quasiquote(ast[0]), quasiquote(ast.drop(1))] | |
27 | end | |
28 | end | |
29 | ||
30 | def eval_ast(ast, env) | |
31 | return case ast | |
32 | when Symbol | |
33 | env.get(ast) | |
34 | when List | |
35 | List.new ast.map{|a| EVAL(a, env)} | |
36 | when Vector | |
37 | Vector.new ast.map{|a| EVAL(a, env)} | |
3e8a088f JM |
38 | when Hash |
39 | new_hm = {} | |
40 | ast.each{|k,v| new_hm[EVAL(k,env)] = EVAL(v, env)} | |
41 | new_hm | |
01e25489 JM |
42 | else |
43 | ast | |
44 | end | |
45 | end | |
46 | ||
47 | def EVAL(ast, env) | |
48 | while true | |
49 | ||
3e8a088f JM |
50 | #puts "EVAL: #{_pr_str(ast, true)}" |
51 | ||
01e25489 JM |
52 | if not ast.is_a? List |
53 | return eval_ast(ast, env) | |
54 | end | |
f53183a3 DM |
55 | if ast.empty? |
56 | return ast | |
57 | end | |
01e25489 JM |
58 | |
59 | # apply list | |
60 | a0,a1,a2,a3 = ast | |
61 | case a0 | |
62 | when :def! | |
63 | return env.set(a1, EVAL(a2, env)) | |
64 | when :"let*" | |
65 | let_env = Env.new(env) | |
66 | a1.each_slice(2) do |a,e| | |
67 | let_env.set(a, EVAL(e, let_env)) | |
68 | end | |
6301e0b6 JM |
69 | env = let_env |
70 | ast = a2 # Continue loop (TCO) | |
01e25489 JM |
71 | when :quote |
72 | return a1 | |
73 | when :quasiquote | |
6301e0b6 | 74 | ast = quasiquote(a1); # Continue loop (TCO) |
01e25489 JM |
75 | when :do |
76 | eval_ast(ast[1..-2], env) | |
6301e0b6 | 77 | ast = ast.last # Continue loop (TCO) |
01e25489 JM |
78 | when :if |
79 | cond = EVAL(a1, env) | |
80 | if not cond | |
81 | return nil if a3 == nil | |
6301e0b6 | 82 | ast = a3 # Continue loop (TCO) |
01e25489 | 83 | else |
6301e0b6 | 84 | ast = a2 # Continue loop (TCO) |
01e25489 JM |
85 | end |
86 | when :"fn*" | |
87 | return Function.new(a2, env, a1) {|*args| | |
79859c62 | 88 | EVAL(a2, Env.new(env, a1, List.new(args))) |
01e25489 JM |
89 | } |
90 | else | |
91 | el = eval_ast(ast, env) | |
92 | f = el[0] | |
93 | if f.class == Function | |
94 | ast = f.ast | |
6301e0b6 | 95 | env = f.gen_env(el.drop(1)) # Continue loop (TCO) |
01e25489 JM |
96 | else |
97 | return f[*el.drop(1)] | |
98 | end | |
99 | end | |
100 | ||
101 | end | |
102 | end | |
103 | ||
104 | ||
105 | def PRINT(exp) | |
106 | return _pr_str(exp, true) | |
107 | end | |
108 | ||
109 | # repl | |
110 | repl_env = Env.new | |
111 | RE = lambda {|str| EVAL(READ(str), repl_env) } | |
112 | REP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) } | |
01e25489 | 113 | |
8cb5cda4 JM |
114 | # core.rb: defined using ruby |
115 | $core_ns.each do |k,v| repl_env.set(k,v) end | |
116 | repl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)}) | |
86b689f3 | 117 | repl_env.set(:"*ARGV*", List.new(ARGV.slice(1,ARGV.length) || [])) |
01e25489 | 118 | |
8cb5cda4 | 119 | # core.mal: defined using the language itself |
01e25489 JM |
120 | RE["(def! not (fn* (a) (if a false true)))"] |
121 | RE["(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"] | |
122 | ||
01e25489 | 123 | if ARGV.size > 0 |
86b689f3 | 124 | RE["(load-file \"" + ARGV[0] + "\")"] |
01e25489 JM |
125 | exit 0 |
126 | end | |
86b689f3 JM |
127 | |
128 | # repl loop | |
718887c3 | 129 | while line = _readline("user> ") |
01e25489 JM |
130 | begin |
131 | puts REP[line] | |
132 | rescue Exception => e | |
133 | puts "Error: #{e}" | |
134 | puts "\t#{e.backtrace.join("\n\t")}" | |
135 | end | |
136 | end |