1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Simple ad-hoc implementation of a JSON parser for String inputs
22 # Is `self` a valid number start ?
23 private fun is_json_num_start
: Bool do
24 if self == '-' then return true
25 if self.is_numeric
then return true
29 # Is `self` a valid JSON separator ?
30 private fun is_json_separator
: Bool do
31 if self == ':' then return true
32 if self == ',' then return true
33 if self == '{' then return true
34 if self == '}' then return true
35 if self == '[' then return true
36 if self == ']' then return true
37 if self == '"' then return true
38 if self.is_whitespace
then return true
43 # A simple ad-hoc JSON parser
45 # To parse a simple JSON document, read it as a String and give it to `parse_entity`
46 # NOTE: if your document contains several non-nested entities, use `parse_entity` for each
47 # JSON entity to parse
48 class JSONStringParser
51 # Parses a JSON Entity
54 # var p = new JSONStringParser("""{"numbers": [1,23,3], "string": "string"}""")
55 # assert p.parse_entity isa JsonObject
57 fun parse_entity
: nullable Jsonable do
60 if pos
>= srclen
then return make_parse_error
("Empty JSON")
64 return parse_json_array
66 var s
= parse_json_string
70 return parse_json_object
72 if pos
+ 4 >= srclen
then make_parse_error
("Error: bad JSON entity")
73 if src
[pos
+ 1] == 'a' and src
[pos
+ 2] == 'l' and src
[pos
+ 3] == 's' and src
[pos
+ 4] == 'e' then
77 return make_parse_error
("Error: bad JSON entity")
79 if pos
+ 3 >= srclen
then make_parse_error
("Error: bad JSON entity")
80 if src
[pos
+ 1] == 'r' and src
[pos
+ 2] == 'u' and src
[pos
+ 3] == 'e' then
84 return make_parse_error
("Error: bad JSON entity")
86 if pos
+ 3 >= srclen
then make_parse_error
("Error: bad JSON entity")
87 if src
[pos
+ 1] == 'u' and src
[pos
+ 2] == 'l' and src
[pos
+ 3] == 'l' then
91 return make_parse_error
("Error: bad JSON entity")
93 if not c
.is_json_num_start
then return make_parse_error
("Bad JSON character")
94 return parse_json_number
98 fun parse_json_array
: Jsonable do
100 if pos
>= max
then return make_parse_error
("Incomplete JSON array")
101 var arr
= new JsonArray
103 while not c
== ']' do
105 if pos
>= max
then return make_parse_error
("Incomplete JSON array")
106 if src
[pos
] == ']' then break
107 var ent
= parse_entity
108 #print "Parsed an entity {ent} for a JSON array"
109 if ent
isa JsonParseError then return ent
112 if pos
>= max
then return make_parse_error
("Incomplete JSON array")
114 if c
== ']' then break
115 if c
!= ',' then return make_parse_error
("Bad array separator {c}")
122 # Parses a JSON Object
123 fun parse_json_object
: Jsonable do
125 if pos
>= max
then return make_parse_error
("Incomplete JSON object")
126 var obj
= new JsonObject
128 while not c
== '}' do
130 if pos
>= max
then return make_parse_error
("Malformed JSON object")
131 if src
[pos
] == '}' then break
132 var key
= parse_entity
133 #print "Parsed key {key} for JSON object"
134 if not key
isa String then return make_parse_error
("Bad key format {key or else "null"}")
136 if pos
>= max
then return make_parse_error
("Incomplete JSON object")
137 if not src
[pos
] == ':' then return make_parse_error
("Bad key/value separator {src[pos]}")
140 var value
= parse_entity
141 #print "Parsed value {value} for JSON object"
142 if value
isa JsonParseError then return value
145 if pos
>= max
then return make_parse_error
("Incomplete JSON object")
147 if c
== '}' then break
148 if c
!= ',' then return make_parse_error
("Bad object separator {src[pos]}")
155 # Creates a `JsonParseError` with the right message and location
156 protected fun make_parse_error
(message
: String): JsonParseError do
157 var err
= new JsonParseError(message
)
158 err
.location
= hot_location
162 # Parses an Int or Float
163 fun parse_json_number
: Jsonable do
171 if p
>= max
then return make_parse_error
("Bad JSON number")
175 while c
.is_numeric
do
179 if p
>= max
then break
184 if p
>= max
then return make_parse_error
("Bad JSON number")
188 while c
.is_numeric
do
189 fl
+= c
.to_i
.to_f
* frac
192 if p
>= max
then break
195 if c
== 'e' or c
== 'E' then
198 if p
>= max
then return make_parse_error
("Malformed JSON number")
200 while c
.is_numeric
do
204 if p
>= max
then break
207 fl
*= (10 ** exp
).to_f
209 if p
< max
and not c
.is_json_separator
then return make_parse_error
("Malformed JSON number")
211 if is_neg
then return -fl
214 if c
== 'e' or c
== 'E' then
216 if p
>= max
then return make_parse_error
("Bad JSON number")
217 var exp
= src
[p
].to_i
219 while c
.is_numeric
do
223 if p
>= max
then break
228 if p
< max
and not src
[p
].is_json_separator
then return make_parse_error
("Malformed JSON number")
230 if is_neg
then return -val
234 private var parse_str_buf
= new FlatBuffer
236 # Parses and returns a Nit string from a JSON String
237 fun parse_json_string
: Jsonable do
242 if p
> ln
then return make_parse_error
("Malformed JSON String")
244 var ret
= parse_str_buf
249 if p
>= ln
then return make_parse_error
("Malformed JSON string")
253 ret
.append_substring_impl
(src
, chunk_st
, p
- chunk_st
)
255 if p
>= ln
then return make_parse_error
("Malformed Escape sequence in JSON string")
260 else if c
== 'n' then
263 else if c
== 't' then
266 else if c
== 'u' then
271 if p
>= ln
then make_parse_error
("Malformed \uXXXX Escape sequence in JSON string")
273 if c
>= '0' and c
<= '9' then
274 cp
+= c
.code_point
- '0'.code_point
275 else if c
>= 'a' and c
<= 'f' then
276 cp
+= c
.code_point
- 'a'.code_point
+ 10
277 else if c
>= 'A' and c
<= 'F' then
278 cp
+= c
.code_point
- 'A'.code_point
+ 10
280 make_parse_error
("Malformed \uXXXX Escape sequence in JSON string")
285 if cp
>= 0xD800 and cp
<= 0xDBFF then
286 if p
>= ln
then make_parse_error
("Malformed \uXXXX Escape sequence in JSON string")
288 if c
!= '\\' then make_parse_error
("Malformed \uXXXX Escape sequence in JSON string")
291 if c
!= 'u' then make_parse_error
("Malformed \uXXXX Escape sequence in JSON string")
296 if p
> ln
then make_parse_error
("Malformed \uXXXX Escape sequence in JSON string")
298 if c
>= '0' and c
<= '9' then
299 locp
+= c
.code_point
- '0'.code_point
300 else if c
>= 'a' and c
<= 'f' then
301 locp
+= c
.code_point
- 'a'.code_point
+ 10
302 else if c
>= 'A' and c
<= 'F' then
303 locp
+= c
.code_point
- 'A'.code_point
+ 10
305 make_parse_error
("Malformed \uXXXX Escape sequence in JSON string")
309 c
= (((locp
& 0x3FF) | ((cp
& 0x3FF) << 10)) + 0x10000).code_point
312 else if c
== 'b' then
315 else if c
== 'f' then
326 if ret
.is_empty
then return src
.substring
(chunk_st
, p
- chunk_st
)
327 ret
.append_substring_impl
(src
, chunk_st
, p
- chunk_st
)
333 # Ignores any character until a JSON separator is encountered
334 fun ignore_until_separator
do
337 if not src
[pos
].is_json_separator
then return
343 redef fun parse_json
do return (new JSONStringParser(self.to_s
)).parse_entity
346 redef class JsonParseError
349 # Location of the error in source
350 var location
: nullable Location = null