-
-
Save rhysd/f493f1fb9ba94d8f4639 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/spec/helper.cr b/spec/helper.cr | |
index 46caee2..df83a00 100644 | |
--- a/spec/helper.cr | |
+++ b/spec/helper.cr | |
@@ -1,3 +1,2 @@ | |
require "spec" | |
require "../src/crisp" | |
- | |
diff --git a/src/crisp/core.cr b/src/crisp/core.cr | |
index fe2b148..19520a2 100644 | |
--- a/src/crisp/core.cr | |
+++ b/src/crisp/core.cr | |
@@ -43,11 +43,11 @@ module Crisp | |
end | |
def pr_str(args) | |
- args.map{|a| Printer.new.print(a)}.join(" ") | |
+ args.map { |a| Printer.new.print(a) }.join(" ") | |
end | |
def str(args) | |
- args.map{|a| Printer.new(false).print(a)}.join | |
+ args.map { |a| Printer.new(false).print(a) }.join | |
end | |
def prn(args) | |
@@ -56,7 +56,7 @@ module Crisp | |
end | |
def println(args) | |
- puts args.map{|a| Printer.new(false).print(a)}.join(" ") | |
+ puts args.map { |a| Printer.new(false).print(a) }.join(" ") | |
nil | |
end | |
@@ -86,7 +86,7 @@ module Crisp | |
args.each_with_object(Crisp::List.new) do |arg, list| | |
a = arg.unwrap | |
Crisp.eval_error "arguments of concat must be list" unless a.is_a?(Array) | |
- a.each{|e| list << e} | |
+ a.each { |e| list << e } | |
end | |
end | |
@@ -141,7 +141,7 @@ module Crisp | |
f = case func | |
when Crisp::Closure then func.fn | |
when Crisp::Func then func | |
- else Crisp.eval_error "1st argument of map must be function" | |
+ else Crisp.eval_error "1st argument of map must be function" | |
end | |
list.each_with_object(Crisp::List.new) do |elem, mapped| | |
@@ -213,7 +213,7 @@ module Crisp | |
Crisp.eval_error "assoc must take a list and even number of arguments" unless (args.size - 1).even? | |
map = Crisp::HashMap.new | |
- head.each{|k, v| map[k] = v} | |
+ head.each { |k, v| map[k] = v } | |
args[1..-1].each_slice(2) do |kv| | |
k = kv[0].unwrap | |
@@ -229,7 +229,7 @@ module Crisp | |
Crisp.eval_error "1st argument of assoc must be hashmap" unless head.is_a? Crisp::HashMap | |
map = Crisp::HashMap.new | |
- head.each{|k,v| map[k] = v} | |
+ head.each { |k, v| map[k] = v } | |
args[1..-1].each do |arg| | |
key = arg.unwrap | |
@@ -259,7 +259,7 @@ module Crisp | |
def keys(args) | |
head = args.first.unwrap | |
Crisp.eval_error "1st argument of assoc must be hashmap" unless head.is_a? Crisp::HashMap | |
- head.keys.each_with_object(Crisp::List.new){|e,l| l << Crisp::Expr.new(e)} | |
+ head.keys.each_with_object(Crisp::List.new) { |e, l| l << Crisp::Expr.new(e) } | |
end | |
def vals(args) | |
@@ -377,7 +377,7 @@ module Crisp | |
"nth" => func(:nth), | |
"first" => func(:first), | |
"rest" => func(:rest), | |
- "throw" => -> (args : Array(Crisp::Expr)) { raise Crisp::RuntimeException.new args[0] }, | |
+ "throw" => ->(args : Array(Crisp::Expr)) { raise Crisp::RuntimeException.new args[0] }, | |
"apply" => func(:apply), | |
"map" => func(:map), | |
"nil?" => func(:nil?), | |
@@ -410,5 +410,4 @@ module Crisp | |
"conj" => func(:conj), | |
"time-ms" => func(:time_ms), | |
} of String => Crisp::Func | |
- | |
end | |
diff --git a/src/crisp/env.cr b/src/crisp/env.cr | |
index 1dea91c..1c1284c 100644 | |
--- a/src/crisp/env.cr | |
+++ b/src/crisp/env.cr | |
@@ -2,7 +2,6 @@ require "./expr" | |
require "./error" | |
module Crisp | |
- | |
class Env | |
property data | |
@@ -23,10 +22,10 @@ module Crisp | |
if sym.str == "&" | |
Crisp.eval_error "missing variable parameter name" if binds.size == idx | |
- next_param = binds[idx+1].unwrap | |
+ next_param = binds[idx + 1].unwrap | |
Crisp.eval_error "bind name must be symbol" unless next_param.is_a? Crisp::Symbol | |
var_args = Crisp::List.new | |
- exprs[idx..-1].each{|e| var_args << e} if idx < exprs.size | |
+ exprs[idx..-1].each { |e| var_args << e } if idx < exprs.size | |
@data[next_param.str] = Crisp::Expr.new var_args | |
break | |
end | |
@@ -63,5 +62,4 @@ module Crisp | |
e.data[key] | |
end | |
end | |
- | |
end | |
diff --git a/src/crisp/error.cr b/src/crisp/error.cr | |
index c715676..449a3e7 100644 | |
--- a/src/crisp/error.cr | |
+++ b/src/crisp/error.cr | |
@@ -9,6 +9,7 @@ module Crisp | |
class RuntimeException < Exception | |
getter :thrown | |
+ | |
def initialize(@thrown) | |
super() | |
end | |
diff --git a/src/crisp/evaluator.cr b/src/crisp/evaluator.cr | |
index c2aafb5..01898af 100644 | |
--- a/src/crisp/evaluator.cr | |
+++ b/src/crisp/evaluator.cr | |
@@ -9,34 +9,33 @@ require "./core" | |
require "./error" | |
module Crisp | |
- | |
class Evaluator | |
def func_of(env, binds, body) | |
- -> (args : Array(Crisp::Expr)) { | |
- new_env = Crisp::Env.new(env, binds, args) | |
- eval(body, new_env) | |
+ ->(args : Array(Crisp::Expr)) { | |
+ new_env = Crisp::Env.new(env, binds, args) | |
+ eval(body, new_env) | |
} as Crisp::Func | |
end | |
def eval_ast(ast, env) | |
- return ast.map{|n| eval(n, env) as Crisp::Expr} if ast.is_a? Array | |
+ return ast.map { |n| eval(n, env) as Crisp::Expr } if ast.is_a? Array | |
val = ast.unwrap | |
Crisp::Expr.new case val | |
when Crisp::Symbol | |
if e = env.get(val.str) | |
- e | |
+ e | |
else | |
- Crisp.eval_error "'#{val.str}' not found" | |
+ Crisp.eval_error "'#{val.str}' not found" | |
end | |
when Crisp::List | |
- val.each_with_object(Crisp::List.new){|n, l| l << eval(n, env)} | |
+ val.each_with_object(Crisp::List.new) { |n, l| l << eval(n, env) } | |
when Crisp::Vector | |
- val.each_with_object(Crisp::Vector.new){|n, l| l << eval(n, env)} | |
+ val.each_with_object(Crisp::Vector.new) { |n, l| l << eval(n, env) } | |
when Crisp::HashMap | |
new_map = Crisp::HashMap.new | |
- val.each{|k, v| new_map[k] = eval(v, env)} | |
+ val.each { |k, v| new_map[k] = eval(v, env) } | |
new_map | |
else | |
val | |
@@ -51,18 +50,17 @@ module Crisp | |
list = ast.unwrap | |
unless pair?(list) | |
- return Crisp::Expr.new( | |
- Crisp::List.new << gen_type(Crisp::Symbol, "quote") << ast | |
- ) | |
+ return Crisp::Expr.new( | |
+ Crisp::List.new << gen_type(Crisp::Symbol, "quote") << ast | |
+ ) | |
end | |
head = list.first.unwrap | |
- case | |
- # ("unquote" ...) | |
+ case # ("unquote" ...) | |
when head.is_a?(Crisp::Symbol) && head.str == "unquote" | |
list[1] | |
- # (("splice-unquote" ...) ...) | |
+ # (("splice-unquote" ...) ...) | |
when pair?(head) && (arg0 = head.first.unwrap).is_a?(Crisp::Symbol) && arg0.str == "splice-unquote" | |
tail = Crisp::Expr.new list[1..-1].to_crisp_value | |
Crisp::Expr.new( | |
@@ -92,7 +90,6 @@ module Crisp | |
def macroexpand(ast, env) | |
while macro_call?(ast, env) | |
- | |
# Already checked in macro_call? | |
list = ast.unwrap as Crisp::List | |
func_sym = list[0].unwrap as Crisp::Symbol | |
@@ -149,85 +146,83 @@ module Crisp | |
return invoke_list(list, env) unless head.is_a? Crisp::Symbol | |
return Crisp::Expr.new case head.str | |
- when "def!" | |
- Crisp.eval_error "wrong number of argument for 'def!'" unless list.size == 3 | |
- a1 = list[1].unwrap | |
- Crisp.eval_error "1st argument of 'def!' must be symbol: #{a1}" unless a1.is_a? Crisp::Symbol | |
- env.set(a1.str, eval(list[2], env)) | |
- when "let*" | |
- Crisp.eval_error "wrong number of argument for 'def!'" unless list.size == 3 | |
- | |
- bindings = list[1].unwrap | |
- Crisp.eval_error "1st argument of 'let*' must be list or vector" unless bindings.is_a? Array | |
- Crisp.eval_error "size of binding list must be even" unless bindings.size.even? | |
- | |
- new_env = Crisp::Env.new env | |
- bindings.each_slice(2) do |binding| | |
- key, value = binding | |
- name = key.unwrap | |
- Crisp.eval_error "name of binding must be specified as symbol #{name}" unless name.is_a? Crisp::Symbol | |
- new_env.set(name.str, eval(value, new_env)) | |
- end | |
- | |
- ast, env = list[2], new_env | |
- next # TCO | |
- when "do" | |
- if list.empty? | |
- ast = Crisp::Expr.new nil | |
- next | |
- end | |
- | |
- eval_ast(list[1..-2].to_crisp_value, env) | |
- ast = list.last | |
- next # TCO | |
- when "if" | |
- ast = unless eval(list[1], env).unwrap | |
- list.size >= 4 ? list[3] : Crisp::Expr.new(nil) | |
- else | |
- list[2] | |
- end | |
- next # TCO | |
- when "fn*" | |
- params = list[1].unwrap | |
- unless params.is_a? Array | |
- Crisp.eval_error "'fn*' parameters must be list or vector: #{params}" | |
- end | |
- Crisp::Closure.new(list[2], params, env, func_of(env, params, list[2])) | |
- when "quote" | |
- list[1] | |
- when "quasiquote" | |
- ast = quasiquote list[1] | |
- next # TCO | |
- when "defmacro!" | |
- Crisp.eval_error "wrong number of argument for 'defmacro!'" unless list.size == 3 | |
- a1 = list[1].unwrap | |
- Crisp.eval_error "1st argument of 'defmacro!' must be symbol: #{a1}" unless a1.is_a? Crisp::Symbol | |
- env.set(a1.str, eval(list[2], env).tap{|n| n.is_macro = true}) | |
- when "macroexpand" | |
- macroexpand(list[1], env) | |
- when "try*" | |
- catch_list = list[2].unwrap | |
- return eval(list[1], env) unless catch_list.is_a? Crisp::List | |
- | |
- catch_head = catch_list.first.unwrap | |
- return eval(list[1], env) unless catch_head.is_a? Crisp::Symbol | |
- return eval(list[1], env) unless catch_head.str == "catch*" | |
- | |
- begin | |
- eval(list[1], env) | |
- rescue e : Crisp::RuntimeException | |
- new_env = Crisp::Env.new(env, [catch_list[1]], [e.thrown]) | |
- eval(catch_list[2], new_env) | |
- rescue e | |
- new_env = Crisp::Env.new(env, [catch_list[1]], [Crisp::Expr.new e.message]) | |
- eval(catch_list[2], new_env) | |
- end | |
+ when "def!" | |
+ Crisp.eval_error "wrong number of argument for 'def!'" unless list.size == 3 | |
+ a1 = list[1].unwrap | |
+ Crisp.eval_error "1st argument of 'def!' must be symbol: #{a1}" unless a1.is_a? Crisp::Symbol | |
+ env.set(a1.str, eval(list[2], env)) | |
+ when "let*" | |
+ Crisp.eval_error "wrong number of argument for 'def!'" unless list.size == 3 | |
+ | |
+ bindings = list[1].unwrap | |
+ Crisp.eval_error "1st argument of 'let*' must be list or vector" unless bindings.is_a? Array | |
+ Crisp.eval_error "size of binding list must be even" unless bindings.size.even? | |
+ | |
+ new_env = Crisp::Env.new env | |
+ bindings.each_slice(2) do |binding| | |
+ key, value = binding | |
+ name = key.unwrap | |
+ Crisp.eval_error "name of binding must be specified as symbol #{name}" unless name.is_a? Crisp::Symbol | |
+ new_env.set(name.str, eval(value, new_env)) | |
+ end | |
+ | |
+ ast, env = list[2], new_env | |
+ next # TCO | |
+ when "do" | |
+ if list.empty? | |
+ ast = Crisp::Expr.new nil | |
+ next | |
+ end | |
+ | |
+ eval_ast(list[1..-2].to_crisp_value, env) | |
+ ast = list.last | |
+ next # TCO | |
+ when "if" | |
+ ast = unless eval(list[1], env).unwrap | |
+ list.size >= 4 ? list[3] : Crisp::Expr.new(nil) | |
else | |
- invoke_list(list, env) | |
+ list[2] | |
end | |
+ next # TCO | |
+ when "fn*" | |
+ params = list[1].unwrap | |
+ unless params.is_a? Array | |
+ Crisp.eval_error "'fn*' parameters must be list or vector: #{params}" | |
+ end | |
+ Crisp::Closure.new(list[2], params, env, func_of(env, params, list[2])) | |
+ when "quote" | |
+ list[1] | |
+ when "quasiquote" | |
+ ast = quasiquote list[1] | |
+ next # TCO | |
+ when "defmacro!" | |
+ Crisp.eval_error "wrong number of argument for 'defmacro!'" unless list.size == 3 | |
+ a1 = list[1].unwrap | |
+ Crisp.eval_error "1st argument of 'defmacro!' must be symbol: #{a1}" unless a1.is_a? Crisp::Symbol | |
+ env.set(a1.str, eval(list[2], env).tap { |n| n.is_macro = true }) | |
+ when "macroexpand" | |
+ macroexpand(list[1], env) | |
+ when "try*" | |
+ catch_list = list[2].unwrap | |
+ return eval(list[1], env) unless catch_list.is_a? Crisp::List | |
+ | |
+ catch_head = catch_list.first.unwrap | |
+ return eval(list[1], env) unless catch_head.is_a? Crisp::Symbol | |
+ return eval(list[1], env) unless catch_head.str == "catch*" | |
+ | |
+ begin | |
+ eval(list[1], env) | |
+ rescue e : Crisp::RuntimeException | |
+ new_env = Crisp::Env.new(env, [catch_list[1]], [e.thrown]) | |
+ eval(catch_list[2], new_env) | |
+ rescue e | |
+ new_env = Crisp::Env.new(env, [catch_list[1]], [Crisp::Expr.new e.message]) | |
+ eval(catch_list[2], new_env) | |
+ end | |
+ else | |
+ invoke_list(list, env) | |
+ end | |
end | |
end | |
- | |
end | |
- | |
end | |
diff --git a/src/crisp/expr.cr b/src/crisp/expr.cr | |
index 0b6ab54..09a6a15 100644 | |
--- a/src/crisp/expr.cr | |
+++ b/src/crisp/expr.cr | |
@@ -5,6 +5,7 @@ module Crisp | |
class Symbol | |
property :str | |
+ | |
def initialize(@str) | |
end | |
@@ -24,6 +25,7 @@ module Crisp | |
class Atom | |
property :val | |
+ | |
def initialize(@val) | |
end | |
@@ -34,6 +36,7 @@ module Crisp | |
class Closure | |
property :ast, :params, :env, :fn | |
+ | |
def initialize(@ast, @params, @env, @fn) | |
end | |
end | |
@@ -110,7 +113,6 @@ end | |
class Array | |
def to_crisp_value(t = Crisp::List) | |
- each_with_object(t.new){|e, l| l << e} | |
+ each_with_object(t.new) { |e, l| l << e } | |
end | |
end | |
- | |
diff --git a/src/crisp/interpreter.cr b/src/crisp/interpreter.cr | |
index e9cbcaa..970eb36 100644 | |
--- a/src/crisp/interpreter.cr | |
+++ b/src/crisp/interpreter.cr | |
@@ -10,15 +10,14 @@ require "./error" | |
require "./evaluator" | |
module Crisp | |
- | |
class Interpreter | |
def initialize(args = nil) | |
@printer = Printer.new | |
@evaluator = Evaluator.new | |
@env = Crisp::Env.new | |
- Crisp::NameSpace.each{|k,v| @env.set(k, Crisp::Expr.new(v))} | |
- @env.set("eval", Crisp::Expr.new -> (args: Array(Crisp::Expr)){ @evaluator.eval(args[0], @env) }) | |
+ Crisp::NameSpace.each { |k, v| @env.set(k, Crisp::Expr.new(v)) } | |
+ @env.set("eval", Crisp::Expr.new ->(args : Array(Crisp::Expr)) { @evaluator.eval(args[0], @env) }) | |
eval_string "(def! not (fn* (a) (if a false true)))" | |
eval_string "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" | |
@@ -73,4 +72,3 @@ module Crisp | |
end | |
end | |
end | |
- | |
diff --git a/src/crisp/printer.cr b/src/crisp/printer.cr | |
index 15311d5..813dd42 100644 | |
--- a/src/crisp/printer.cr | |
+++ b/src/crisp/printer.cr | |
@@ -7,19 +7,19 @@ module Crisp | |
def print(value) | |
case value | |
- when Nil then "nil" | |
- when Bool then value.to_s | |
- when Int32 then value.to_s | |
- when Crisp::List then "(#{value.map{|v| print(v) as String}.join(" ")})" | |
- when Crisp::Vector then "[#{value.map{|v| print(v) as String}.join(" ")}]" | |
+ when Nil then "nil" | |
+ when Bool then value.to_s | |
+ when Int32 then value.to_s | |
+ when Crisp::List then "(#{value.map { |v| print(v) as String }.join(" ")})" | |
+ when Crisp::Vector then "[#{value.map { |v| print(v) as String }.join(" ")}]" | |
when Crisp::Symbol then value.str.to_s | |
when Crisp::Func then "<function>" | |
when Crisp::Closure then "<closure>" | |
when Crisp::HashMap | |
- "{#{value.map{|k, v| "#{print(k)} #{print(v)}"}.join(" ")}}" | |
+ "{#{value.map { |k, v| "#{print(k)} #{print(v)}" }.join(" ")}}" | |
when String | |
case | |
- when value.empty?() | |
+ when value.empty? | |
@print_readably ? value.inspect : value | |
when value[0] == '\u029e' | |
":#{value[1..-1]}" | |
diff --git a/src/crisp/reader.cr b/src/crisp/reader.cr | |
index 7efc5c5..ea00755 100644 | |
--- a/src/crisp/reader.cr | |
+++ b/src/crisp/reader.cr | |
@@ -33,7 +33,7 @@ module Crisp | |
def read_sequence(init, open, close) | |
token = self.next | |
Crisp.parse_error "expected '#{open}', got EOF" unless token | |
- Crisp.parse_error "expected '#{open}', got #{token}" unless token[0] == open | |
+ Crisp.parse_error "expected '#{open}', got #{token}" unless token[0] == open | |
loop do | |
token = peek | |
@@ -119,12 +119,11 @@ module Crisp | |
else read_atom | |
end | |
end | |
- | |
end | |
def tokenize(str) | |
regex = /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/ | |
- str.scan(regex).map{|m| m[1]}.reject(&.empty?) | |
+ str.scan(regex).map { |m| m[1] }.reject(&.empty?) | |
end | |
def read_str(str) | |
@@ -137,6 +136,4 @@ module Crisp | |
end | |
end | |
end | |
- | |
end | |
- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment