gamnit: make `SpriteSet` public so clients can use its services
[nit.git] / lib / json / static.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
4 # Copyright 2014 Alexandre Terrasa <alexandre@moz-concept.com>
5 # Copyright 2014 Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 # http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18
19 # Static interface to read Nit objects from JSON strings
20 #
21 # `Text::parse_json` returns a simple Nit object from the JSON source.
22 # This object can then be type checked as usual with `isa` and `as`.
23 module static
24
25 import error
26 private import json_parser
27 private import json_lexer
28
29 redef class Text
30
31 # Removes JSON-escaping if necessary in a JSON string
32 #
33 # assert "\\\"string\\uD83D\\uDE02\\\"".unescape_json == "\"string😂\""
34 fun unescape_json: Text do
35 if not json_need_escape then return self
36 return self.json_to_nit_string
37 end
38
39 # Does `self` need treatment from JSON to Nit ?
40 #
41 # i.e. is there at least one `\` character in it ?
42 #
43 # assert not "string".json_need_escape
44 # assert "\\\"string\\\"".json_need_escape
45 protected fun json_need_escape: Bool do return has('\\')
46
47 # Escapes `self` from a JSON string to a Nit string
48 #
49 # assert "\\\"string\\\"".json_to_nit_string == "\"string\""
50 # assert "\\nEscape\\t\\n".json_to_nit_string == "\nEscape\t\n"
51 # assert "\\u0041zu\\uD800\\uDFD3".json_to_nit_string == "Azu𐏓"
52 protected fun json_to_nit_string: String do
53 var res = new FlatBuffer.with_capacity(byte_length)
54 var i = 0
55 var ln = self.length
56 while i < ln do
57 var char = self[i]
58 if char == '\\' then
59 i += 1
60 char = self[i]
61 if char == 'b' then
62 char = 0x08.code_point
63 else if char == 'f' then
64 char = 0x0C.code_point
65 else if char == 'n' then
66 char = '\n'
67 else if char == 'r' then
68 char = '\r'
69 else if char == 't' then
70 char = '\t'
71 else if char == 'u' then
72 var u16_esc = from_utf16_digit(i + 1)
73 char = u16_esc.code_point
74 if char.is_surrogate and i + 10 < ln then
75 if self[i + 5] == '\\' and self[i + 6] == 'u' then
76 u16_esc <<= 16
77 u16_esc += from_utf16_digit(i + 7)
78 char = u16_esc.to_u32.from_utf16_surr.code_point
79 i += 6
80 else
81 char = 0xFFFD.code_point
82 end
83 end
84 i += 4
85 end
86 # `"`, `/` or `\` => Keep `char` as-is.
87 end
88 res.add char
89 i += 1
90 end
91 return res.to_s
92 end
93
94 # Parse `self` as JSON.
95 #
96 # If `self` is not a valid JSON document or contains an unsupported escape
97 # sequence, return a `JSONParseError`.
98 #
99 # Example with `JsonObject`:
100 #
101 # var obj = "\{\"foo\": \{\"bar\": true, \"goo\": [1, 2, 3]\}\}".parse_json
102 # assert obj isa JsonObject
103 # assert obj["foo"] isa JsonObject
104 # assert obj["foo"].as(JsonObject)["bar"] == true
105 #
106 # Example with `JsonArray`:
107 #
108 # var arr = "[1, 2, 3]".parse_json
109 # assert arr isa JsonArray
110 # assert arr.length == 3
111 # assert arr.first == 1
112 # assert arr.last == 3
113 #
114 # Example with `String`:
115 #
116 # var str = "\"foo, bar, baz\"".parse_json
117 # assert str isa String
118 # assert str == "foo, bar, baz"
119 #
120 # Example of a syntaxic error:
121 #
122 # var bad = "\{foo: \"bar\"\}".parse_json
123 # assert bad isa JsonParseError
124 # assert bad.position.col_start == 2
125 fun parse_json: nullable Serializable do
126 var lexer = new Lexer_json(to_s)
127 var parser = new Parser_json
128 var tokens = lexer.lex
129 parser.tokens.add_all(tokens)
130 var root_node = parser.parse
131 if root_node isa NStart then
132 return root_node.n_0.to_nit_object
133 else if root_node isa NError then
134 return new JsonParseError(root_node.message, root_node.position)
135 else abort
136 end
137 end
138
139 redef class FlatText
140 redef fun json_need_escape do
141 var its = items
142 for i in [first_byte .. last_byte] do
143 if its[i] == 0x5Cu8 then return true
144 end
145 return false
146 end
147 end
148
149 # A map that can be translated into a JSON object.
150 interface JsonMapRead[K: String, V: nullable Serializable]
151 super MapRead[K, V]
152 super Serializable
153 end
154
155 # A JSON Object.
156 class JsonObject
157 super JsonMapRead[String, nullable Serializable]
158 super HashMap[String, nullable Serializable]
159 end
160
161 # A sequence that can be translated into a JSON array.
162 class JsonSequenceRead[E: nullable Serializable]
163 super Serializable
164 super SequenceRead[E]
165 end
166
167 # A JSON array.
168 class JsonArray
169 super JsonSequenceRead[nullable Serializable]
170 super Array[nullable Serializable]
171 end
172
173 ################################################################################
174 # Redef parser
175
176 redef class Nvalue
177 # The represented value.
178 private fun to_nit_object: nullable Serializable is abstract
179 end
180
181 redef class Nvalue_number
182 redef fun to_nit_object
183 do
184 var text = n_number.text
185 if text.chars.has('.') or text.chars.has('e') or text.chars.has('E') then return text.to_f
186 return text.to_i
187 end
188 end
189
190 redef class Nvalue_string
191 redef fun to_nit_object do return n_string.to_nit_string
192 end
193
194 redef class Nvalue_true
195 redef fun to_nit_object do return true
196 end
197
198 redef class Nvalue_false
199 redef fun to_nit_object do return false
200 end
201
202 redef class Nvalue_null
203 redef fun to_nit_object do return null
204 end
205
206 redef class Nstring
207 # The represented string.
208 private fun to_nit_string: String do return text.substring(1, text.length - 2).unescape_json.to_s
209 end
210
211 redef class Nvalue_object
212 redef fun to_nit_object do
213 var obj = new JsonObject
214 var members = n_members
215 if members != null then
216 var pairs = members.pairs
217 for pair in pairs do obj[pair.name] = pair.value
218 end
219 return obj
220 end
221 end
222
223 redef class Nmembers
224 # All the key-value pairs.
225 private fun pairs: Array[Npair] is abstract
226 end
227
228 redef class Nmembers_tail
229 redef fun pairs
230 do
231 var arr = n_members.pairs
232 arr.add n_pair
233 return arr
234 end
235 end
236
237 redef class Nmembers_head
238 redef fun pairs do return [n_pair]
239 end
240
241 redef class Npair
242 # The represented key.
243 private fun name: String do return n_string.to_nit_string
244
245 # The represented value.
246 private fun value: nullable Serializable do return n_value.to_nit_object
247 end
248
249 redef class Nvalue_array
250 redef fun to_nit_object
251 do
252 var arr = new JsonArray
253 var elements = n_elements
254 if elements != null then
255 var items = elements.items
256 for item in items do arr.add(item.to_nit_object)
257 end
258 return arr
259 end
260 end
261
262 redef class Nelements
263 # All the items.
264 private fun items: Array[Nvalue] is abstract
265 end
266
267 redef class Nelements_tail
268 redef fun items
269 do
270 var items = n_elements.items
271 items.add(n_value)
272 return items
273 end
274 end
275
276 redef class Nelements_head
277 redef fun items do return [n_value]
278 end