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)) { |
725ee0bd |
18 | x, y = args[0].unwrap, args[1].unwrap |
7546ae18 |
19 | eval_error "invalid arguments" unless x.is_a?(Int64) && y.is_a?(Int64) |
725ee0bd |
20 | Mal::Type.new func.call(x, y) |
21 | } |
b26be022 |
22 | end |
23 | |
24 | $repl_env = Mal::Env.new nil |
7546ae18 |
25 | $repl_env.set("+", Mal::Type.new num_func(->(x : Int64, y : Int64){ x + y })) |
26 | $repl_env.set("-", Mal::Type.new num_func(->(x : Int64, y : Int64){ x - y })) |
27 | $repl_env.set("*", Mal::Type.new num_func(->(x : Int64, y : Int64){ x * y })) |
28 | $repl_env.set("/", Mal::Type.new num_func(->(x : Int64, y : Int64){ x / y })) |
b26be022 |
29 | |
97d0deb1 |
30 | module Mal |
31 | extend self |
32 | |
33 | def eval_ast(a, env) |
34 | return a.map{|n| eval(n, env) } if a.is_a? Array |
35 | |
36 | Mal::Type.new case ast = a.unwrap |
37 | when Mal::Symbol |
38 | if e = env.get(ast.str) |
39 | e |
40 | else |
41 | eval_error "'#{ast.str}' not found" |
42 | end |
43 | when Mal::List |
44 | ast.each_with_object(Mal::List.new){|n, l| l << eval(n, env)} |
45 | when Mal::Vector |
46 | ast.each_with_object(Mal::Vector.new){|n, l| l << eval(n, env)} |
47 | when Mal::HashMap |
48 | new_map = Mal::HashMap.new |
49 | ast.each{|k, v| new_map[k] = eval(v, env)} |
50 | new_map |
b26be022 |
51 | else |
97d0deb1 |
52 | ast |
b26be022 |
53 | end |
b26be022 |
54 | end |
b26be022 |
55 | |
97d0deb1 |
56 | def read(str) |
57 | read_str str |
58 | end |
725ee0bd |
59 | |
97d0deb1 |
60 | def eval(t, env) |
61 | ast = t.unwrap |
b26be022 |
62 | |
97d0deb1 |
63 | return eval_ast(t, env) unless ast.is_a?(Mal::List) |
4525f9ea |
64 | return gen_type Mal::List if ast.empty? |
b26be022 |
65 | |
97d0deb1 |
66 | sym = ast.first.unwrap |
67 | eval_error "first element of list must be a symbol" unless sym.is_a?(Mal::Symbol) |
b26be022 |
68 | |
97d0deb1 |
69 | Mal::Type.new case sym.str |
70 | when "def!" |
71 | eval_error "wrong number of argument for 'def!'" unless ast.size == 3 |
72 | a1 = ast[1].unwrap |
73 | eval_error "1st argument of 'def!' must be symbol" unless a1.is_a?(Mal::Symbol) |
74 | env.set(a1.str, eval(ast[2], env) as Mal::Type) |
75 | when "let*" |
76 | eval_error "wrong number of argument for 'def!'" unless ast.size == 3 |
b26be022 |
77 | |
97d0deb1 |
78 | bindings = ast[1].unwrap |
79 | eval_error "1st argument of 'let*' must be list or vector" unless bindings.is_a?(Array) |
80 | eval_error "size of binding list must be even" unless bindings.size.even? |
b26be022 |
81 | |
97d0deb1 |
82 | new_env = Mal::Env.new env |
83 | bindings.each_slice(2) do |binding| |
84 | name, value = binding[0].unwrap, binding[1] |
85 | eval_error "name of binding must be specified as symbol" unless name.is_a?(Mal::Symbol) |
86 | new_env.set(name.str, eval(value, new_env)) |
87 | end |
b26be022 |
88 | |
97d0deb1 |
89 | eval(ast[2], new_env) |
b26be022 |
90 | else |
97d0deb1 |
91 | f = eval_ast(ast.first, env) |
92 | ast.shift(1) |
93 | args = eval_ast(ast, env) |
94 | |
95 | if f.is_a?(Mal::Type) && (f2 = f.unwrap).is_a?(Mal::Func) |
96 | f2.call(args as Array(Mal::Type)) |
97 | else |
98 | eval_error "expected function symbol as the first symbol of list" |
99 | end |
b26be022 |
100 | end |
101 | end |
b26be022 |
102 | |
97d0deb1 |
103 | def print(result) |
104 | pr_str(result, true) |
105 | end |
b26be022 |
106 | |
97d0deb1 |
107 | def rep(str) |
108 | print(eval(read(str), $repl_env)) |
109 | end |
b26be022 |
110 | end |
111 | |
112 | while line = my_readline("user> ") |
113 | begin |
97d0deb1 |
114 | puts Mal.rep(line) |
b26be022 |
115 | rescue e |
116 | STDERR.puts e |
117 | end |
118 | end |