1 /*************************************************************************/
2 /*  variant_construct_string.cpp                                         */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 #include "variant.h"
31 
32 class VariantConstruct {
33 
34 	enum TokenType {
35 		TK_CURLY_BRACKET_OPEN,
36 		TK_CURLY_BRACKET_CLOSE,
37 		TK_BRACKET_OPEN,
38 		TK_BRACKET_CLOSE,
39 		TK_IDENTIFIER,
40 		TK_STRING,
41 		TK_NUMBER,
42 		TK_COLON,
43 		TK_COMMA,
44 		TK_EOF,
45 		TK_MAX
46 	};
47 
48 	enum Expecting {
49 
50 		EXPECT_OBJECT,
51 		EXPECT_OBJECT_KEY,
52 		EXPECT_COLON,
53 		EXPECT_OBJECT_VALUE,
54 	};
55 
56 	struct Token {
57 
58 		TokenType type;
59 		Variant value;
60 	};
61 
62 	static const char *tk_name[TK_MAX];
63 
64 	static String _print_var(const Variant &p_var);
65 
66 	static Error _get_token(const CharType *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str);
67 	static Error _parse_value(Variant &value, Token &token, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str, Variant::ObjectConstruct *p_construct, void *p_ud);
68 	static Error _parse_array(Array &array, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str, Variant::ObjectConstruct *p_construct, void *p_ud);
69 	static Error _parse_dict(Dictionary &object, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str, Variant::ObjectConstruct *p_construct, void *p_ud);
70 
71 public:
72 	static Error parse(const String &p_string, Variant &r_ret, String &r_err_str, int &r_err_line, Variant::ObjectConstruct *p_construct, void *p_ud);
73 };
74 
75 const char *VariantConstruct::tk_name[TK_MAX] = {
76 	"'{'",
77 	"'}'",
78 	"'['",
79 	"']'",
80 	"identifier",
81 	"string",
82 	"number",
83 	"':'",
84 	"','",
85 	"EOF",
86 };
87 
_get_token(const CharType * p_str,int & idx,int p_len,Token & r_token,int & line,String & r_err_str)88 Error VariantConstruct::_get_token(const CharType *p_str, int &idx, int p_len, Token &r_token, int &line, String &r_err_str) {
89 
90 	while (true) {
91 		switch (p_str[idx]) {
92 
93 			case '\n': {
94 
95 				line++;
96 				idx++;
97 				break;
98 			};
99 			case 0: {
100 				r_token.type = TK_EOF;
101 				return OK;
102 			} break;
103 			case '{': {
104 
105 				r_token.type = TK_CURLY_BRACKET_OPEN;
106 				idx++;
107 				return OK;
108 			};
109 			case '}': {
110 
111 				r_token.type = TK_CURLY_BRACKET_CLOSE;
112 				idx++;
113 				return OK;
114 			};
115 			case '[': {
116 
117 				r_token.type = TK_BRACKET_OPEN;
118 				idx++;
119 				return OK;
120 			};
121 			case ']': {
122 
123 				r_token.type = TK_BRACKET_CLOSE;
124 				idx++;
125 				return OK;
126 			};
127 			case ':': {
128 
129 				r_token.type = TK_COLON;
130 				idx++;
131 				return OK;
132 			};
133 			case ',': {
134 
135 				r_token.type = TK_COMMA;
136 				idx++;
137 				return OK;
138 			};
139 			case '"': {
140 
141 				idx++;
142 				String str;
143 				while (true) {
144 					if (p_str[idx] == 0) {
145 						r_err_str = "Unterminated String";
146 						return ERR_PARSE_ERROR;
147 					} else if (p_str[idx] == '"') {
148 						idx++;
149 						break;
150 					} else if (p_str[idx] == '\\') {
151 						//escaped characters...
152 						idx++;
153 						CharType next = p_str[idx];
154 						if (next == 0) {
155 							r_err_str = "Unterminated String";
156 							return ERR_PARSE_ERROR;
157 						}
158 						CharType res = 0;
159 
160 						switch (next) {
161 
162 							case 'b': res = 8; break;
163 							case 't': res = 9; break;
164 							case 'n': res = 10; break;
165 							case 'f': res = 12; break;
166 							case 'r': res = 13; break;
167 							case '\"': res = '\"'; break;
168 							case '\\': res = '\\'; break;
169 							case '/': res = '/'; break;
170 							case 'u': {
171 								//hexnumbarh - oct is deprecated
172 
173 								for (int j = 0; j < 4; j++) {
174 									CharType c = p_str[idx + j + 1];
175 									if (c == 0) {
176 										r_err_str = "Unterminated String";
177 										return ERR_PARSE_ERROR;
178 									}
179 									if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
180 
181 										r_err_str = "Malformed hex constant in string";
182 										return ERR_PARSE_ERROR;
183 									}
184 									CharType v;
185 									if (c >= '0' && c <= '9') {
186 										v = c - '0';
187 									} else if (c >= 'a' && c <= 'f') {
188 										v = c - 'a';
189 										v += 10;
190 									} else if (c >= 'A' && c <= 'F') {
191 										v = c - 'A';
192 										v += 10;
193 									} else {
194 										ERR_PRINT("BUG");
195 										v = 0;
196 									}
197 
198 									res <<= 4;
199 									res |= v;
200 								}
201 								idx += 4; //will add at the end anyway
202 
203 							} break;
204 							default: {
205 
206 								r_err_str = "Invalid escape sequence";
207 								return ERR_PARSE_ERROR;
208 							} break;
209 						}
210 
211 						str += res;
212 
213 					} else {
214 						if (p_str[idx] == '\n')
215 							line++;
216 						str += p_str[idx];
217 					}
218 					idx++;
219 				}
220 
221 				r_token.type = TK_STRING;
222 				r_token.value = str;
223 				return OK;
224 
225 			} break;
226 			default: {
227 
228 				if (p_str[idx] <= 32) {
229 					idx++;
230 					break;
231 				}
232 
233 				if (p_str[idx] == '-' || (p_str[idx] >= '0' && p_str[idx] <= '9')) {
234 					//a number
235 					const CharType *rptr;
236 					double number = String::to_double(&p_str[idx], &rptr);
237 					idx += (rptr - &p_str[idx]);
238 					r_token.type = TK_NUMBER;
239 					r_token.value = number;
240 					return OK;
241 
242 				} else if ((p_str[idx] >= 'A' && p_str[idx] <= 'Z') || (p_str[idx] >= 'a' && p_str[idx] <= 'z')) {
243 
244 					String id;
245 
246 					while ((p_str[idx] >= 'A' && p_str[idx] <= 'Z') || (p_str[idx] >= 'a' && p_str[idx] <= 'z')) {
247 
248 						id += p_str[idx];
249 						idx++;
250 					}
251 
252 					r_token.type = TK_IDENTIFIER;
253 					r_token.value = id;
254 					return OK;
255 				} else {
256 					r_err_str = "Unexpected character.";
257 					return ERR_PARSE_ERROR;
258 				}
259 			}
260 		}
261 	}
262 
263 	return ERR_PARSE_ERROR;
264 }
265 
_parse_value(Variant & value,Token & token,const CharType * p_str,int & index,int p_len,int & line,String & r_err_str,Variant::ObjectConstruct * p_construct,void * p_ud)266 Error VariantConstruct::_parse_value(Variant &value, Token &token, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str, Variant::ObjectConstruct *p_construct, void *p_ud) {
267 
268 	if (token.type == TK_CURLY_BRACKET_OPEN) {
269 
270 		Dictionary d;
271 		Error err = _parse_dict(d, p_str, index, p_len, line, r_err_str, p_construct, p_ud);
272 		if (err)
273 			return err;
274 		value = d;
275 		return OK;
276 	} else if (token.type == TK_BRACKET_OPEN) {
277 
278 		Array a;
279 		Error err = _parse_array(a, p_str, index, p_len, line, r_err_str, p_construct, p_ud);
280 		if (err)
281 			return err;
282 		value = a;
283 		return OK;
284 
285 	} else if (token.type == TK_IDENTIFIER) {
286 
287 		String id = token.value;
288 		if (id == "true")
289 			value = true;
290 		else if (id == "false")
291 			value = false;
292 		else if (id == "null")
293 			value = Variant();
294 		else {
295 			r_err_str = "Expected 'true','false' or 'null', got '" + id + "'.";
296 			return ERR_PARSE_ERROR;
297 		}
298 		return OK;
299 
300 	} else if (token.type == TK_NUMBER) {
301 
302 		value = token.value;
303 		return OK;
304 	} else if (token.type == TK_STRING) {
305 
306 		value = token.value;
307 		return OK;
308 	} else {
309 		r_err_str = "Expected value, got " + String(tk_name[token.type]) + ".";
310 		return ERR_PARSE_ERROR;
311 	}
312 
313 	return ERR_PARSE_ERROR;
314 }
315 
_parse_array(Array & array,const CharType * p_str,int & index,int p_len,int & line,String & r_err_str,Variant::ObjectConstruct * p_construct,void * p_ud)316 Error VariantConstruct::_parse_array(Array &array, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str, Variant::ObjectConstruct *p_construct, void *p_ud) {
317 
318 	Token token;
319 	bool need_comma = false;
320 
321 	while (index < p_len) {
322 
323 		Error err = _get_token(p_str, index, p_len, token, line, r_err_str);
324 		if (err != OK)
325 			return err;
326 
327 		if (token.type == TK_BRACKET_CLOSE) {
328 
329 			return OK;
330 		}
331 
332 		if (need_comma) {
333 
334 			if (token.type != TK_COMMA) {
335 
336 				r_err_str = "Expected ','";
337 				return ERR_PARSE_ERROR;
338 			} else {
339 				need_comma = false;
340 				continue;
341 			}
342 		}
343 
344 		Variant v;
345 		err = _parse_value(v, token, p_str, index, p_len, line, r_err_str, p_construct, p_ud);
346 		if (err)
347 			return err;
348 
349 		array.push_back(v);
350 		need_comma = true;
351 	}
352 
353 	return OK;
354 }
355 
_parse_dict(Dictionary & dict,const CharType * p_str,int & index,int p_len,int & line,String & r_err_str,Variant::ObjectConstruct * p_construct,void * p_ud)356 Error VariantConstruct::_parse_dict(Dictionary &dict, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str, Variant::ObjectConstruct *p_construct, void *p_ud) {
357 
358 	bool at_key = true;
359 	Variant key;
360 	Token token;
361 	bool need_comma = false;
362 
363 	while (index < p_len) {
364 
365 		if (at_key) {
366 
367 			Error err = _get_token(p_str, index, p_len, token, line, r_err_str);
368 			if (err != OK)
369 				return err;
370 
371 			if (token.type == TK_CURLY_BRACKET_CLOSE) {
372 
373 				return OK;
374 			}
375 
376 			if (need_comma) {
377 
378 				if (token.type != TK_COMMA) {
379 
380 					r_err_str = "Expected '}' or ','";
381 					return ERR_PARSE_ERROR;
382 				} else {
383 					need_comma = false;
384 					continue;
385 				}
386 			}
387 
388 			err = _parse_value(key, token, p_str, index, p_len, line, r_err_str, p_construct, p_ud);
389 
390 			if (err != OK)
391 				return err;
392 
393 			err = _get_token(p_str, index, p_len, token, line, r_err_str);
394 
395 			if (err != OK)
396 				return err;
397 
398 			if (token.type != TK_COLON) {
399 
400 				r_err_str = "Expected ':'";
401 				return ERR_PARSE_ERROR;
402 			}
403 			at_key = false;
404 		} else {
405 
406 			Error err = _get_token(p_str, index, p_len, token, line, r_err_str);
407 			if (err != OK)
408 				return err;
409 
410 			Variant v;
411 			err = _parse_value(v, token, p_str, index, p_len, line, r_err_str, p_construct, p_ud);
412 			if (err)
413 				return err;
414 			dict[key] = v;
415 			need_comma = true;
416 			at_key = true;
417 		}
418 	}
419 
420 	return OK;
421 }
422 
parse(const String & p_string,Variant & r_ret,String & r_err_str,int & r_err_line,Variant::ObjectConstruct * p_construct,void * p_ud)423 Error VariantConstruct::parse(const String &p_string, Variant &r_ret, String &r_err_str, int &r_err_line, Variant::ObjectConstruct *p_construct, void *p_ud) {
424 
425 	const CharType *str = p_string.ptr();
426 	int idx = 0;
427 	int len = p_string.length();
428 	Token token;
429 	r_err_line = 0;
430 	String aux_key;
431 
432 	Error err = _get_token(str, idx, len, token, r_err_line, r_err_str);
433 	if (err)
434 		return err;
435 
436 	return _parse_value(r_ret, token, str, idx, len, r_err_line, r_err_str, p_construct, p_ud);
437 }
438