forked from RomainFranceschini/icrystal
/
ndjson.cr
177 lines (140 loc) · 3.58 KB
/
ndjson.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
require "json"
module JSON
# Same as JSON::Parser but only calls next_token at the beginning of .parse
# This allows us to parse multiple JSON objects from a single stream
class NDParser
property max_nesting = 512
def initialize(string_or_io : String | IO)
@lexer = JSON::Lexer.new(string_or_io)
@nest = 0
end
def parse : Any
next_token
parse_nd_object
end
private def parse_nd_object
next_token_expect_object_key
object = {} of String => Any
nest do
unless token.kind.end_object?
while true
check :string
key = token.string_value
next_token
check :colon
next_token
value = parse_value
object[key] = value
case token.kind
when .comma?
next_token_expect_object_key
unexpected_token if token.kind.end_object?
when .end_object?
break
else
unexpected_token
end
end
end
end
# next_token
Any.new(object)
end
private def parse_value
case token.kind
when .int?
value_and_next_token token.int_value
when .float?
value_and_next_token token.float_value
when .string?
value_and_next_token token.string_value
when .null?
value_and_next_token nil
when .true?
value_and_next_token true
when .false?
value_and_next_token false
when .begin_array?
parse_array
when .begin_object?
parse_object
else
unexpected_token
end
end
private def parse_array
next_token
ary = [] of Any
nest do
unless token.kind.end_array?
while true
ary << parse_value
case token.kind
when .comma?
next_token
unexpected_token if token.kind.end_array?
when .end_array?
break
else
unexpected_token
end
end
end
end
next_token
Any.new(ary)
end
private def parse_object
next_token_expect_object_key
object = {} of String => Any
nest do
unless token.kind.end_object?
while true
check :string
key = token.string_value
next_token
check :colon
next_token
value = parse_value
object[key] = value
case token.kind
when .comma?
next_token_expect_object_key
unexpected_token if token.kind.end_object?
when .end_object?
break
else
unexpected_token
end
end
end
end
next_token
Any.new(object)
end
private delegate token, to: @lexer
private delegate next_token, to: @lexer
private delegate next_token_expect_object_key, to: @lexer
private def value_and_next_token(value)
next_token
Any.new(value)
end
private def check(kind : Token::Kind)
unexpected_token unless token.kind == kind
end
private def unexpected_token
parse_exception "unexpected token '#{token}'"
end
private def parse_exception(msg)
raise ParseException.new(msg, token.line_number, token.column_number)
end
private def nest(&)
@nest += 1
if @nest > @max_nesting
parse_exception "Nesting of #{@nest} is too deep"
end
yield
@nest -= 1
end
end
end