Commit | Line | Data |
---|---|---|
107d9694 | 1 | require_relative "types" |
f705f0fc JM |
2 | |
3 | class Reader | |
4 | def initialize(tokens) | |
5 | @position = 0 | |
6 | @tokens = tokens | |
7 | end | |
8 | def peek | |
9 | return @tokens[@position] | |
10 | end | |
11 | def next | |
12 | @position += 1 | |
13 | return @tokens[@position-1] | |
14 | end | |
15 | end | |
16 | ||
17 | ||
18 | def tokenize(str) | |
19 | re = /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/ | |
46dbc0d8 JM |
20 | return str.scan(re).map{|m| m[0]}.select{ |t| |
21 | t != "" && t[0..0] != ";" | |
22 | } | |
f705f0fc JM |
23 | end |
24 | ||
8d78bc26 JM |
25 | def parse_str(t) # trim and unescape |
26 | return t[1..-2].gsub(/\\"/, '"').gsub(/\\n/, "\n").gsub(/\\\\/, "\\") | |
f705f0fc JM |
27 | end |
28 | ||
29 | def read_atom(rdr) | |
30 | token = rdr.next | |
31 | return case token | |
32 | when /^-?[0-9]+$/ then token.to_i # integer | |
33 | when /^-?[0-9][0-9.]*$/ then token.to_f # float | |
96f1845a | 34 | when /^".*"$/ then parse_str(token) # string |
b8ee29b2 | 35 | when /^:/ then "\u029e" + token[1..-1] # keyword |
f705f0fc JM |
36 | when "nil" then nil |
37 | when "true" then true | |
38 | when "false" then false | |
39 | else token.to_sym # symbol | |
40 | end | |
41 | end | |
42 | ||
43 | def read_list(rdr, klass, start="(", last =")") | |
44 | ast = klass.new | |
45 | token = rdr.next() | |
46 | if token != start | |
47 | raise "expected '" + start + "'" | |
48 | end | |
49 | while (token = rdr.peek) != last | |
50 | if not token | |
51 | raise "expected '" + last + "', got EOF" | |
52 | end | |
53 | ast.push(read_form(rdr)) | |
54 | end | |
55 | rdr.next | |
56 | return ast | |
57 | end | |
58 | ||
59 | def read_form(rdr) | |
f705f0fc JM |
60 | return case rdr.peek |
61 | when ";" then nil | |
01e25489 JM |
62 | when "'" then rdr.next; List.new [:quote, read_form(rdr)] |
63 | when "`" then rdr.next; List.new [:quasiquote, read_form(rdr)] | |
64 | when "~" then rdr.next; List.new [:unquote, read_form(rdr)] | |
65 | when "~@" then rdr.next; List.new [:"splice-unquote", read_form(rdr)] | |
3a56f91a JM |
66 | when "^" then rdr.next; meta = read_form(rdr); |
67 | List.new [:"with-meta", read_form(rdr), meta] | |
68 | when "@" then rdr.next; List.new [:deref, read_form(rdr)] | |
69 | ||
f705f0fc JM |
70 | when "(" then read_list(rdr, List, "(", ")") |
71 | when ")" then raise "unexpected ')'" | |
72 | when "[" then read_list(rdr, Vector, "[", "]") | |
73 | when "]" then raise "unexpected ']'" | |
3a56f91a | 74 | when "{" then Hash[read_list(rdr, List, "{", "}").each_slice(2).to_a] |
f705f0fc JM |
75 | when "}" then raise "unexpected '}'" |
76 | else read_atom(rdr) | |
77 | end | |
78 | end | |
79 | ||
80 | def read_str(str) | |
81 | tokens = tokenize(str) | |
46dbc0d8 | 82 | return nil if tokens.size == 0 |
f705f0fc JM |
83 | return read_form(Reader.new(tokens)) |
84 | end | |
85 |