Browse Source

add all parts to Crisp module

master
rhysd 9 years ago
parent
commit
91424033b0
5 changed files with 480 additions and 470 deletions
  1. +331
    -330
      src/core.cr
  2. +6
    -6
      src/error.cr
  3. +31
    -27
      src/printer.cr
  4. +109
    -104
      src/reader.cr
  5. +3
    -3
      src/types.cr

+ 331
- 330
src/core.cr View File

@ -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? b">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
o">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

+ 6
- 6
src/error.cr View File

@ -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

+ 31
- 27
src/printer.cr View File

@ -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 "<function>"
when Crisp::Closure then "<closure>"
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 "<function>"
when Crisp::Closure then "<closure>"
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
b">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

+ 109
- 104
src/reader.cr View File

@ -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
vi">@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 = kp">self.next
parse_error "expected '#{open}', got EOF" unless token
parse_error "expected '#{open}', got #{token}" unless token[0] == open
loop do
token = n">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
n">init << read_form
peek
kp">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

+ 3
- 3
src/types.cr View File

@ -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

Loading…
Cancel
Save