Don't pass negative token id to Lemon parser
[lucy.git] / core / Lucy / Util / Json.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <ctype.h>
18 #include <stdio.h>
19
20 #include "Lucy/Util/ToolSet.h"
21
22 #include "Lucy/Util/Json.h"
23
24 #include "Clownfish/Boolean.h"
25 #include "Clownfish/CharBuf.h"
26 #include "Clownfish/Num.h"
27 #include "Clownfish/Util/Memory.h"
28 #include "Lucy/Store/Folder.h"
29 #include "Lucy/Store/InStream.h"
30 #include "Lucy/Store/OutStream.h"
31 #include "Lucy/Util/Json/JsonParser.h"
32 #include "Lucy/Util/StringHelper.h"
33
34 /* Routines generated by Lemon. */
35 void*
36 LucyParseJsonAlloc(void * (*allocate)(size_t));
37 void
38 LucyParseJson(void *json_parser, int token_type, Obj *value,
39 lucy_JsonParserState *state);
40 void
41 LucyParseJsonFree(void *json_parser, void(*freemem)(void*));
42 void
43 LucyParseJsonTrace(FILE *trace, char *line_prefix);
44
45 // Encode JSON for supplied "dump". On failure, sets the global error object
46 // and returns false.
47 static bool
48 S_to_json(Obj *dump, CharBuf *buf, int32_t depth);
49
50 // Parse JSON from raw UTF-8 in memory.
51 static Obj*
52 S_parse_json(const char *text, size_t size);
53 static Obj*
54 S_do_parse_json(void *json_parser, const char *json, size_t len);
55
56 // Parse a JSON number. Advance the text buffer just past the number.
57 static Float*
58 S_parse_number(const char **json_ptr, const char *limit);
59
60 // Parse a JSON string. Advance the text buffer from pointing at the opening
61 // double quote to pointing just after the closing double quote.
62 static String*
63 S_parse_string(const char **json_ptr, const char *limit);
64
65 // Unescape JSON string text. Expects pointers bookending the text data (i.e.
66 // pointing just after the opening double quote and directly at the closing
67 // double quote), and assumes that escapes have already been sanity checked
68 // for length.
69 static String*
70 S_unescape_text(const char *top, const char *end);
71
72 // Check that the supplied text begins with the specified keyword, which must
73 // then end on a word boundary (i.e. match "null" but not the first four
74 // letters of "nullify").
75 static CFISH_INLINE bool
76 SI_check_keyword(const char *json, const char* end, const char *keyword,
77 size_t len);
78
79 // Make it possible to be loosen constraints during testing.
80 static bool tolerant = false;
81
82 // Indentation: two spaces per level.
83 static const char indentation[] = " ";
84 static const size_t INDENTATION_LEN = sizeof(indentation) - 1;
85
86 // Append indentation spaces x depth.
87 static void
88 S_cat_whitespace(CharBuf *buf, int32_t depth);
89
90 // Set the global error object, appending escaped JSON in the vicinity of the
91 // error.
92 static void
93 S_set_error(const char *mess, const char *json, const char *limit, int line,
94 const char *func);
95 #define SET_ERROR(_mess, _json, _end) \
96 S_set_error(_mess, _json, _end, __LINE__, CFISH_ERR_FUNC_MACRO)
97
98 Obj*
99 Json_from_json(String *json) {
100 Obj *dump = S_parse_json(Str_Get_Ptr8(json), Str_Get_Size(json));
101 if (!dump) {
102 ERR_ADD_FRAME(Err_get_error());
103 }
104 return dump;
105 }
106
107 Obj*
108 Json_slurp_json(Folder *folder, String *path) {
109 InStream *instream = Folder_Open_In(folder, path);
110 if (!instream) {
111 ERR_ADD_FRAME(Err_get_error());
112 return NULL;
113 }
114 size_t len = (size_t)InStream_Length(instream);
115 const char *buf = InStream_Buf(instream, len);
116 Obj *dump = S_parse_json(buf, len);
117 InStream_Close(instream);
118 DECREF(instream);
119 if (!dump) {
120 ERR_ADD_FRAME(Err_get_error());
121 }
122 return dump;
123 }
124
125 bool
126 Json_spew_json(Obj *dump, Folder *folder, String *path) {
127 String *json = Json_to_json(dump);
128 if (!json) {
129 ERR_ADD_FRAME(Err_get_error());
130 return false;
131 }
132 OutStream *outstream = Folder_Open_Out(folder, path);
133 if (!outstream) {
134 ERR_ADD_FRAME(Err_get_error());
135 DECREF(json);
136 return false;
137 }
138 size_t size = Str_Get_Size(json);
139 OutStream_Write_Bytes(outstream, Str_Get_Ptr8(json), size);
140 OutStream_Close(outstream);
141 DECREF(outstream);
142 DECREF(json);
143 return true;
144 }
145
146 String*
147 Json_to_json(Obj *dump) {
148 // Validate object type, only allowing hashes and arrays per JSON spec.
149 if (!dump || !(Obj_is_a(dump, HASH) || Obj_is_a(dump, VECTOR))) {
150 if (!tolerant) {
151 String *class_name = dump ? Obj_get_class_name(dump) : NULL;
152 String *mess = MAKE_MESS("Illegal top-level object type: %o",
153 class_name);
154 Err_set_error(Err_new(mess));
155 return NULL;
156 }
157 }
158
159 // Encode.
160 CharBuf *buf = CB_new(31);
161 String *json = NULL;
162 if (!S_to_json(dump, buf, 0)) {
163 ERR_ADD_FRAME(Err_get_error());
164 }
165 else {
166 // Append newline.
167 CB_Cat_Trusted_Utf8(buf, "\n", 1);
168 json = CB_Yield_String(buf);
169 }
170
171 DECREF(buf);
172 return json;
173 }
174
175 void
176 Json_set_tolerant(bool tolerance) {
177 tolerant = tolerance;
178 }
179
180 static const int32_t MAX_DEPTH = 200;
181
182 static void
183 S_append_json_string(String *dump, CharBuf *buf) {
184 // Append opening quote.
185 CB_Cat_Trusted_Utf8(buf, "\"", 1);
186
187 // Process string data.
188 StringIterator *iter = Str_Top(dump);
189 int32_t code_point;
190 while (STR_OOB != (code_point = StrIter_Next(iter))) {
191 if (code_point > 127) {
192 // There is no need to escape any high characters, including those
193 // above the BMP, as we assume that the destination channel can
194 // handle arbitrary UTF-8 data.
195 CB_Cat_Char(buf, code_point);
196 }
197 else {
198 char buffer[7];
199 size_t len;
200 switch (code_point & 127) {
201 // Perform all mandatory escapes enumerated in the JSON spec.
202 // Note that the spec makes escaping forward slash optional;
203 // we choose not to.
204 case 0x00: case 0x01: case 0x02: case 0x03:
205 case 0x04: case 0x05: case 0x06: case 0x07:
206 case 0x0b: case 0x0e: case 0x0f:
207 case 0x10: case 0x11: case 0x12: case 0x13:
208 case 0x14: case 0x15: case 0x16: case 0x17:
209 case 0x18: case 0x19: case 0x1a: case 0x1b:
210 case 0x1c: case 0x1d: case 0x1e: case 0x1f: {
211 sprintf(buffer, "\\u%04x", (unsigned)code_point);
212 len = 6;
213 break;
214 }
215 case '\b':
216 memcpy(buffer, "\\b", 2);
217 len = 2;
218 break;
219 case '\t':
220 memcpy(buffer, "\\t", 2);
221 len = 2;
222 break;
223 case '\n':
224 memcpy(buffer, "\\n", 2);
225 len = 2;
226 break;
227 case '\f':
228 memcpy(buffer, "\\f", 2);
229 len = 2;
230 break;
231 case '\r':
232 memcpy(buffer, "\\r", 2);
233 len = 2;
234 break;
235 case '\\':
236 memcpy(buffer, "\\\\", 2);
237 len = 2;
238 break;
239 case '\"':
240 memcpy(buffer, "\\\"", 2);
241 len = 2;
242 break;
243
244 // Ordinary printable ASCII.
245 default:
246 buffer[0] = (char)code_point;
247 len = 1;
248 }
249 CB_Cat_Trusted_Utf8(buf, buffer, len);
250 }
251 }
252
253 // Append closing quote.
254 CB_Cat_Trusted_Utf8(buf, "\"", 1);
255
256 DECREF(iter);
257 }
258
259 static void
260 S_cat_whitespace(CharBuf *buf, int32_t depth) {
261 while (depth--) {
262 CB_Cat_Trusted_Utf8(buf, indentation, INDENTATION_LEN);
263 }
264 }
265
266 static bool
267 S_to_json(Obj *dump, CharBuf *buf, int32_t depth) {
268 // Guard against infinite recursion in self-referencing data structures.
269 if (depth > MAX_DEPTH) {
270 String *mess = MAKE_MESS("Exceeded max depth of %i32", MAX_DEPTH);
271 Err_set_error(Err_new(mess));
272 return false;
273 }
274
275 if (!dump) {
276 CB_Cat_Trusted_Utf8(buf, "null", 4);
277 }
278 else if (dump == (Obj*)CFISH_TRUE) {
279 CB_Cat_Trusted_Utf8(buf, "true", 4);
280 }
281 else if (dump == (Obj*)CFISH_FALSE) {
282 CB_Cat_Trusted_Utf8(buf, "false", 5);
283 }
284 else if (Obj_is_a(dump, STRING)) {
285 S_append_json_string((String*)dump, buf);
286 }
287 else if (Obj_is_a(dump, INTEGER)) {
288 CB_catf(buf, "%i64", Int_Get_Value((Integer*)dump));
289 }
290 else if (Obj_is_a(dump, FLOAT)) {
291 CB_catf(buf, "%f64", Float_Get_Value((Float*)dump));
292 }
293 else if (Obj_is_a(dump, VECTOR)) {
294 Vector *array = (Vector*)dump;
295 size_t size = Vec_Get_Size(array);
296 if (size == 0) {
297 // Put empty array on single line.
298 CB_Cat_Trusted_Utf8(buf, "[]", 2);
299 return true;
300 }
301 else if (size == 1) {
302 Obj *elem = Vec_Fetch(array, 0);
303 if (!(Obj_is_a(elem, HASH) || Obj_is_a(elem, VECTOR))) {
304 // Put array containing single scalar element on one line.
305 CB_Cat_Trusted_Utf8(buf, "[", 1);
306 if (!S_to_json(elem, buf, depth + 1)) {
307 return false;
308 }
309 CB_Cat_Trusted_Utf8(buf, "]", 1);
310 return true;
311 }
312 }
313 // Fall back to spreading elements across multiple lines.
314 CB_Cat_Trusted_Utf8(buf, "[", 1);
315 for (size_t i = 0; i < size; i++) {
316 CB_Cat_Trusted_Utf8(buf, "\n", 1);
317 S_cat_whitespace(buf, depth + 1);
318 if (!S_to_json(Vec_Fetch(array, i), buf, depth + 1)) {
319 return false;
320 }
321 if (i + 1 < size) {
322 CB_Cat_Trusted_Utf8(buf, ",", 1);
323 }
324 }
325 CB_Cat_Trusted_Utf8(buf, "\n", 1);
326 S_cat_whitespace(buf, depth);
327 CB_Cat_Trusted_Utf8(buf, "]", 1);
328 }
329 else if (Obj_is_a(dump, HASH)) {
330 Hash *hash = (Hash*)dump;
331 size_t size = Hash_Get_Size(hash);
332
333 // Put empty hash on single line.
334 if (size == 0) {
335 CB_Cat_Trusted_Utf8(buf, "{}", 2);
336 return true;
337 }
338
339 // Validate that all keys are strings, then sort.
340 Vector *keys = Hash_Keys(hash);
341 for (size_t i = 0; i < size; i++) {
342 Obj *key = Vec_Fetch(keys, i);
343 if (!key || !Obj_is_a(key, STRING)) {
344 DECREF(keys);
345 String *key_class = key ? Obj_get_class_name(key) : NULL;
346 String *mess = MAKE_MESS("Illegal key type: %o", key_class);
347 Err_set_error(Err_new(mess));
348 return false;
349 }
350 }
351 Vec_Sort(keys);
352
353 // Spread pairs across multiple lines.
354 CB_Cat_Trusted_Utf8(buf, "{", 1);
355 for (size_t i = 0; i < size; i++) {
356 String *key = (String*)Vec_Fetch(keys, i);
357 CB_Cat_Trusted_Utf8(buf, "\n", 1);
358 S_cat_whitespace(buf, depth + 1);
359 S_append_json_string(key, buf);
360 CB_Cat_Trusted_Utf8(buf, ": ", 2);
361 if (!S_to_json(Hash_Fetch(hash, key), buf, depth + 1)) {
362 DECREF(keys);
363 return false;
364 }
365 if (i + 1 < size) {
366 CB_Cat_Trusted_Utf8(buf, ",", 1);
367 }
368 }
369 CB_Cat_Trusted_Utf8(buf, "\n", 1);
370 S_cat_whitespace(buf, depth);
371 CB_Cat_Trusted_Utf8(buf, "}", 1);
372
373 DECREF(keys);
374 }
375
376 return true;
377 }
378
379 static Obj*
380 S_parse_json(const char *text, size_t size) {
381 void *json_parser = LucyParseJsonAlloc(Memory_wrapped_malloc);
382 if (json_parser == NULL) {
383 String *mess = MAKE_MESS("Failed to allocate JSON parser");
384 Err_set_error(Err_new(mess));
385 return NULL;
386 }
387 Obj *dump = S_do_parse_json(json_parser, text, size);
388 LucyParseJsonFree(json_parser, Memory_wrapped_free);
389 return dump;
390 }
391
392 static Obj*
393 S_do_parse_json(void *json_parser, const char *json, size_t len) {
394 lucy_JsonParserState state;
395 state.result = NULL;
396 state.errors = false;
397
398 const char *text = json;
399 const char *const end = text + len;
400 while (text < end) {
401 int token_type = -1;
402 Obj *value = NULL;
403 const char *const save = text;
404 switch (*text) {
405 case ' ': case '\n': case '\r': case '\t':
406 // Skip insignificant whitespace, which the JSON RFC defines
407 // as only four ASCII characters.
408 text++;
409 continue;
410 case '[':
411 token_type = LUCY_JSON_TOKENTYPE_LEFT_SQUARE_BRACKET;
412 text++;
413 break;
414 case ']':
415 token_type = LUCY_JSON_TOKENTYPE_RIGHT_SQUARE_BRACKET;
416 text++;
417 break;
418 case '{':
419 token_type = LUCY_JSON_TOKENTYPE_LEFT_CURLY_BRACKET;
420 text++;
421 break;
422 case '}':
423 token_type = LUCY_JSON_TOKENTYPE_RIGHT_CURLY_BRACKET;
424 text++;
425 break;
426 case ':':
427 token_type = LUCY_JSON_TOKENTYPE_COLON;
428 text++;
429 break;
430 case ',':
431 token_type = LUCY_JSON_TOKENTYPE_COMMA;
432 text++;
433 break;
434 case '"':
435 value = (Obj*)S_parse_string(&text, end);
436 if (value) {
437 token_type = LUCY_JSON_TOKENTYPE_STRING;
438 }
439 else {
440 // Clear out parser and return.
441 LucyParseJson(json_parser, 0, NULL, &state);
442 ERR_ADD_FRAME(Err_get_error());
443 return NULL;
444 }
445 break;
446 case 'n':
447 if (SI_check_keyword(text, end, "null", 4)) {
448 token_type = LUCY_JSON_TOKENTYPE_NULL;
449 text += 4;
450 }
451 break;
452 case 't':
453 if (SI_check_keyword(text, end, "true", 4)) {
454 token_type = LUCY_JSON_TOKENTYPE_TRUE;
455 value = (Obj*)CFISH_TRUE;
456 text += 4;
457 }
458 break;
459 case 'f':
460 if (SI_check_keyword(text, end, "false", 5)) {
461 token_type = LUCY_JSON_TOKENTYPE_FALSE;
462 value = (Obj*)CFISH_FALSE;
463 text += 5;
464 }
465 break;
466 case '0': case '1': case '2': case '3': case '4':
467 case '5': case '6': case '7': case '8': case '9':
468 case '-': { // Note no '+', as JSON spec doesn't allow it.
469 value = (Obj*)S_parse_number(&text, end);
470 if (value) {
471 token_type = LUCY_JSON_TOKENTYPE_NUMBER;
472 }
473 else {
474 // Clear out parser and return.
475 LucyParseJson(json_parser, 0, NULL, &state);
476 ERR_ADD_FRAME(Err_get_error());
477 return NULL;
478 }
479 }
480 break;
481 }
482 if (token_type < 0) {
483 // Clear out parser and return.
484 LucyParseJson(json_parser, 0, NULL, &state);
485 SET_ERROR("JSON syntax error", save, end);
486 return NULL;
487 }
488 LucyParseJson(json_parser, token_type, value, &state);
489 if (state.errors) {
490 SET_ERROR("JSON syntax error", save, end);
491 return NULL;
492 }
493 }
494
495 // Finish up.
496 LucyParseJson(json_parser, 0, NULL, &state);
497 if (state.errors) {
498 SET_ERROR("JSON syntax error", json, end);
499 return NULL;
500 }
501 return state.result;
502 }
503
504 static Float*
505 S_parse_number(const char **json_ptr, const char *limit) {
506 const char *top = *json_ptr;
507 const char *end = top;
508 bool terminated = false;
509
510 // We can't assume NULL termination for the JSON string, so we need to
511 // ensure that strtod() cannot overrun and access invalid memory.
512 for (; end < limit; end++) {
513 switch (*end) {
514 // Only these characters may legally follow a number in
515 // Javascript. If we don't find one before the end of the JSON,
516 // it's a parse error.
517 case ' ': case '\n': case '\r': case '\t':
518 case ']':
519 case '}':
520 case ':':
521 case ',':
522 terminated = true;
523 break;
524 }
525 }
526
527 Float *result = NULL;
528 if (terminated) {
529 char *terminus;
530 double number = strtod(top, &terminus);
531 if (terminus != top) {
532 *json_ptr = terminus;
533 result = Float_new(number);
534 }
535 }
536 if (!result) {
537 SET_ERROR("JSON syntax error", top, limit);
538 }
539 return result;
540 }
541
542 static String*
543 S_parse_string(const char **json_ptr, const char *limit) {
544 // Find terminating double quote, determine whether there are any escapes.
545 const char *top = *json_ptr + 1;
546 const char *end = NULL;
547 bool saw_backslash = false;
548 for (const char *text = top; text < limit; text++) {
549 if (*text == '"') {
550 end = text;
551 break;
552 }
553 else if (*text == '\\') {
554 saw_backslash = true;
555 if (text + 1 < limit && text[1] == 'u') {
556 text += 5;
557 }
558 else {
559 text += 1;
560 }
561 }
562 }
563 if (!end) {
564 SET_ERROR("Unterminated string", *json_ptr, limit);
565 return NULL;
566 }
567
568 // Advance the text buffer to just beyond the closing quote.
569 *json_ptr = end + 1;
570
571 if (saw_backslash) {
572 return S_unescape_text(top, end);
573 }
574 else {
575 // Optimize common case where there are no escapes.
576 size_t len = (size_t)(end - top);
577 if (!Str_utf8_valid(top, len)) {
578 String *mess = MAKE_MESS("Bad UTF-8 in JSON");
579 Err_set_error(Err_new(mess));
580 return NULL;
581 }
582 return Str_new_from_trusted_utf8(top, len);
583 }
584 }
585
586 static String*
587 S_unescape_text(const char *top, const char *end) {
588 size_t cap = (size_t)(end - top) + 1;
589 CharBuf *cb = CB_new(cap);
590 const char *chunk = top;
591 const char *text = top;
592
593 while (text < end) {
594 if (*text != '\\') {
595 text++;
596 }
597 else {
598 if (!Str_utf8_valid(chunk, (size_t)(text - chunk))) {
599 DECREF(cb);
600 String *mess = MAKE_MESS("Bad UTF-8 in JSON");
601 Err_set_error(Err_new(mess));
602 return NULL;
603 }
604 CB_Cat_Trusted_Utf8(cb, chunk, (size_t)(text - chunk));
605
606 // Process escape.
607 text++;
608 switch (*text) {
609 case '"':
610 case '\\':
611 case '/':
612 CB_Cat_Trusted_Utf8(cb, text, 1);
613 break;
614 case 'b':
615 CB_Cat_Trusted_Utf8(cb, "\b", 1);
616 break;
617 case 'f':
618 CB_Cat_Trusted_Utf8(cb, "\f", 1);
619 break;
620 case 'n':
621 CB_Cat_Trusted_Utf8(cb, "\n", 1);
622 break;
623 case 'r':
624 CB_Cat_Trusted_Utf8(cb, "\r", 1);
625 break;
626 case 't':
627 CB_Cat_Trusted_Utf8(cb, "\t", 1);
628 break;
629 case 'u': {
630 int32_t code_point = 0;
631 for (int i = 1; i < 5; i++) {
632 char c = text[i];
633 int32_t digit = 0;
634 if (c >= '0' && c <= '9') {
635 digit = c - '0';
636 }
637 else if (c >= 'a' && c <= 'f') {
638 digit = c - 'a' + 10;
639 }
640 else if (c >= 'A' && c <= 'F') {
641 digit = c - 'A' + 10;
642 }
643 else {
644 DECREF(cb);
645 SET_ERROR("Invalid \\u escape", text - 1, end);
646 return NULL;
647 }
648 code_point = code_point * 16 + digit;
649 }
650 if (code_point >= 0xD800 && code_point <= 0xDFFF) {
651 DECREF(cb);
652 SET_ERROR("Surrogate pairs not supported",
653 text - 1, end);
654 return NULL;
655 }
656 CB_Cat_Char(cb, code_point);
657 text += 4;
658 }
659 break;
660 default:
661 DECREF(cb);
662 SET_ERROR("Illegal escape", text - 1, end);
663 return NULL;
664 }
665
666 text++;
667 chunk = text;
668 }
669 }
670
671 if (!Str_utf8_valid(chunk, (size_t)(text - chunk))) {
672 DECREF(cb);
673 String *mess = MAKE_MESS("Bad UTF-8 in JSON");
674 Err_set_error(Err_new(mess));
675 return NULL;
676 }
677 CB_Cat_Trusted_Utf8(cb, chunk, (size_t)(text - chunk));
678
679 String *retval = CB_Yield_String(cb);
680 DECREF(cb);
681 return retval;
682 }
683
684 static CFISH_INLINE bool
685 SI_check_keyword(const char *json, const char* end, const char *keyword,
686 size_t len) {
687 if ((size_t)(end - json) > len
688 && strncmp(json, keyword, len) == 0
689 && json[len] != '_'
690 && !isalnum(json[len])
691 ) {
692 return true;
693 }
694 return false;
695 }
696
697 static void
698 S_set_error(const char *mess, const char *json, const char *limit, int line,
699 const char *func) {
700 CharBuf *buf = CB_new(0);
701 CB_Cat_Utf8(buf, mess, strlen(mess));
702
703 if (func) {
704 CB_catf(buf, " at %s %s line %i32 near ", func, __FILE__,
705 (int32_t)line);
706 }
707 else {
708 CB_catf(buf, " at %s line %i32 near ", __FILE__, (int32_t)line);
709 }
710
711 // Append escaped text.
712 int64_t len = limit - json;
713 if (len > 32) {
714 const char *end = StrHelp_back_utf8_char(json + 32, json);
715 len = end - json;
716 }
717 else if (len < 0) {
718 len = 0; // sanity check
719 }
720 String *snippet = SSTR_WRAP_UTF8(json, (size_t)len);
721 S_append_json_string(snippet, buf);
722
723 String *full_mess = CB_Yield_String(buf);
724 DECREF(buf);
725
726 // Set global error object.
727 Err_set_error(Err_new(full_mess));
728 }
729
730 int64_t
731 Json_obj_to_i64(Obj *obj) {
732 int64_t retval = 0;
733
734 if (!obj) {
735 THROW(ERR, "Can't extract integer from NULL");
736 }
737 else if (Obj_is_a(obj, INTEGER)) {
738 retval = Int_Get_Value((Integer*)obj);
739 }
740 else if (Obj_is_a(obj, FLOAT)) {
741 retval = Float_To_I64((Float*)obj);
742 }
743 else if (Obj_is_a(obj, STRING)) {
744 retval = Str_To_I64((String*)obj);
745 }
746 else {
747 THROW(ERR, "Can't extract integer from object of type %o",
748 Obj_get_class_name(obj));
749 }
750
751 return retval;
752 }
753
754 double
755 Json_obj_to_f64(Obj *obj) {
756 double retval = 0;
757
758 if (!obj) {
759 THROW(ERR, "Can't extract float from NULL");
760 }
761 else if (Obj_is_a(obj, FLOAT)) {
762 retval = Float_Get_Value((Float*)obj);
763 }
764 else if (Obj_is_a(obj, INTEGER)) {
765 retval = Int_To_F64((Integer*)obj);
766 }
767 else if (Obj_is_a(obj, STRING)) {
768 retval = Str_To_F64((String*)obj);
769 }
770 else {
771 THROW(ERR, "Can't extract float from object of type %o",
772 Obj_get_class_name(obj));
773 }
774
775 return retval;
776 }
777
778 bool
779 Json_obj_to_bool(Obj *obj) {
780 bool retval = false;
781
782 if (!obj) {
783 THROW(ERR, "Can't extract bool from NULL");
784 }
785 else if (Obj_is_a(obj, BOOLEAN)) {
786 retval = Bool_Get_Value((Boolean*)obj);
787 }
788 else if (Obj_is_a(obj, INTEGER)) {
789 retval = Int_Get_Value((Integer*)obj) != 0;
790 }
791 else if (Obj_is_a(obj, FLOAT)) {
792 retval = Float_Get_Value((Float*)obj) != 0.0;
793 }
794 else if (Obj_is_a(obj, STRING)) {
795 retval = (Str_To_I64((String*)obj) != 0);
796 }
797 else {
798 THROW(ERR, "Can't extract bool from object of type %o",
799 Obj_get_class_name(obj));
800 }
801
802 return retval;
803 }
804