A fork of Crisp for HARP
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 

115 lignes
2.9 KiB

#! /usr/bin/env crystal run
require "./readline"
require "./reader"
require "./printer"
require "./types"
require "./env"
# Note:
# Employed downcase names because Crystal prohibits uppercase names for methods
def eval_error(msg)
raise Mal::EvalException.new msg
end
def num_func(func)
-> (args : Array(Mal::Type)) {
x, y = args[0].unwrap, args[1].unwrap
eval_error "invalid arguments" unless x.is_a?(Int32) && y.is_a?(Int32)
Mal::Type.new func.call(x, y)
}
end
$repl_env = Mal::Env.new nil
$repl_env.set("+", Mal::Type.new num_func(->(x : Int32, y : Int32){ x + y }))
$repl_env.set("-", Mal::Type.new num_func(->(x : Int32, y : Int32){ x - y }))
$repl_env.set("*", Mal::Type.new num_func(->(x : Int32, y : Int32){ x * y }))
$repl_env.set("/", Mal::Type.new num_func(->(x : Int32, y : Int32){ x / y }))
def eval_ast(a, env)
return a.map{|n| eval(n, env) } if a.is_a? Array
Mal::Type.new case ast = a.unwrap
when Mal::Symbol
if e = env.get(ast.str)
e
else
eval_error "'#{ast.str}' not found"
end
when Mal::List
ast.each_with_object(Mal::List.new){|n, l| l << eval(n, env)}
when Mal::Vector
ast.each_with_object(Mal::Vector.new){|n, l| l << eval(n, env)}
when Mal::HashMap
new_map = Mal::HashMap.new
ast.each{|k, v| new_map[k] = eval(v, env)}
new_map
else
ast
end
end
def read(str)
read_str str
end
def eval(t, env)
ast = t.unwrap
return eval_ast(t, env) unless ast.is_a?(Mal::List)
eval_error "empty list" if ast.empty?
sym = ast.first.unwrap
eval_error "first element of list must be a symbol" unless sym.is_a?(Mal::Symbol)
Mal::Type.new case sym.str
when "def!"
eval_error "wrong number of argument for 'def!'" unless ast.size == 3
a1 = ast[1].unwrap
eval_error "1st argument of 'def!' must be symbol" unless a1.is_a?(Mal::Symbol)
env.set(a1.str, eval(ast[2], env) as Mal::Type)
when "let*"
eval_error "wrong number of argument for 'def!'" unless ast.size == 3
bindings = ast[1].unwrap
eval_error "1st argument of 'let*' must be list or vector" unless bindings.is_a?(Array)
eval_error "size of binding list must be even" unless bindings.size.even?
new_env = Mal::Env.new env
bindings.each_slice(2) do |binding|
name, value = binding[0].unwrap, binding[1]
eval_error "name of binding must be specified as symbol" unless name.is_a?(Mal::Symbol)
new_env.set(name.str, eval(value, new_env))
end
eval(ast[2], new_env)
else
f = eval_ast(ast.first, env)
ast.shift(1)
args = eval_ast(ast, env)
if f.is_a?(Mal::Type) && (f2 = f.unwrap).is_a?(Mal::Func)
f2.call(args as Array(Mal::Type))
else
eval_error "expected function symbol as the first symbol of list"
end
end
end
def print(result)
pr_str(result, true)
end
def rep(str)
print(eval(read(str), $repl_env))
end
while line = my_readline("user> ")
begin
puts rep(line)
rescue e
STDERR.puts e
end
end