crystal: implement step6
[jackhill/mal.git] / crystal / step3_env.cr
CommitLineData
b26be022 1#! /usr/bin/env crystal run
2
3require "./readline"
4require "./reader"
5require "./printer"
6require "./types"
7require "./env"
8
9# Note:
10# Employed downcase names because Crystal prohibits uppercase names for methods
11
12def eval_error(msg)
13 raise Mal::EvalException.new msg
14end
15
16def 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
22end
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
30def 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
48end
49
50def read(str)
51 read_str str
52end
53
54def 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
94end
95
96def print(result)
97 pr_str(result, true)
98end
99
100def rep(str)
101 print(eval(read(str), $repl_env))
102end
103
104while line = my_readline("user> ")
105 begin
106 puts rep(line)
107 rescue e
108 STDERR.puts e
109 end
110end