diff options
author | Mark Pulford <mark@kyne.com.au> | 2011-04-15 23:37:41 +0930 |
---|---|---|
committer | Mark Pulford <mark@kyne.com.au> | 2011-04-15 23:37:41 +0930 |
commit | 9f3d6b59c5f097d66e94f987c7731d7a4113057f (patch) | |
tree | 4a3b0aa4f59a0b645b0cfb12fc89d260f1fca9b8 /lua_json.c | |
parent | a99fc753a590d7dd1cf19d083af504cb3ec9a8d4 (diff) | |
download | lua-cjson-9f3d6b59c5f097d66e94f987c7731d7a4113057f.tar.gz lua-cjson-9f3d6b59c5f097d66e94f987c7731d7a4113057f.tar.bz2 lua-cjson-9f3d6b59c5f097d66e94f987c7731d7a4113057f.zip |
Merge lua_json_encode/decode.c into lua_json.c
Diffstat (limited to 'lua_json.c')
-rw-r--r-- | lua_json.c | 668 |
1 files changed, 668 insertions, 0 deletions
diff --git a/lua_json.c b/lua_json.c new file mode 100644 index 0000000..ab72dd5 --- /dev/null +++ b/lua_json.c | |||
@@ -0,0 +1,668 @@ | |||
1 | /* Lua JSON routines | ||
2 | */ | ||
3 | |||
4 | /* Caveats: | ||
5 | * - No unicode support | ||
6 | * - JSON "null" values are represented as lightuserdata. Compare with | ||
7 | * json.null. | ||
8 | * - Parsing comments is not support. According to json.org, this isn't | ||
9 | * part of the spec. | ||
10 | */ | ||
11 | |||
12 | /* FIXME: | ||
13 | * - Ensure JSON data is UTF-8. Fail otherwise. | ||
14 | * - Alternatively, dynamically support Unicode in JSON string. Return current locale. | ||
15 | * - Use lua_checkstack() to ensure there is enough stack space left to | ||
16 | * fulfill an operation. What happens if we don't, is that acceptible too? | ||
17 | * Does lua_checkstack grow the stack, or merely check if it is possible? | ||
18 | */ | ||
19 | |||
20 | /* FIXME: | ||
21 | * - Option to encode non-printable characters? Only \" \\ are required | ||
22 | * - Unicode? | ||
23 | */ | ||
24 | |||
25 | /* FIXME: | ||
26 | * - Review memory allocation handling and error returns. | ||
27 | * Ensure all memory is free. Including after exceptions. | ||
28 | */ | ||
29 | |||
30 | #include <assert.h> | ||
31 | #include <string.h> | ||
32 | #include <math.h> | ||
33 | |||
34 | #include <lua.h> | ||
35 | #include <lauxlib.h> | ||
36 | |||
37 | #include "lua_json.h" | ||
38 | #include "utils.h" | ||
39 | #include "strbuf.h" | ||
40 | |||
41 | /* ===== ENCODING ===== */ | ||
42 | |||
43 | |||
44 | /* JSON escape a character if required, or return NULL */ | ||
45 | static inline char *json_escape_char(int c) | ||
46 | { | ||
47 | switch(c) { | ||
48 | case 0: | ||
49 | return "\\u0000"; | ||
50 | case '\\': | ||
51 | return "\\\\"; | ||
52 | case '"': | ||
53 | return "\\\""; | ||
54 | case '\b': | ||
55 | return "\\b"; | ||
56 | case '\t': | ||
57 | return "\\t"; | ||
58 | case '\n': | ||
59 | return "\\n"; | ||
60 | case '\f': | ||
61 | return "\\f"; | ||
62 | case '\r': | ||
63 | return "\\r"; | ||
64 | } | ||
65 | |||
66 | return NULL; | ||
67 | } | ||
68 | |||
69 | /* json_append_string args: | ||
70 | * - lua_State | ||
71 | * - JSON strbuf | ||
72 | * - String (Lua stack index) | ||
73 | * | ||
74 | * Returns nothing. Doesn't remove string from Lua stack */ | ||
75 | static void json_append_string(lua_State *l, strbuf_t *json, int lindex) | ||
76 | { | ||
77 | char *p; | ||
78 | int i; | ||
79 | const char *str; | ||
80 | size_t len; | ||
81 | |||
82 | str = lua_tolstring(l, lindex, &len); | ||
83 | |||
84 | strbuf_append_char(json, '\"'); | ||
85 | for (i = 0; i < len; i++) { | ||
86 | p = json_escape_char(str[i]); | ||
87 | if (p) | ||
88 | strbuf_append_string(json, p); | ||
89 | else | ||
90 | strbuf_append_char(json, str[i]); | ||
91 | } | ||
92 | strbuf_append_char(json, '\"'); | ||
93 | } | ||
94 | |||
95 | /* Find the size of the array on the top of the Lua stack | ||
96 | * -1 object (not a pure array) | ||
97 | * >=0 elements in array | ||
98 | */ | ||
99 | static int lua_array_length(lua_State *l) | ||
100 | { | ||
101 | double k; | ||
102 | int max; | ||
103 | |||
104 | max = 0; | ||
105 | |||
106 | lua_pushnil(l); | ||
107 | /* table, startkey */ | ||
108 | while (lua_next(l, -2) != 0) { | ||
109 | /* table, key, value */ | ||
110 | if ((k = lua_tonumber(l, -2))) { | ||
111 | /* Integer >= 1 ? */ | ||
112 | if (floor(k) == k && k >= 1) { | ||
113 | if (k > max) | ||
114 | max = k; | ||
115 | lua_pop(l, 1); | ||
116 | continue; | ||
117 | } | ||
118 | } | ||
119 | |||
120 | /* Must not be an array (non integer key) */ | ||
121 | lua_pop(l, 2); | ||
122 | return -1; | ||
123 | } | ||
124 | |||
125 | return max; | ||
126 | } | ||
127 | |||
128 | static void json_append_data(lua_State *l, strbuf_t *json); | ||
129 | |||
130 | /* json_append_array args: | ||
131 | * - lua_State | ||
132 | * - JSON strbuf | ||
133 | * - Size of passwd Lua array (top of stack) */ | ||
134 | static void json_append_array(lua_State *l, strbuf_t *json, int array_length) | ||
135 | { | ||
136 | int comma, i; | ||
137 | |||
138 | strbuf_append_string(json, "[ "); | ||
139 | |||
140 | comma = 0; | ||
141 | for (i = 1; i <= array_length; i++) { | ||
142 | if (comma) | ||
143 | strbuf_append_string(json, ", "); | ||
144 | else | ||
145 | comma = 1; | ||
146 | |||
147 | lua_rawgeti(l, -1, i); | ||
148 | json_append_data(l, json); | ||
149 | lua_pop(l, 1); | ||
150 | } | ||
151 | |||
152 | strbuf_append_string(json, " ]"); | ||
153 | } | ||
154 | |||
155 | static void json_append_object(lua_State *l, strbuf_t *json) | ||
156 | { | ||
157 | int comma, keytype; | ||
158 | |||
159 | /* Object */ | ||
160 | strbuf_append_string(json, "{ "); | ||
161 | |||
162 | lua_pushnil(l); | ||
163 | /* table, startkey */ | ||
164 | comma = 0; | ||
165 | while (lua_next(l, -2) != 0) { | ||
166 | if (comma) | ||
167 | strbuf_append_string(json, ", "); | ||
168 | else | ||
169 | comma = 1; | ||
170 | |||
171 | /* table, key, value */ | ||
172 | keytype = lua_type(l, -2); | ||
173 | if (keytype == LUA_TNUMBER) { | ||
174 | strbuf_append_fmt(json, "\"" LUA_NUMBER_FMT "\": ", | ||
175 | lua_tonumber(l, -2)); | ||
176 | } else if (keytype == LUA_TSTRING) { | ||
177 | json_append_string(l, json, -2); | ||
178 | strbuf_append_string(json, ": "); | ||
179 | } else { | ||
180 | die("Cannot serialise table key %s", lua_typename(l, lua_type(l, -2))); | ||
181 | } | ||
182 | |||
183 | /* table, key, value */ | ||
184 | json_append_data(l, json); | ||
185 | lua_pop(l, 1); | ||
186 | /* table, key */ | ||
187 | } | ||
188 | |||
189 | strbuf_append_string(json, " }"); | ||
190 | } | ||
191 | |||
192 | /* Serialise Lua data into JSON string. | ||
193 | * | ||
194 | * FIXME: | ||
195 | * - Error handling when cannot serialise key or value (return to script) | ||
196 | */ | ||
197 | static void json_append_data(lua_State *l, strbuf_t *json) | ||
198 | { | ||
199 | int len; | ||
200 | |||
201 | switch (lua_type(l, -1)) { | ||
202 | case LUA_TSTRING: | ||
203 | json_append_string(l, json, -1); | ||
204 | break; | ||
205 | case LUA_TNUMBER: | ||
206 | strbuf_append_fmt(json, "%lf", lua_tonumber(l, -1)); | ||
207 | break; | ||
208 | case LUA_TBOOLEAN: | ||
209 | if (lua_toboolean(l, -1)) | ||
210 | strbuf_append_string(json, "true"); | ||
211 | else | ||
212 | strbuf_append_string(json, "false"); | ||
213 | break; | ||
214 | case LUA_TTABLE: | ||
215 | len = lua_array_length(l); | ||
216 | if (len >= 0) | ||
217 | json_append_array(l, json, len); | ||
218 | else | ||
219 | json_append_object(l, json); | ||
220 | break; | ||
221 | case LUA_TNIL: | ||
222 | strbuf_append_string(json, "null"); | ||
223 | break; | ||
224 | case LUA_TLIGHTUSERDATA: | ||
225 | if (lua_touserdata(l, -1) == NULL) { | ||
226 | strbuf_append_string(json, "null"); | ||
227 | break; | ||
228 | } | ||
229 | default: | ||
230 | /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, and LUA_TLIGHTUSERDATA) | ||
231 | * cannot be serialised */ | ||
232 | /* FIXME: return error */ | ||
233 | die("Cannot serialise %s", lua_typename(l, lua_type(l, -1))); | ||
234 | } | ||
235 | } | ||
236 | |||
237 | char *lua_json_encode(lua_State *l, int *len) | ||
238 | { | ||
239 | strbuf_t buf; | ||
240 | char *json; | ||
241 | |||
242 | strbuf_init(&buf); | ||
243 | strbuf_set_increment(&buf, 256); | ||
244 | json_append_data(l, &buf); | ||
245 | json = strbuf_free_to_string(&buf, len); | ||
246 | |||
247 | return json; | ||
248 | } | ||
249 | |||
250 | int lua_api_json_encode(lua_State *l) | ||
251 | { | ||
252 | char *json; | ||
253 | int len; | ||
254 | |||
255 | json = lua_json_encode(l, &len); | ||
256 | lua_pushlstring(l, json, len); | ||
257 | free(json); | ||
258 | |||
259 | return 1; | ||
260 | } | ||
261 | |||
262 | /* ===== DECODING ===== */ | ||
263 | |||
264 | typedef struct { | ||
265 | const char *data; | ||
266 | int index; | ||
267 | strbuf_t *tmp; /* Temporary storage for strings */ | ||
268 | } json_parse_t; | ||
269 | |||
270 | typedef enum { | ||
271 | T_OBJ_BEGIN, | ||
272 | T_OBJ_END, | ||
273 | T_ARR_BEGIN, | ||
274 | T_ARR_END, | ||
275 | T_STRING, | ||
276 | T_NUMBER, | ||
277 | T_BOOLEAN, | ||
278 | T_NULL, | ||
279 | T_COLON, | ||
280 | T_COMMA, | ||
281 | T_END, | ||
282 | T_WHITESPACE, | ||
283 | T_ERROR, | ||
284 | T_UNKNOWN | ||
285 | } json_token_type_t; | ||
286 | |||
287 | static const char *json_token_type_name[] = { | ||
288 | "T_OBJ_BEGIN", | ||
289 | "T_OBJ_END", | ||
290 | "T_ARR_BEGIN", | ||
291 | "T_ARR_END", | ||
292 | "T_STRING", | ||
293 | "T_NUMBER", | ||
294 | "T_BOOLEAN", | ||
295 | "T_NULL", | ||
296 | "T_COLON", | ||
297 | "T_COMMA", | ||
298 | "T_END", | ||
299 | "T_WHITESPACE", | ||
300 | "T_ERROR", | ||
301 | "T_UNKNOWN", | ||
302 | NULL | ||
303 | }; | ||
304 | |||
305 | typedef struct { | ||
306 | json_token_type_t type; | ||
307 | int index; | ||
308 | union { | ||
309 | char *string; | ||
310 | double number; | ||
311 | int boolean; | ||
312 | } value; | ||
313 | int length; /* FIXME: Merge into union? Won't save memory, but more logical */ | ||
314 | } json_token_t; | ||
315 | |||
316 | static void json_process_value(lua_State *l, json_parse_t *json, json_token_t *token); | ||
317 | |||
318 | static json_token_type_t json_ch2token[256]; | ||
319 | static char json_ch2escape[256]; | ||
320 | |||
321 | static void json_init_lookup_tables() | ||
322 | { | ||
323 | int i; | ||
324 | |||
325 | /* Tag all characters as an error */ | ||
326 | for (i = 0; i < 256; i++) | ||
327 | json_ch2token[i] = T_ERROR; | ||
328 | |||
329 | /* Set tokens that require no further processing */ | ||
330 | json_ch2token['{'] = T_OBJ_BEGIN; | ||
331 | json_ch2token['}'] = T_OBJ_END; | ||
332 | json_ch2token['['] = T_ARR_BEGIN; | ||
333 | json_ch2token[']'] = T_ARR_END; | ||
334 | json_ch2token[','] = T_COMMA; | ||
335 | json_ch2token[':'] = T_COLON; | ||
336 | json_ch2token['\0'] = T_END; | ||
337 | json_ch2token[' '] = T_WHITESPACE; | ||
338 | json_ch2token['\t'] = T_WHITESPACE; | ||
339 | json_ch2token['\n'] = T_WHITESPACE; | ||
340 | json_ch2token['\r'] = T_WHITESPACE; | ||
341 | |||
342 | /* Update characters that require further processing */ | ||
343 | json_ch2token['n'] = T_UNKNOWN; | ||
344 | json_ch2token['t'] = T_UNKNOWN; | ||
345 | json_ch2token['f'] = T_UNKNOWN; | ||
346 | json_ch2token['"'] = T_UNKNOWN; | ||
347 | json_ch2token['-'] = T_UNKNOWN; | ||
348 | for (i = 0; i < 10; i++) | ||
349 | json_ch2token['0' + i] = T_UNKNOWN; | ||
350 | |||
351 | for (i = 0; i < 256; i++) | ||
352 | json_ch2escape[i] = 0; /* String error */ | ||
353 | |||
354 | json_ch2escape['"'] = '"'; | ||
355 | json_ch2escape['\\'] = '\\'; | ||
356 | json_ch2escape['/'] = '/'; | ||
357 | json_ch2escape['b'] = '\b'; | ||
358 | json_ch2escape['t'] = '\t'; | ||
359 | json_ch2escape['n'] = '\n'; | ||
360 | json_ch2escape['f'] = '\f'; | ||
361 | json_ch2escape['r'] = '\r'; | ||
362 | json_ch2escape['u'] = 'u'; /* This needs to be parsed as unicode */ | ||
363 | } | ||
364 | |||
365 | static void json_next_string_token(json_parse_t *json, json_token_t *token) | ||
366 | { | ||
367 | char ch; | ||
368 | |||
369 | /* Caller must ensure a string is next */ | ||
370 | assert(json->data[json->index] == '"'); | ||
371 | |||
372 | /* Gobble string. FIXME, ugly */ | ||
373 | |||
374 | json->tmp->length = 0; | ||
375 | while ((ch = json->data[++json->index]) != '"') { | ||
376 | /* Handle escapes */ | ||
377 | if (ch == '\\') { | ||
378 | /* Translate escape code */ | ||
379 | ch = json_ch2escape[(unsigned char)json->data[++json->index]]; | ||
380 | if (!ch) { | ||
381 | /* Invalid escape code */ | ||
382 | token->type = T_ERROR; | ||
383 | return; | ||
384 | } | ||
385 | if (ch == 'u') { | ||
386 | /* Process unicode */ | ||
387 | /* FIXME: cleanup memory handling. Implement iconv(3) | ||
388 | * conversion from UCS-2 -> UTF-8 | ||
389 | */ | ||
390 | if (!memcmp(&json->data[json->index], "u0000", 5)) { | ||
391 | /* Handle NULL */ | ||
392 | ch = 0; | ||
393 | json->index += 4; | ||
394 | } else { | ||
395 | /* Remaining codepoints unhandled */ | ||
396 | token->type = T_ERROR; | ||
397 | return; | ||
398 | } | ||
399 | } | ||
400 | } | ||
401 | strbuf_append_char(json->tmp, ch); | ||
402 | } | ||
403 | json->index++; /* Eat final quote (") */ | ||
404 | |||
405 | strbuf_ensure_null(json->tmp); | ||
406 | |||
407 | token->type = T_STRING; | ||
408 | token->value.string = strbuf_string(json->tmp, NULL); | ||
409 | token->length = json->tmp->length; | ||
410 | } | ||
411 | |||
412 | static void json_next_number_token(json_parse_t *json, json_token_t *token) | ||
413 | { | ||
414 | const char *startptr; | ||
415 | char *endptr; | ||
416 | |||
417 | /* FIXME: | ||
418 | * Verify that the number takes the following form: | ||
419 | * -?(0|[1-9]|[1-9][0-9]+)(.[0-9]+)?([eE][-+]?[0-9]+)? | ||
420 | * strtod() below allows other forms (Hex, infinity, NaN,..) */ | ||
421 | /* i = json->index; | ||
422 | if (json->data[i] == '-') | ||
423 | i++; | ||
424 | j = i; | ||
425 | while ('0' <= json->data[i] && json->data[i] <= '9') | ||
426 | i++; | ||
427 | if (i == j) | ||
428 | return T_ERROR; */ | ||
429 | |||
430 | token->type = T_NUMBER; | ||
431 | startptr = &json->data[json->index]; | ||
432 | token->value.number = strtod(&json->data[json->index], &endptr); | ||
433 | if (startptr == endptr) | ||
434 | token->type = T_ERROR; | ||
435 | else | ||
436 | json->index += endptr - startptr; /* Skip the processed number */ | ||
437 | |||
438 | return; | ||
439 | } | ||
440 | |||
441 | /* Fills in the token struct. | ||
442 | * T_STRING will return a pointer to the json_parse_t temporary string | ||
443 | * T_ERROR will leave the json->index pointer at the error. | ||
444 | */ | ||
445 | static void json_next_token(json_parse_t *json, json_token_t *token) | ||
446 | { | ||
447 | int ch; | ||
448 | |||
449 | /* Eat whitespace. FIXME: UGLY */ | ||
450 | token->type = json_ch2token[(unsigned char)json->data[json->index]]; | ||
451 | while (token->type == T_WHITESPACE) | ||
452 | token->type = json_ch2token[(unsigned char)json->data[++json->index]]; | ||
453 | |||
454 | token->index = json->index; | ||
455 | |||
456 | /* Don't advance the pointer for an error or the end */ | ||
457 | if (token->type == T_ERROR || token->type == T_END) | ||
458 | return; | ||
459 | |||
460 | /* Found a known token, advance index and return */ | ||
461 | if (token->type != T_UNKNOWN) { | ||
462 | json->index++; | ||
463 | return; | ||
464 | } | ||
465 | |||
466 | /* Process characters which triggered T_UNKNOWN */ | ||
467 | ch = json->data[json->index]; | ||
468 | |||
469 | if (ch == '"') { | ||
470 | json_next_string_token(json, token); | ||
471 | return; | ||
472 | } else if (ch == '-' || ('0' <= ch && ch <= '9')) { | ||
473 | json_next_number_token(json, token); | ||
474 | return; | ||
475 | } else if (!strncmp(&json->data[json->index], "true", 4)) { | ||
476 | token->type = T_BOOLEAN; | ||
477 | token->value.boolean = 1; | ||
478 | json->index += 4; | ||
479 | return; | ||
480 | } else if (!strncmp(&json->data[json->index], "false", 5)) { | ||
481 | token->type = T_BOOLEAN; | ||
482 | token->value.boolean = 0; | ||
483 | json->index += 5; | ||
484 | return; | ||
485 | } else if (!strncmp(&json->data[json->index], "null", 4)) { | ||
486 | token->type = T_NULL; | ||
487 | json->index += 4; | ||
488 | return; | ||
489 | } | ||
490 | |||
491 | token->type = T_ERROR; | ||
492 | } | ||
493 | |||
494 | /* This function does not return. | ||
495 | * DO NOT CALL WITH DYNAMIC MEMORY ALLOCATED. | ||
496 | * The only allowed exception is the temporary parser string | ||
497 | * json->tmp struct. | ||
498 | * json and token should exist on the stack somewhere. | ||
499 | * luaL_error() will long_jmp and release the stack */ | ||
500 | static void json_throw_parse_error(lua_State *l, json_parse_t *json, | ||
501 | const char *exp, json_token_t *token) | ||
502 | { | ||
503 | strbuf_free(json->tmp); | ||
504 | luaL_error(l, "Expected %s but found type <%s> at character %d", | ||
505 | exp, json_token_type_name[token->type], token->index); | ||
506 | } | ||
507 | |||
508 | static void json_parse_object_context(lua_State *l, json_parse_t *json) | ||
509 | { | ||
510 | json_token_t token; | ||
511 | |||
512 | lua_newtable(l); | ||
513 | |||
514 | json_next_token(json, &token); | ||
515 | |||
516 | /* Handle empty objects */ | ||
517 | if (token.type == T_OBJ_END) | ||
518 | return; | ||
519 | |||
520 | while (1) { | ||
521 | if (token.type != T_STRING) | ||
522 | json_throw_parse_error(l, json, "object key", &token); | ||
523 | |||
524 | lua_pushlstring(l, token.value.string, token.length); /* Push key */ | ||
525 | |||
526 | json_next_token(json, &token); | ||
527 | if (token.type != T_COLON) | ||
528 | json_throw_parse_error(l, json, "colon", &token); | ||
529 | |||
530 | json_next_token(json, &token); | ||
531 | json_process_value(l, json, &token); | ||
532 | lua_rawset(l, -3); /* Set key = value */ | ||
533 | |||
534 | json_next_token(json, &token); | ||
535 | |||
536 | if (token.type == T_OBJ_END) | ||
537 | return; | ||
538 | |||
539 | if (token.type != T_COMMA) | ||
540 | json_throw_parse_error(l, json, "comma or object end", &token); | ||
541 | |||
542 | json_next_token(json, &token); | ||
543 | } | ||
544 | } | ||
545 | |||
546 | /* Handle the array context */ | ||
547 | static void json_parse_array_context(lua_State *l, json_parse_t *json) | ||
548 | { | ||
549 | json_token_t token; | ||
550 | int i; | ||
551 | |||
552 | lua_newtable(l); | ||
553 | |||
554 | json_next_token(json, &token); | ||
555 | |||
556 | /* Handle empty arrays */ | ||
557 | if (token.type == T_ARR_END) | ||
558 | return; | ||
559 | |||
560 | i = 1; | ||
561 | while (1) { | ||
562 | json_process_value(l, json, &token); | ||
563 | lua_rawseti(l, -2, i); /* arr[i] = value */ | ||
564 | |||
565 | json_next_token(json, &token); | ||
566 | |||
567 | if (token.type == T_ARR_END) | ||
568 | return; | ||
569 | |||
570 | if (token.type != T_COMMA) | ||
571 | json_throw_parse_error(l, json, "comma or array end", &token); | ||
572 | |||
573 | json_next_token(json, &token); | ||
574 | i++; | ||
575 | } | ||
576 | } | ||
577 | |||
578 | /* Handle the "value" context */ | ||
579 | static void json_process_value(lua_State *l, json_parse_t *json, json_token_t *token) | ||
580 | { | ||
581 | switch (token->type) { | ||
582 | case T_STRING: | ||
583 | lua_pushlstring(l, token->value.string, token->length); | ||
584 | break;; | ||
585 | case T_NUMBER: | ||
586 | lua_pushnumber(l, token->value.number); | ||
587 | break;; | ||
588 | case T_BOOLEAN: | ||
589 | lua_pushboolean(l, token->value.boolean); | ||
590 | break;; | ||
591 | case T_OBJ_BEGIN: | ||
592 | json_parse_object_context(l, json); | ||
593 | break;; | ||
594 | case T_ARR_BEGIN: | ||
595 | json_parse_array_context(l, json); | ||
596 | break;; | ||
597 | case T_NULL: | ||
598 | /* In Lua, setting "t[k] = nil" will delete k from the table. | ||
599 | * Hence a NULL pointer lightuserdata object is used instead */ | ||
600 | lua_pushlightuserdata(l, NULL); | ||
601 | break;; | ||
602 | default: | ||
603 | json_throw_parse_error(l, json, "value", token); | ||
604 | } | ||
605 | } | ||
606 | |||
607 | /* json_text must be null terminated string */ | ||
608 | void lua_json_decode(lua_State *l, const char *json_text) | ||
609 | { | ||
610 | json_parse_t json; | ||
611 | json_token_t token; | ||
612 | |||
613 | json.data = json_text; | ||
614 | json.index = 0; | ||
615 | json.tmp = strbuf_new(); | ||
616 | strbuf_set_increment(json.tmp, 256); | ||
617 | |||
618 | json_next_token(&json, &token); | ||
619 | json_process_value(l, &json, &token); | ||
620 | |||
621 | /* Ensure there is no more input left */ | ||
622 | json_next_token(&json, &token); | ||
623 | |||
624 | if (token.type != T_END) | ||
625 | json_throw_parse_error(l, &json, "the end", &token); | ||
626 | |||
627 | strbuf_free(json.tmp); | ||
628 | } | ||
629 | |||
630 | static int lua_api_json_decode(lua_State *l) | ||
631 | { | ||
632 | int i, n; | ||
633 | |||
634 | n = lua_gettop(l); | ||
635 | |||
636 | for (i = 1; i <= n; i++) { | ||
637 | if (lua_isstring(l, i)) { | ||
638 | lua_json_decode(l, lua_tostring(l, i)); | ||
639 | } else { | ||
640 | lua_pushnil(l); | ||
641 | } | ||
642 | } | ||
643 | |||
644 | return n; /* Number of results */ | ||
645 | } | ||
646 | |||
647 | /* ===== INITIALISATION ===== */ | ||
648 | |||
649 | void lua_json_init(lua_State *l) | ||
650 | { | ||
651 | luaL_Reg reg[] = { | ||
652 | { "encode", lua_api_json_encode }, | ||
653 | { "decode", lua_api_json_decode }, | ||
654 | { NULL, NULL } | ||
655 | }; | ||
656 | |||
657 | json_init_lookup_tables(); | ||
658 | |||
659 | luaL_register(l, "json", reg); | ||
660 | |||
661 | /* Set json.null, and pop "json" table from the stack */ | ||
662 | lua_pushlightuserdata(l, NULL); | ||
663 | lua_setfield(l, -2, "null"); | ||
664 | lua_pop(l, 1); | ||
665 | } | ||
666 | |||
667 | /* vi:ai et sw=4 ts=4: | ||
668 | */ | ||