2015-02-28 02:36:27 +03:00
|
|
|
require_relative "types"
|
2014-04-11 05:34:29 +04:00
|
|
|
|
|
|
|
class Reader
|
|
|
|
def initialize(tokens)
|
|
|
|
@position = 0
|
|
|
|
@tokens = tokens
|
|
|
|
end
|
|
|
|
def peek
|
|
|
|
return @tokens[@position]
|
|
|
|
end
|
|
|
|
def next
|
|
|
|
@position += 1
|
|
|
|
return @tokens[@position-1]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def tokenize(str)
|
2019-01-24 21:40:36 +03:00
|
|
|
re = /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]*)/
|
2014-04-11 08:08:42 +04:00
|
|
|
return str.scan(re).map{|m| m[0]}.select{ |t|
|
|
|
|
t != "" && t[0..0] != ";"
|
|
|
|
}
|
2014-04-11 05:34:29 +04:00
|
|
|
end
|
|
|
|
|
2015-10-31 06:05:49 +03:00
|
|
|
def parse_str(t) # trim and unescape
|
2017-09-26 17:12:29 +03:00
|
|
|
return t[1..-2].gsub(/\\./, {"\\\\" => "\\", "\\n" => "\n", "\\\"" => '"'})
|
2014-04-11 05:34:29 +04:00
|
|
|
end
|
|
|
|
|
|
|
|
def read_atom(rdr)
|
|
|
|
token = rdr.next
|
|
|
|
return case token
|
|
|
|
when /^-?[0-9]+$/ then token.to_i # integer
|
|
|
|
when /^-?[0-9][0-9.]*$/ then token.to_f # float
|
2019-05-09 00:45:08 +03:00
|
|
|
when /^"(?:\\.|[^\\"])*"$/ then parse_str(token) # string
|
|
|
|
when /^"/ then raise "expected '\"', got EOF"
|
2014-12-19 05:33:49 +03:00
|
|
|
when /^:/ then "\u029e" + token[1..-1] # keyword
|
2014-04-11 05:34:29 +04:00
|
|
|
when "nil" then nil
|
|
|
|
when "true" then true
|
|
|
|
when "false" then false
|
|
|
|
else token.to_sym # symbol
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def read_list(rdr, klass, start="(", last =")")
|
|
|
|
ast = klass.new
|
|
|
|
token = rdr.next()
|
|
|
|
if token != start
|
|
|
|
raise "expected '" + start + "'"
|
|
|
|
end
|
|
|
|
while (token = rdr.peek) != last
|
|
|
|
if not token
|
|
|
|
raise "expected '" + last + "', got EOF"
|
|
|
|
end
|
|
|
|
ast.push(read_form(rdr))
|
|
|
|
end
|
|
|
|
rdr.next
|
|
|
|
return ast
|
|
|
|
end
|
|
|
|
|
|
|
|
def read_form(rdr)
|
|
|
|
return case rdr.peek
|
|
|
|
when ";" then nil
|
2014-04-11 08:20:11 +04:00
|
|
|
when "'" then rdr.next; List.new [:quote, read_form(rdr)]
|
|
|
|
when "`" then rdr.next; List.new [:quasiquote, read_form(rdr)]
|
|
|
|
when "~" then rdr.next; List.new [:unquote, read_form(rdr)]
|
|
|
|
when "~@" then rdr.next; List.new [:"splice-unquote", read_form(rdr)]
|
2014-04-13 23:37:56 +04:00
|
|
|
when "^" then rdr.next; meta = read_form(rdr);
|
|
|
|
List.new [:"with-meta", read_form(rdr), meta]
|
|
|
|
when "@" then rdr.next; List.new [:deref, read_form(rdr)]
|
|
|
|
|
2014-04-11 05:34:29 +04:00
|
|
|
when "(" then read_list(rdr, List, "(", ")")
|
|
|
|
when ")" then raise "unexpected ')'"
|
|
|
|
when "[" then read_list(rdr, Vector, "[", "]")
|
|
|
|
when "]" then raise "unexpected ']'"
|
2014-04-13 23:37:56 +04:00
|
|
|
when "{" then Hash[read_list(rdr, List, "{", "}").each_slice(2).to_a]
|
2014-04-11 05:34:29 +04:00
|
|
|
when "}" then raise "unexpected '}'"
|
|
|
|
else read_atom(rdr)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def read_str(str)
|
|
|
|
tokens = tokenize(str)
|
2014-04-11 08:08:42 +04:00
|
|
|
return nil if tokens.size == 0
|
2014-04-11 05:34:29 +04:00
|
|
|
return read_form(Reader.new(tokens))
|
|
|
|
end
|
|
|
|
|