Fix unescaping in matlab, miniMAL and rpython.
[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)) {
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 22end
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 30module 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 110end
111
112while line = my_readline("user> ")
113 begin
97d0deb1 114 puts Mal.rep(line)
b26be022 115 rescue e
116 STDERR.puts e
117 end
118end