eec5fc4f |
1 | #! /usr/bin/env crystal run |
2 | |
3 | require "./readline" |
4 | require "./reader" |
5 | require "./printer" |
6 | require "./types" |
7 | |
8 | # Note: |
9 | # Employed downcase names because Crystal prohibits uppercase names for methods |
10 | |
11 | def eval_error(msg) |
12 | raise Mal::EvalException.new msg |
13 | end |
14 | |
15 | def num_func(func) |
16 | -> (args : Array(Mal::Type)) { |
725ee0bd |
17 | x, y = args[0].unwrap, args[1].unwrap |
eec5fc4f |
18 | eval_error "invalid arguments" unless x.is_a?(Int32) && y.is_a?(Int32) |
725ee0bd |
19 | Mal::Type.new func.call(x, y) |
20 | } |
eec5fc4f |
21 | end |
22 | |
23 | $repl_env = { |
24 | "+" => num_func(->(x : Int32, y : Int32){ x + y }), |
25 | "-" => num_func(->(x : Int32, y : Int32){ x - y }), |
26 | "*" => num_func(->(x : Int32, y : Int32){ x * y }), |
27 | "/" => num_func(->(x : Int32, y : Int32){ x / y }), |
725ee0bd |
28 | } of String => Mal::Func |
eec5fc4f |
29 | |
725ee0bd |
30 | def eval_ast(a, env) |
31 | return a.map{|n| eval(n, env) as Mal::Type} if a.is_a? Mal::List |
32 | return a unless a |
33 | |
34 | ast = a.unwrap |
eec5fc4f |
35 | case ast |
36 | when Mal::Symbol |
725ee0bd |
37 | if env.has_key? ast.str |
38 | env[ast.str] |
eec5fc4f |
39 | else |
725ee0bd |
40 | eval_error "'#{ast.str}' not found" |
eec5fc4f |
41 | end |
42 | when Mal::List |
725ee0bd |
43 | ast.each_with_object(Mal::List.new){|n, l| l << eval(n, env)} |
eec5fc4f |
44 | when Mal::Vector |
45 | ast.each_with_object(Mal::Vector.new){|n, l| l << eval(n, env)} |
46 | when Mal::HashMap |
47 | ast.each{|k, v| ast[k] = eval(v, env)} |
48 | else |
49 | ast |
50 | end |
51 | end |
52 | |
53 | def read(str) |
54 | read_str str |
55 | end |
56 | |
725ee0bd |
57 | def eval(t, env) |
58 | Mal::Type.new case ast = t.unwrap |
59 | when Mal::List |
60 | eval_error "empty list" if ast.empty? |
eec5fc4f |
61 | |
725ee0bd |
62 | f = eval_ast(ast.first, env) |
63 | ast.shift(1) |
64 | args = eval_ast(ast, env) |
eec5fc4f |
65 | |
725ee0bd |
66 | if f.is_a?(Mal::Func) |
67 | f.call(args) |
eec5fc4f |
68 | else |
725ee0bd |
69 | eval_error "expected function symbol as the first symbol of list" |
eec5fc4f |
70 | end |
725ee0bd |
71 | else |
72 | eval_ast(t, env) |
73 | end |
eec5fc4f |
74 | end |
75 | |
76 | def print(result) |
77 | pr_str(result, true) |
78 | end |
79 | |
80 | def rep(str) |
81 | print(eval(read(str), $repl_env)) |
82 | end |
83 | |
84 | while line = my_readline("user> ") |
85 | begin |
86 | puts rep(line) |
87 | rescue e |
88 | STDERR.puts e |
89 | end |
90 | end |