b26be022 |
1 | #! /usr/bin/env crystal run |
2 | |
3 | require "./readline" |
4 | require "./reader" |
5 | require "./printer" |
6 | require "./types" |
7 | require "./env" |
8 | |
9 | # Note: |
10 | # Employed downcase names because Crystal prohibits uppercase names for methods |
11 | |
12 | def eval_error(msg) |
13 | raise Mal::EvalException.new msg |
14 | end |
15 | |
16 | def num_func(func) |
17 | -> (args : Array(Mal::Type)) { |
18 | x, y = args[0], args[1] |
19 | eval_error "invalid arguments" unless x.is_a?(Int32) && y.is_a?(Int32) |
20 | func.call(x, y) as Mal::Type |
21 | } as Mal::Func |
22 | end |
23 | |
24 | $repl_env = Mal::Env.new nil |
25 | $repl_env.set("+", num_func(->(x : Int32, y : Int32){ x + y })) |
26 | $repl_env.set("-", num_func(->(x : Int32, y : Int32){ x - y })) |
27 | $repl_env.set("*", num_func(->(x : Int32, y : Int32){ x * y })) |
28 | $repl_env.set("/", num_func(->(x : Int32, y : Int32){ x / y })) |
29 | |
30 | def eval_ast(ast, env) |
31 | case ast |
32 | when Mal::Symbol |
33 | if e = env.get(ast.val) |
34 | e |
35 | else |
36 | eval_error "'#{ast.val}' not found" |
37 | end |
38 | when Mal::List |
39 | # ast.each_with_object(Mal::List.new){|n, l| l << eval(n, env) as Mal::Type} |
40 | ast.map{|n| eval(n, env) as Mal::Type} |
41 | when Mal::Vector |
42 | ast.each_with_object(Mal::Vector.new){|n, l| l << eval(n, env)} |
43 | when Mal::HashMap |
44 | ast.each{|k, v| ast[k] = eval(v, env)} |
45 | else |
46 | ast |
47 | end |
48 | end |
49 | |
50 | def read(str) |
51 | read_str str |
52 | end |
53 | |
54 | def eval(ast, env) |
55 | return eval_ast(ast, env) unless ast.is_a?(Mal::List) |
56 | |
57 | eval_error "empty list" if ast.empty? |
58 | |
59 | sym = ast.first |
60 | eval_error "first element of list must be a symbol" unless sym.is_a?(Mal::Symbol) |
61 | |
62 | case sym.val |
63 | when "def!" |
64 | eval_error "wrong number of argument for 'def!'" unless ast.size == 3 |
65 | a1 = ast[1] |
66 | eval_error "1st argument of 'def!' must be symbol" unless a1.is_a?(Mal::Symbol) |
67 | env.set(a1.val, eval(ast[2], env) as Mal::Type) |
68 | when "let*" |
69 | eval_error "wrong number of argument for 'def!'" unless ast.size == 3 |
70 | |
71 | bindings = ast[1] |
e014087d |
72 | eval_error "1st argument of 'let*' must be list or vector" unless bindings.is_a?(Array) |
b26be022 |
73 | eval_error "size of binding list must be even" unless bindings.size.even? |
74 | |
75 | new_env = Mal::Env.new env |
76 | bindings.each_slice(2) do |binding| |
77 | name, value = binding |
78 | eval_error "name of binding must be specified as symbol" unless name.is_a?(Mal::Symbol) |
79 | new_env.set(name.val, eval(value, new_env)) |
80 | end |
81 | |
82 | eval(ast[2], new_env) |
83 | else |
84 | f = eval_ast(sym, env) |
85 | ast.shift(1) |
86 | args = eval_ast(ast, env).each_with_object([] of Mal::Type){|e, a| a << e} |
87 | |
88 | if f.is_a?(Mal::Func) |
89 | f.call(args) |
90 | else |
91 | eval_error "expected function symbol as the first symbol of list" |
92 | end |
93 | end |
94 | end |
95 | |
96 | def print(result) |
97 | pr_str(result, true) |
98 | end |
99 | |
100 | def rep(str) |
101 | print(eval(read(str), $repl_env)) |
102 | end |
103 | |
104 | while line = my_readline("user> ") |
105 | begin |
106 | puts rep(line) |
107 | rescue e |
108 | STDERR.puts e |
109 | end |
110 | end |