Skip to content

Instantly share code, notes, and snippets.

@rhysd
Created December 12, 2015 08:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rhysd/f493f1fb9ba94d8f4639 to your computer and use it in GitHub Desktop.
Save rhysd/f493f1fb9ba94d8f4639 to your computer and use it in GitHub Desktop.
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