diff --git a/src/core.cr b/src/core.cr index a5de839..337fe40 100644 --- a/src/core.cr +++ b/src/core.cr @@ -7,407 +7,408 @@ require "./printer" require "./reader" module Crisp + extend self + + macro calc_op(op) + -> (args : Array(Crisp::Type)) { + x, y = args[0].unwrap, args[1].unwrap + eval_error "invalid arguments for binary operator {{op.id}}" unless x.is_a?(Int32) && y.is_a?(Int32) + Crisp::Type.new(x {{op.id}} y) + } + end -macro calc_op(op) - -> (args : Array(Crisp::Type)) { - x, y = args[0].unwrap, args[1].unwrap - eval_error "invalid arguments for binary operator {{op.id}}" unless x.is_a?(Int32) && y.is_a?(Int32) - Crisp::Type.new(x {{op.id}} y) - } -end - -def self.list(args) - args.to_crisp_value -end + def list(args) + args.to_crisp_value + end -def self.list?(args) - args.first.unwrap.is_a? Crisp::List -end + def list?(args) + args.first.unwrap.is_a? Crisp::List + end -def self.empty?(args) - a = args.first.unwrap - a.is_a?(Array) ? a.empty? : false -end + def empty?(args) + a = args.first.unwrap + a.is_a?(Array) ? a.empty? : false + end -def self.count(args) - a = args.first.unwrap - case a - when Array - a.size as Int32 - when Nil - 0 - else - eval_error "invalid argument for function 'count'" + def count(args) + a = args.first.unwrap + case a + when Array + a.size as Int32 + when Nil + 0 + else + eval_error "invalid argument for function 'count'" + end end -end -def self.pr_str_(args) - args.map{|a| pr_str(a)}.join(" ") -end + def pr_str_(args) + args.map{|a| pr_str(a)}.join(" ") + end -def self.str(args) - args.map{|a| pr_str(a, false)}.join -end + def str(args) + args.map{|a| pr_str(a, false)}.join + end -def self.prn(args) - puts self.pr_str_(args) - nil -end + def prn(args) + puts self.pr_str_(args) + nil + end -def self.println(args) - puts args.map{|a| pr_str(a, false)}.join(" ") - nil -end + def println(args) + puts args.map{|a| pr_str(a, false)}.join(" ") + nil + end -def self.read_string(args) - head = args.first.unwrap - eval_error "argument of read-str must be string" unless head.is_a? String - read_str head -end + def read_string(args) + head = args.first.unwrap + eval_error "argument of read-str must be string" unless head.is_a? String + read_str head + end -def self.slurp(args) - head = args.first.unwrap - eval_error "argument of slurp must be string" unless head.is_a? String - begin - File.read head - rescue e : Errno - eval_error "no such file" + def slurp(args) + head = args.first.unwrap + eval_error "argument of slurp must be string" unless head.is_a? String + begin + File.read head + rescue e : Errno + eval_error "no such file" + end end -end -def self.cons(args) - head, tail = args[0] as Crisp::Type, args[1].unwrap - eval_error "2nd arg of cons must be list" unless tail.is_a? Array - ([head] + tail).to_crisp_value -end + def cons(args) + head, tail = args[0] as Crisp::Type, args[1].unwrap + eval_error "2nd arg of cons must be list" unless tail.is_a? Array + ([head] + tail).to_crisp_value + end -def self.concat(args) - args.each_with_object(Crisp::List.new) do |arg, list| - a = arg.unwrap - eval_error "arguments of concat must be list" unless a.is_a?(Array) - a.each{|e| list << e} + def concat(args) + args.each_with_object(Crisp::List.new) do |arg, list| + a = arg.unwrap + eval_error "arguments of concat must be list" unless a.is_a?(Array) + a.each{|e| list << e} + end end -end -def self.nth(args) - a0, a1 = args[0].unwrap, args[1].unwrap - eval_error "1st argument of nth must be list or vector" unless a0.is_a? Array - eval_error "2nd argument of nth must be integer" unless a1.is_a? Int32 - a0[a1] -end + def nth(args) + a0, a1 = args[0].unwrap, args[1].unwrap + eval_error "1st argument of nth must be list or vector" unless a0.is_a? Array + eval_error "2nd argument of nth must be integer" unless a1.is_a? Int32 + a0[a1] + end -def self.first(args) - a0 = args[0].unwrap + def first(args) + a0 = args[0].unwrap - return nil if a0.nil? - eval_error "1st argument of first must be list or vector or nil" unless a0.is_a? Array - a0.empty? ? nil : a0.first -end + return nil if a0.nil? + eval_error "1st argument of first must be list or vector or nil" unless a0.is_a? Array + a0.empty? ? nil : a0.first + end -def self.rest(args) - a0 = args[0].unwrap + def rest(args) + a0 = args[0].unwrap - return Crisp::List.new if a0.nil? - eval_error "1st argument of first must be list or vector or nil" unless a0.is_a? Array - return Crisp::List.new if a0.empty? - a0[1..-1].to_crisp_value -end + return Crisp::List.new if a0.nil? + eval_error "1st argument of first must be list or vector or nil" unless a0.is_a? Array + return Crisp::List.new if a0.empty? + a0[1..-1].to_crisp_value + end -def self.apply(args) - eval_error "apply must take at least 2 arguments" unless args.size >= 2 + def apply(args) + eval_error "apply must take at least 2 arguments" unless args.size >= 2 - head = args.first.unwrap - last = args.last.unwrap + head = args.first.unwrap + last = args.last.unwrap - eval_error "last argument of apply must be list or vector" unless last.is_a? Array + eval_error "last argument of apply must be list or vector" unless last.is_a? Array - case head - when Crisp::Closure - head.fn.call(args[1..-2] + last) - when Crisp::Func - head.call(args[1..-2] + last) - else - eval_error "1st argument of apply must be function or closure" + case head + when Crisp::Closure + head.fn.call(args[1..-2] + last) + when Crisp::Func + head.call(args[1..-2] + last) + else + eval_error "1st argument of apply must be function or closure" + end end -end -def self.map(args) - func = args.first.unwrap - list = args[1].unwrap + def map(args) + func = args.first.unwrap + list = args[1].unwrap - eval_error "2nd argument of map must be list or vector" unless list.is_a? Array + eval_error "2nd argument of map must be list or vector" unless list.is_a? Array - f = case func - when Crisp::Closure then func.fn - when Crisp::Func then func - else eval_error "1st argument of map must be function" - end + f = case func + when Crisp::Closure then func.fn + when Crisp::Func then func + else eval_error "1st argument of map must be function" + end - list.each_with_object(Crisp::List.new) do |elem, mapped| - mapped << f.call([elem]) + list.each_with_object(Crisp::List.new) do |elem, mapped| + mapped << f.call([elem]) + end end -end -def self.nil?(args) - args.first.unwrap.nil? -end - -def self.true?(args) - a = args.first.unwrap - a.is_a?(Bool) && a -end - -def self.false?(args) - a = args.first.unwrap - a.is_a?(Bool) && !a -end - -def self.symbol?(args) - args.first.unwrap.is_a?(Crisp::Symbol) -end + def nil?(args) + args.first.unwrap.nil? + end -def self.symbol(args) - head = args.first.unwrap - eval_error "1st argument of symbol function must be string" unless head.is_a? String - Crisp::Symbol.new head -end + def true?(args) + a = args.first.unwrap + a.is_a?(Bool) && a + end -def self.keyword(args) - head = args.first.unwrap - eval_error "1st argument of symbol function must be string" unless head.is_a? String - "\u029e" + head -end + def false?(args) + a = args.first.unwrap + a.is_a?(Bool) && !a + end -def self.keyword?(args) - head = args.first.unwrap - head.is_a?(String) && !head.empty? && head[0] == '\u029e' -end + def symbol?(args) + args.first.unwrap.is_a?(Crisp::Symbol) + end -def self.vector(args) - args.to_crisp_value(Crisp::Vector) -end + def symbol(args) + head = args.first.unwrap + eval_error "1st argument of symbol function must be string" unless head.is_a? String + Crisp::Symbol.new head + end -def self.vector?(args) - args.first.unwrap.is_a? Crisp::Vector -end + def keyword(args) + head = args.first.unwrap + eval_error "1st argument of symbol function must be string" unless head.is_a? String + "\u029e" + head + end -def self.hash_map(args) - eval_error "hash-map must take even number of arguments" unless args.size.even? - map = Crisp::HashMap.new - args.each_slice(2) do |kv| - k = kv[0].unwrap - eval_error "key must be string" unless k.is_a? String - map[k] = kv[1] + def keyword?(args) + head = args.first.unwrap + head.is_a?(String) && !head.empty? && head[0] == '\u029e' end - map -end -def self.map?(args) - args.first.unwrap.is_a? Crisp::HashMap -end + def vector(args) + args.to_crisp_value(Crisp::Vector) + end -def self.assoc(args) - head = args.first.unwrap - eval_error "1st argument of assoc must be hashmap" unless head.is_a? Crisp::HashMap - eval_error "assoc must take a list and even number of arguments" unless (args.size - 1).even? + def vector?(args) + args.first.unwrap.is_a? Crisp::Vector + end - map = Crisp::HashMap.new - head.each{|k, v| map[k] = v} + def hash_map(args) + eval_error "hash-map must take even number of arguments" unless args.size.even? + map = Crisp::HashMap.new + args.each_slice(2) do |kv| + k = kv[0].unwrap + eval_error "key must be string" unless k.is_a? String + map[k] = kv[1] + end + map + end - args[1..-1].each_slice(2) do |kv| - k = kv[0].unwrap - eval_error "key must be string" unless k.is_a? String - map[k] = kv[1] + def map?(args) + args.first.unwrap.is_a? Crisp::HashMap end - map -end + def assoc(args) + head = args.first.unwrap + eval_error "1st argument of assoc must be hashmap" unless head.is_a? Crisp::HashMap + eval_error "assoc must take a list and even number of arguments" unless (args.size - 1).even? -def self.dissoc(args) - head = args.first.unwrap - 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} - map = Crisp::HashMap.new - head.each{|k,v| map[k] = v} + args[1..-1].each_slice(2) do |kv| + k = kv[0].unwrap + eval_error "key must be string" unless k.is_a? String + map[k] = kv[1] + end - args[1..-1].each do |arg| - key = arg.unwrap - eval_error "key must be string" unless key.is_a? String - map.delete key + map end - map -end + def dissoc(args) + head = args.first.unwrap + eval_error "1st argument of assoc must be hashmap" unless head.is_a? Crisp::HashMap -def self.get(args) - a0, a1 = args[0].unwrap, args[1].unwrap - return nil unless a0.is_a? Crisp::HashMap - eval_error "2nd argument of get must be string" unless a1.is_a? String + map = Crisp::HashMap.new + head.each{|k,v| map[k] = v} - # a0[a1]? isn't available because type ofa0[a1] is infered NoReturn - a0.has_key?(a1) ? a0[a1] : nil -end + args[1..-1].each do |arg| + key = arg.unwrap + eval_error "key must be string" unless key.is_a? String + map.delete key + end -def self.contains?(args) - a0, a1 = args[0].unwrap, args[1].unwrap - eval_error "1st argument of get must be hashmap" unless a0.is_a? Crisp::HashMap - eval_error "2nd argument of get must be string" unless a1.is_a? String - a0.has_key? a1 -end + map + end -def self.keys(args) - head = args.first.unwrap - 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::Type.new(e)} -end + def get(args) + a0, a1 = args[0].unwrap, args[1].unwrap + return nil unless a0.is_a? Crisp::HashMap + eval_error "2nd argument of get must be string" unless a1.is_a? String -def self.vals(args) - head = args.first.unwrap - eval_error "1st argument of assoc must be hashmap" unless head.is_a? Crisp::HashMap - head.values.to_crisp_value -end + # a0[a1]? isn't available because type ofa0[a1] is infered NoReturn + a0.has_key?(a1) ? a0[a1] : nil + end -def self.sequential?(args) - args.first.unwrap.is_a? Array -end + def contains?(args) + a0, a1 = args[0].unwrap, args[1].unwrap + eval_error "1st argument of get must be hashmap" unless a0.is_a? Crisp::HashMap + eval_error "2nd argument of get must be string" unless a1.is_a? String + a0.has_key? a1 + end -def self.readline(args) - head = args.first.unwrap - eval_error "1st argument of readline must be string" unless head.is_a? String - Readline.readline head -end + def keys(args) + head = args.first.unwrap + 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::Type.new(e)} + end -def self.meta(args) - m = args.first.meta - m.nil? ? nil : m -end + def vals(args) + head = args.first.unwrap + eval_error "1st argument of assoc must be hashmap" unless head.is_a? Crisp::HashMap + head.values.to_crisp_value + end -def self.with_meta(args) - t = args.first.dup - t.meta = args[1] - t -end + def sequential?(args) + args.first.unwrap.is_a? Array + end -def self.atom(args) - Crisp::Atom.new args.first -end + def readline(args) + head = args.first.unwrap + eval_error "1st argument of readline must be string" unless head.is_a? String + Readline.readline head + end -def self.atom?(args) - args.first.unwrap.is_a? Crisp::Atom -end + def meta(args) + m = args.first.meta + m.nil? ? nil : m + end -def self.deref(args) - head = args.first.unwrap - eval_error "1st argument of deref must be atom" unless head.is_a? Crisp::Atom - head.val -end + def with_meta(args) + t = args.first.dup + t.meta = args[1] + t + end -def self.reset!(args) - head = args.first.unwrap - eval_error "1st argument of reset! must be atom" unless head.is_a? Crisp::Atom - head.val = args[1] -end + def atom(args) + Crisp::Atom.new args.first + end + + def atom?(args) + args.first.unwrap.is_a? Crisp::Atom + end -def self.swap!(args) - atom = args.first.unwrap - eval_error "1st argument of swap! must be atom" unless atom.is_a? Crisp::Atom + def deref(args) + head = args.first.unwrap + eval_error "1st argument of deref must be atom" unless head.is_a? Crisp::Atom + head.val + end - a = [atom.val] + args[2..-1] + def reset!(args) + head = args.first.unwrap + eval_error "1st argument of reset! must be atom" unless head.is_a? Crisp::Atom + head.val = args[1] + end - func = args[1].unwrap - case func - when Crisp::Func - atom.val = func.call a - when Crisp::Closure - atom.val = func.fn.call a - else - eval_error "2nd argumetn of swap! must be function" + def swap!(args) + atom = args.first.unwrap + eval_error "1st argument of swap! must be atom" unless atom.is_a? Crisp::Atom + + a = [atom.val] + args[2..-1] + + func = args[1].unwrap + case func + when Crisp::Func + atom.val = func.call a + when Crisp::Closure + atom.val = func.fn.call a + else + eval_error "2nd argumetn of swap! must be function" + end end -end -def self.conj(args) - seq = args.first.unwrap - case seq - when Crisp::List - (args[1..-1].reverse + seq).to_crisp_value - when Crisp::Vector - (seq + args[1..-1]).to_crisp_value(Crisp::Vector) - else - eval_error "1st argument of conj must be list or vector" + def conj(args) + seq = args.first.unwrap + case seq + when Crisp::List + (args[1..-1].reverse + seq).to_crisp_value + when Crisp::Vector + (seq + args[1..-1]).to_crisp_value(Crisp::Vector) + else + eval_error "1st argument of conj must be list or vector" + end end -end -def self.time_ms(args) - (Time.now.to_i.to_i32) * 1000 -end + def time_ms(args) + (Time.now.to_i.to_i32) * 1000 + end -# Note: -# Simply using ->self.some_func doesn't work -macro func(name) - -> (args : Array(Crisp::Type)) { Crisp::Type.new self.{{name.id}}(args) } -end + # Note: + # Simply using ->self.some_func doesn't work + macro func(name) + -> (args : Array(Crisp::Type)) { Crisp::Type.new self.{{name.id}}(args) } + end -macro rel_op(op) --> (args : Array(Crisp::Type)) { Crisp::Type.new (args[0] {{op.id}} args[1]) } -end + macro rel_op(op) + -> (args : Array(Crisp::Type)) { Crisp::Type.new (args[0] {{op.id}} args[1]) } + end -NS = { - "+" => calc_op(:+) - "-" => calc_op(:-) - "*" => calc_op(:*) - "/" => calc_op(:/) - "list" => func(:list) - "list?" => func(:list?) - "empty?" => func(:empty?) - "count" => func(:count) - "=" => rel_op(:==) - "<" => rel_op(:<) - ">" => rel_op(:>) - "<=" => rel_op(:<=) - ">=" => rel_op(:>=) - "pr-str" => func(:pr_str_) - "str" => func(:str) - "prn" => func(:prn) - "println" => func(:println) - "read-string" => func(:read_string) - "slurp" => func(:slurp) - "cons" => func(:cons) - "concat" => func(:concat) - "nth" => func(:nth) - "first" => func(:first) - "rest" => func(:rest) - "throw" => -> (args : Array(Crisp::Type)) { raise Crisp::RuntimeException.new args[0] } - "apply" => func(:apply) - "map" => func(:map) - "nil?" => func(:nil?) - "true?" => func(:true?) - "false?" => func(:false?) - "symbol?" => func(:symbol?) - "symbol" => func(:symbol) - "keyword" => func(:keyword) - "keyword?" => func(:keyword?) - "vector" => func(:vector) - "vector?" => func(:vector?) - "hash-map" => func(:hash_map) - "map?" => func(:map?) - "assoc" => func(:assoc) - "dissoc" => func(:dissoc) - "get" => func(:get) - "contains?" => func(:contains?) - "keys" => func(:keys) - "vals" => func(:vals) - "sequential?" => func(:sequential?) - "readline" => func(:readline) - "meta" => func(:meta) - "with-meta" => func(:with_meta) - "atom" => func(:atom) - "atom?" => func(:atom?) - "deref" => func(:deref) - "deref" => func(:deref) - "reset!" => func(:reset!) - "swap!" => func(:swap!) - "conj" => func(:conj) - "time-ms" => func(:time_ms) -} of String => Crisp::Func + NameSpace = { + "+" => calc_op(:+) + "-" => calc_op(:-) + "*" => calc_op(:*) + "/" => calc_op(:/) + "list" => func(:list) + "list?" => func(:list?) + "empty?" => func(:empty?) + "count" => func(:count) + "=" => rel_op(:==) + "<" => rel_op(:<) + ">" => rel_op(:>) + "<=" => rel_op(:<=) + ">=" => rel_op(:>=) + "pr-str" => func(:pr_str_) + "str" => func(:str) + "prn" => func(:prn) + "println" => func(:println) + "read-string" => func(:read_string) + "slurp" => func(:slurp) + "cons" => func(:cons) + "concat" => func(:concat) + "nth" => func(:nth) + "first" => func(:first) + "rest" => func(:rest) + "throw" => -> (args : Array(Crisp::Type)) { raise Crisp::RuntimeException.new args[0] } + "apply" => func(:apply) + "map" => func(:map) + "nil?" => func(:nil?) + "true?" => func(:true?) + "false?" => func(:false?) + "symbol?" => func(:symbol?) + "symbol" => func(:symbol) + "keyword" => func(:keyword) + "keyword?" => func(:keyword?) + "vector" => func(:vector) + "vector?" => func(:vector?) + "hash-map" => func(:hash_map) + "map?" => func(:map?) + "assoc" => func(:assoc) + "dissoc" => func(:dissoc) + "get" => func(:get) + "contains?" => func(:contains?) + "keys" => func(:keys) + "vals" => func(:vals) + "sequential?" => func(:sequential?) + "readline" => func(:readline) + "meta" => func(:meta) + "with-meta" => func(:with_meta) + "atom" => func(:atom) + "atom?" => func(:atom?) + "deref" => func(:deref) + "deref" => func(:deref) + "reset!" => func(:reset!) + "swap!" => func(:swap!) + "conj" => func(:conj) + "time-ms" => func(:time_ms) + } of String => Crisp::Func end diff --git a/src/error.cr b/src/error.cr index dde354c..b9a2a49 100644 --- a/src/error.cr +++ b/src/error.cr @@ -11,12 +11,12 @@ module Crisp super() end end -end -def eval_error(msg) - raise Crisp::EvalException.new msg -end + def self.eval_error(msg) + raise Crisp::EvalException.new msg + end -def parse_error(msg) - raise Crisp::ParseException.new msg + def self.parse_error(msg) + raise Crisp::ParseException.new msg + end end diff --git a/src/printer.cr b/src/printer.cr index ef88fe2..8b7b742 100644 --- a/src/printer.cr +++ b/src/printer.cr @@ -1,34 +1,38 @@ require "./types" -def pr_str(value, print_readably = true) - 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| pr_str(v, print_readably) as String}.join(" ")})" - when Crisp::Vector then "[#{value.map{|v| pr_str(v, print_readably) as String}.join(" ")}]" - when Crisp::Symbol then value.str.to_s - when Crisp::Func then "" - when Crisp::Closure then "" - when Crisp::HashMap - # step1_read_print.cr requires specifying type - "{#{value.map{|k, v| "#{pr_str(k, print_readably)} #{pr_str(v, print_readably)}" as String}.join(" ")}}" - when String - case - when value.empty?() - print_readably ? value.inspect : value - when value[0] == '\u029e' - ":#{value[1..-1]}" +module Crisp + extend self + + def pr_str(value, print_readably = true) + 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| pr_str(v, print_readably) as String}.join(" ")})" + when Crisp::Vector then "[#{value.map{|v| pr_str(v, print_readably) as String}.join(" ")}]" + when Crisp::Symbol then value.str.to_s + when Crisp::Func then "" + when Crisp::Closure then "" + when Crisp::HashMap + # step1_read_print.cr requires specifying type + "{#{value.map{|k, v| "#{pr_str(k, print_readably)} #{pr_str(v, print_readably)}" as String}.join(" ")}}" + when String + case + when value.empty?() + print_readably ? value.inspect : value + when value[0] == '\u029e' + ":#{value[1..-1]}" + else + print_readably ? value.inspect : value + end + when Crisp::Atom + "(atom #{pr_str(value.val, print_readably)})" else - print_readably ? value.inspect : value + raise "invalid CrispType: #{value.to_s}" end - when Crisp::Atom - "(atom #{pr_str(value.val, print_readably)})" - else - raise "invalid CrispType: #{value.to_s}" end -end -def pr_str(t : Crisp::Type, print_readably = true) - pr_str(t.unwrap, print_readably) + (t.macro? ? " (macro)" : "") + def pr_str(t : Crisp::Type, print_readably = true) + pr_str(t.unwrap, print_readably) + (t.macro? ? " (macro)" : "") + end end diff --git a/src/reader.cr b/src/reader.cr index ea31bd0..9dcdb1f 100644 --- a/src/reader.cr +++ b/src/reader.cr @@ -1,137 +1,142 @@ require "./types" require "./error" -class Reader - def initialize(@tokens) - @pos = 0 - end +module Crisp + extend self - def current_token - @tokens[@pos] rescue nil - end + class Reader + def initialize(@tokens) + @pos = 0 + end - def peek - t = current_token + def current_token + @tokens[@pos] rescue nil + end - if t && t[0] == ';' - @pos += 1 + def peek + t = current_token + + if t && t[0] == ';' + @pos += 1 + peek + else + t + end + end + + def next peek - else - t + ensure + @pos += 1 end - end - def next - peek - ensure - @pos += 1 - end + def read_sequence(init, open, close) + token = self.next + parse_error "expected '#{open}', got EOF" unless token + parse_error "expected '#{open}', got #{token}" unless token[0] == open - def read_sequence(init, open, close) - token = self.next - parse_error "expected '#{open}', got EOF" unless token - parse_error "expected '#{open}', got #{token}" unless token[0] == open + loop do + token = peek + parse_error "expected '#{close}', got EOF" unless token + break if token[0] == close - loop do - token = peek - parse_error "expected '#{close}', got EOF" unless token - break if token[0] == close + init << read_form + peek + end - init << read_form - peek + self.next + init end - self.next - init - end - - def read_list - Crisp::Type.new read_sequence(Crisp::List.new, '(', ')') - end + def read_list + Crisp::Type.new read_sequence(Crisp::List.new, '(', ')') + end - def read_vector - Crisp::Type.new read_sequence(Crisp::Vector.new, '[', ']') - end + def read_vector + Crisp::Type.new read_sequence(Crisp::Vector.new, '[', ']') + end - def read_hashmap - types = read_sequence([] of Crisp::Type, '{', '}') + def read_hashmap + types = read_sequence([] of Crisp::Type, '{', '}') - parse_error "odd number of elements for hash-map: #{types.size}" if types.size.odd? - map = Crisp::HashMap.new + parse_error "odd number of elements for hash-map: #{types.size}" if types.size.odd? + map = Crisp::HashMap.new - types.each_slice(2) do |kv| - k, v = kv[0].unwrap, kv[1] - case k - when String - map[k] = v - else - parse_error("key of hash-map must be string or keyword") + types.each_slice(2) do |kv| + k, v = kv[0].unwrap, kv[1] + case k + when String + map[k] = v + else + parse_error("key of hash-map must be string or keyword") + end end + + Crisp::Type.new map end - Crisp::Type.new map - end + def read_atom + token = self.next + parse_error "expected Atom but got EOF" unless token + + Crisp::Type.new case + when token =~ /^-?\d+$/ then token.to_i + when token == "true" then true + when token == "false" then false + when token == "nil" then nil + when token[0] == '"' then token[1..-2].gsub(/\\"/, "\"") + when token[0] == ':' then "\u029e#{token[1..-1]}" + else Crisp::Symbol.new token + end + end - def read_atom - token = self.next - parse_error "expected Atom but got EOF" unless token - - Crisp::Type.new case - when token =~ /^-?\d+$/ then token.to_i - when token == "true" then true - when token == "false" then false - when token == "nil" then nil - when token[0] == '"' then token[1..-2].gsub(/\\"/, "\"") - when token[0] == ':' then "\u029e#{token[1..-1]}" - else Crisp::Symbol.new token + def list_of(symname) + Crisp::List.new << gen_type(Crisp::Symbol, symname) << read_form end - end - def list_of(symname) - Crisp::List.new << gen_type(Crisp::Symbol, symname) << read_form - end + def read_form + token = peek - def read_form - token = peek - - parse_error "unexpected EOF" unless token - parse_error "unexpected comment" if token[0] == ';' - - Crisp::Type.new case token - when "(" then read_list - when ")" then parse_error "unexpected ')'" - when "[" then read_vector - when "]" then parse_error "unexpected ']'" - when "{" then read_hashmap - when "}" then parse_error "unexpected '}'" - when "'" then self.next; list_of("quote") - when "`" then self.next; list_of("quasiquote") - when "~" then self.next; list_of("unquote") - when "~@" then self.next; list_of("splice-unquote") - when "@" then self.next; list_of("deref") - when "^" - self.next - meta = read_form - list_of("with-meta") << meta - else read_atom + parse_error "unexpected EOF" unless token + parse_error "unexpected comment" if token[0] == ';' + + Crisp::Type.new case token + when "(" then read_list + when ")" then parse_error "unexpected ')'" + when "[" then read_vector + when "]" then parse_error "unexpected ']'" + when "{" then read_hashmap + when "}" then parse_error "unexpected '}'" + when "'" then self.next; list_of("quote") + when "`" then self.next; list_of("quasiquote") + when "~" then self.next; list_of("unquote") + when "~@" then self.next; list_of("splice-unquote") + when "@" then self.next; list_of("deref") + when "^" + self.next + meta = read_form + list_of("with-meta") << meta + else read_atom + end end - end -end + end -def tokenize(str) - regex = /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/ - str.scan(regex).map{|m| m[1]}.reject(&.empty?) -end + def tokenize(str) + regex = /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/ + str.scan(regex).map{|m| m[1]}.reject(&.empty?) + end -def read_str(str) - r = Reader.new(tokenize(str)) - begin - r.read_form - ensure - unless r.peek.nil? - raise Crisp::ParseException.new "expected EOF, got #{r.peek.to_s}" + def read_str(str) + r = Reader.new(tokenize(str)) + begin + r.read_form + ensure + unless r.peek.nil? + raise Crisp::ParseException.new "expected EOF, got #{r.peek.to_s}" + end end end + end diff --git a/src/types.cr b/src/types.cr index b8009a5..79a4d60 100644 --- a/src/types.cr +++ b/src/types.cr @@ -100,10 +100,10 @@ module Crisp end alias Func = Type::Func -end -macro gen_type(t, *args) - Crisp::Type.new {{t.id}}.new({{*args}}) + macro gen_type(t, *args) + Crisp::Type.new {{t.id}}.new({{*args}}) + end end class Array