1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-20 01:57:09 +03:00
mal/crystal/reader.cr
2015-06-03 02:26:58 +09:00

80 lines
1.4 KiB
Crystal

require "./types"
class Reader
def initialize(@tokens)
@pos = 0
end
def peek
@tokens[@pos] rescue nil
end
def next
@tokens[@pos] rescue nil
ensure
@pos += 1
end
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
read_form r
ensure
unless r.peek.nil?
raise "expected EOF but got #{r.peek.to_s}"
end
end
end
def read_list(reader)
token = reader.next
raise "expected '('" unless token && token[0] == '('
forms = [] of Mal::Type
loop do
token = reader.peek
raise "expected ')' but got EOF" unless token
break if token[0] == ')'
forms << read_form reader
reader.peek
end
reader.next
forms
end
def read_atom(reader)
token = reader.next
raise "expected Atom but got EOF" unless token
case
when token =~ /^-?\d+$/ then token.to_i
when token[0] == '"' then token[1..-2].gsub(/\\"/, "\"")
when token == "true" then true
when token == "false" then false
when token == "nil" then nil
else Mal::Symbol.new token
end
end
def read_form(reader)
token = reader.peek
raise "unexpected EOF" unless token
case token[0]
when ';' then nil
when '(' then read_list reader
when ')' then raise "unexpected ')'"
else read_atom reader
end
end