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