From 456610769b4ddb1b185400f174d6ce915055c16f Mon Sep 17 00:00:00 2001 From: Vitalii Elenhaupt Date: Tue, 24 Oct 2017 17:41:53 +0300 Subject: [PATCH 1/2] Crystal 0.23.1 --- README.md | 2 +- src/crisp/core.cr | 4 ++-- src/crisp/evaluator.cr | 10 +++++----- src/crisp/expr.cr | 6 ++++-- src/crisp/printer.cr | 4 ++-- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index f07f9e6..9267eaa 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Please see [mal test cases](https://github.com/rhysd/Crisp/tree/master/spec/cris ## Development Environment - OS X -- Crystal 0.7.1 ~ 0.7.3 or 0.7.7 ~ 0.11.0 or 0.17.4 or 0.18.4 +- Crystal 0.23.1 ## License diff --git a/src/crisp/core.cr b/src/crisp/core.cr index e21b626..7e9dbf9 100644 --- a/src/crisp/core.cr +++ b/src/crisp/core.cr @@ -34,7 +34,7 @@ module Crisp a = args.first.unwrap case a when Array - a.size as Int32 + a.size.as Int32 when Nil 0 else @@ -77,7 +77,7 @@ module Crisp end def cons(args) - head, tail = args[0] as Crisp::Expr, args[1].unwrap + head, tail = args[0].as Crisp::Expr, args[1].unwrap Crisp.eval_error "2nd arg of cons must be list" unless tail.is_a? Array ([head] + tail).to_crisp_value end diff --git a/src/crisp/evaluator.cr b/src/crisp/evaluator.cr index c2aafb5..bcd1f68 100644 --- a/src/crisp/evaluator.cr +++ b/src/crisp/evaluator.cr @@ -15,11 +15,11 @@ module Crisp -> (args : Array(Crisp::Expr)) { new_env = Crisp::Env.new(env, binds, args) eval(body, new_env) - } as Crisp::Func + }.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 @@ -94,8 +94,8 @@ module Crisp while macro_call?(ast, env) # Already checked in macro_call? - list = ast.unwrap as Crisp::List - func_sym = list[0].unwrap as Crisp::Symbol + list = ast.unwrap.as Crisp::List + func_sym = list[0].unwrap.as Crisp::Symbol func = env.get(func_sym.str).unwrap case func @@ -113,7 +113,7 @@ module Crisp macro invoke_list(l, env) f = eval({{l}}.first, {{env}}).unwrap - args = eval_ast({{l}}[1..-1], {{env}}) as Array + args = eval_ast({{l}}[1..-1], {{env}}).as Array case f when Crisp::Closure diff --git a/src/crisp/expr.cr b/src/crisp/expr.cr index eccf59b..9be1dfb 100644 --- a/src/crisp/expr.cr +++ b/src/crisp/expr.cr @@ -13,6 +13,8 @@ module Crisp end end + class Expr; end + class List < Array(Expr) end @@ -34,7 +36,7 @@ module Crisp class Closure property :ast, :params, :env, :fn - def initialize(@ast : Expr, @params : List | Vector, @env : Env, @fn : Func) + def initialize(@ast : Expr, @params : Array(Crisp::Expr), @env : Env, @fn : Func) end end @@ -46,7 +48,7 @@ module Crisp def initialize(@val : Type) @is_macro = false - @meta = nil as Expr? + @meta = nil.as Expr? end def initialize(other : Expr) diff --git a/src/crisp/printer.cr b/src/crisp/printer.cr index 15311d5..88ba11b 100644 --- a/src/crisp/printer.cr +++ b/src/crisp/printer.cr @@ -10,8 +10,8 @@ module Crisp 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::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 "" when Crisp::Closure then "" From eb8466793a5c32bfe687238f3963a794b4515359 Mon Sep 17 00:00:00 2001 From: Vitalii Elenhaupt Date: Tue, 24 Oct 2017 17:42:56 +0300 Subject: [PATCH 2/2] Autoformat sources --- spec/helper.cr | 1 - src/crisp/core.cr | 19 ++--- src/crisp/env.cr | 6 +- src/crisp/error.cr | 1 + src/crisp/evaluator.cr | 178 +++++++++++++++++++-------------------- src/crisp/expr.cr | 6 +- src/crisp/interpreter.cr | 6 +- src/crisp/printer.cr | 14 +-- src/crisp/reader.cr | 7 +- 9 files changed, 114 insertions(+), 124 deletions(-) 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 7e9dbf9..95fcd48 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_value?), @@ -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 3cc6306..5e95dd8 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 d24bda1..c43d952 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 : Crisp::Expr) super() end diff --git a/src/crisp/evaluator.cr b/src/crisp/evaluator.cr index bcd1f68..7a7056e 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,9 +50,9 @@ 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 @@ -62,7 +61,7 @@ module Crisp # ("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 +91,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 +147,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 9be1dfb..6931310 100644 --- a/src/crisp/expr.cr +++ b/src/crisp/expr.cr @@ -5,6 +5,7 @@ module Crisp class Symbol property :str + def initialize(@str : String) end @@ -26,6 +27,7 @@ module Crisp class Atom property :val + def initialize(@val : Expr) end @@ -36,6 +38,7 @@ module Crisp class Closure property :ast, :params, :env, :fn + def initialize(@ast : Expr, @params : Array(Crisp::Expr), @env : Env, @fn : Func) end end @@ -109,7 +112,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 88ba11b..6769418 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 "" when Crisp::Closure then "" 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 224647e..f172499 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 -