Commit | Line | Data |
---|---|---|
718887c3 JM |
1 | $: << File.expand_path(File.dirname(__FILE__)) |
2 | require "mal_readline" | |
46dbc0d8 JM |
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)} | |
3e8a088f JM |
23 | when Hash |
24 | new_hm = {} | |
25 | ast.each{|k,v| new_hm[EVAL(k,env)] = EVAL(v, env)} | |
26 | new_hm | |
46dbc0d8 JM |
27 | else |
28 | ast | |
29 | end | |
30 | end | |
31 | ||
32 | def EVAL(ast, env) | |
33 | while true | |
34 | ||
3e8a088f JM |
35 | #puts "EVAL: #{_pr_str(ast, true)}" |
36 | ||
46dbc0d8 JM |
37 | if not ast.is_a? List |
38 | return eval_ast(ast, env) | |
39 | end | |
40 | ||
41 | # apply list | |
42 | a0,a1,a2,a3 = ast | |
43 | case a0 | |
44 | when :def! | |
45 | return env.set(a1, EVAL(a2, env)) | |
46 | when :"let*" | |
47 | let_env = Env.new(env) | |
48 | a1.each_slice(2) do |a,e| | |
49 | let_env.set(a, EVAL(e, let_env)) | |
50 | end | |
51 | return EVAL(a2, let_env) | |
52 | when :do | |
53 | eval_ast(ast[1..-2], env) | |
54 | ast = ast.last | |
55 | when :if | |
56 | cond = EVAL(a1, env) | |
57 | if not cond | |
58 | return nil if a3 == nil | |
59 | ast = a3 | |
60 | else | |
61 | ast = a2 | |
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)) | |
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 |