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 # Parses and returns a Nit string from a JSON String
235 fun parse_json_string
: Jsonable do
239 if p
> ln
then return make_parse_error
("Malformed JSON String")
244 if p
+ 1 >= ln
then return make_parse_error
("Malformed Escape sequence in JSON string")
249 if p
+ 3 >= ln
then return make_parse_error
("Bad Unicode escape sequence in string")
250 for i
in [0 .. 4[ do if not src
[p
+ i
].is_hexdigit
then return make_parse_error
("Bad Unicode escape sequence in string")
255 if p
>= ln
then return make_parse_error
("Malformed JSON String")
259 return src
.substring
(st
, p
- st
).unescape_json
262 # Ignores any character until a JSON separator is encountered
263 fun ignore_until_separator
do
266 if not src
[pos
].is_json_separator
then return
272 redef fun parse_json
do return (new JSONStringParser(self.to_s
)).parse_entity
275 redef class JsonParseError
277 # Location of the error in source
278 var location
: nullable Location = null
280 # Get the JSON representation of `self`.
283 # var err = new JsonParseError("foo", new Position(1, 2, 3, 4, 5, 6))
284 # assert err.to_json == "Parsing error: foo"
289 return if l
== null then "Parsing error: {m}" else "Parsing error at {l}: {m}"
292 redef fun to_s
do return to_json