crystal: fix early steps broken because of later refactoring
[jackhill/mal.git] / crystal / step2_eval.cr
CommitLineData
eec5fc4f 1#! /usr/bin/env crystal run
2
3require "./readline"
4require "./reader"
5require "./printer"
6require "./types"
7
8# Note:
9# Employed downcase names because Crystal prohibits uppercase names for methods
10
11def eval_error(msg)
12 raise Mal::EvalException.new msg
13end
14
15def 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 21end
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 30def 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
51end
52
53def read(str)
54 read_str str
55end
56
725ee0bd 57def 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 74end
75
76def print(result)
77 pr_str(result, true)
78end
79
80def rep(str)
81 print(eval(read(str), $repl_env))
82end
83
84while line = my_readline("user> ")
85 begin
86 puts rep(line)
87 rescue e
88 STDERR.puts e
89 end
90end