webmcp

annotate libraries/json/json.c @ 136:3cf5fcf2bd5f

Some code cleanup and documentation of JSON library
author jbe
date Mon Jul 28 17:16:34 2014 +0200 (2014-07-28)
parents 663722e35330
children f490b78827d6
rev   line source
jbe@121 1 #include <lua.h>
jbe@121 2 #include <lauxlib.h>
jbe@122 3 #include <stdlib.h>
jbe@121 4 #include <string.h>
jbe@121 5
jbe@130 6 #define JSON_UPVAL_NULLMARK lua_upvalueindex(1)
jbe@130 7 #define JSON_UPVAL_SHADOWTBL lua_upvalueindex(2)
jbe@130 8 #define JSON_UPVAL_TYPES lua_upvalueindex(3)
jbe@130 9 #define JSON_UPVAL_METATABLE lua_upvalueindex(4)
jbe@135 10 #define JSON_UPVAL_PAIRS_ITERFUNC lua_upvalueindex(5)
jbe@135 11 #define JSON_UPVAL_IPAIRS_ITERFUNC lua_upvalueindex(6)
jbe@123 12
jbe@136 13 // marks a table as JSON object or JSON array:
jbe@136 14 // (returns its modified argument or a new table if argument is nil)
jbe@136 15 static int json_mark(lua_State *L, const char *marker) {
jbe@136 16 // if argument is nil, then create new table:
jbe@136 17 if (lua_isnoneornil(L, 1)) {
jbe@136 18 lua_settop(L, 0);
jbe@136 19 lua_newtable(L);
jbe@136 20 // skip testing of existing shadow table:
jbe@136 21 goto json_object_create_shadow_table;
jbe@136 22 }
jbe@136 23 lua_pushvalue(L, 1);
jbe@136 24 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
jbe@136 25 if (lua_isnil(L, -1)) {
jbe@136 26 json_object_create_shadow_table:
jbe@136 27 // set shadow table:
jbe@136 28 lua_pushvalue(L, 1);
jbe@136 29 lua_newtable(L);
jbe@136 30 lua_rawset(L, JSON_UPVAL_SHADOWTBL);
jbe@136 31 }
jbe@136 32 // set metatable:
jbe@136 33 lua_pushvalue(L, JSON_UPVAL_METATABLE);
jbe@136 34 lua_setmetatable(L, 1);
jbe@136 35 // mark table as JSON object or as JSON array
jbe@136 36 lua_pushvalue(L, 1);
jbe@136 37 lua_pushstring(L, marker);
jbe@136 38 lua_rawset(L, JSON_UPVAL_TYPES);
jbe@136 39 return 1;
jbe@136 40 }
jbe@136 41
jbe@136 42 // marks a table as JSON object:
jbe@136 43 // (returns its modified argument or a new table if argument is nil)
jbe@136 44 static int json_object(lua_State *L) {
jbe@136 45 return json_mark(L, "object");
jbe@136 46 }
jbe@136 47
jbe@136 48 // marks a table as JSON array:
jbe@136 49 // (returns its modified argument or a new table if argument is nil)
jbe@136 50 static int json_array(lua_State *L) {
jbe@136 51 return json_mark(L, "array");
jbe@136 52 }
jbe@136 53
jbe@124 54 #define JSON_STATE_VALUE 0
jbe@124 55 #define JSON_STATE_OBJECT_KEY 1
jbe@124 56 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2
jbe@124 57 #define JSON_STATE_OBJECT_VALUE 3
jbe@124 58 #define JSON_STATE_OBJECT_SEPARATOR 4
jbe@124 59 #define JSON_STATE_ARRAY_VALUE 5
jbe@124 60 #define JSON_STATE_ARRAY_SEPARATOR 6
jbe@124 61 #define JSON_STATE_END 7
jbe@121 62
jbe@136 63 // decodes a JSON document:
jbe@121 64 static int json_import(lua_State *L) {
jbe@136 65 const char *str; // string to parse
jbe@136 66 size_t total; // total length of string to parse
jbe@136 67 size_t pos = 0; // current position in string to parse
jbe@136 68 size_t level = 0; // nested levels of objects/arrays currently being processed
jbe@136 69 int mode = JSON_STATE_VALUE; // state of parser
jbe@136 70 char c; // variable to store a single character to be processed
jbe@136 71 luaL_Buffer luabuf; // Lua buffer to decode (possibly escaped) strings
jbe@136 72 char *cbuf; // C buffer to decode (possibly escaped) strings
jbe@136 73 size_t writepos; // write position of decoded strings in C buffer
jbe@136 74 // require string as first argument:
jbe@136 75 str = luaL_checklstring(L, 1, &total);
jbe@136 76 // if string contains a NULL byte, this is a syntax error
jbe@136 77 if (strlen(str) != total) goto json_import_syntax_error;
jbe@136 78 // main loop of parser:
jbe@136 79 json_import_loop:
jbe@136 80 // skip whitespace and store next character in variable 'c':
jbe@121 81 while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++;
jbe@136 82 // switch statement to handle certain (single) characters:
jbe@121 83 switch (c) {
jbe@136 84 // handle end of JSON document:
jbe@121 85 case 0:
jbe@136 86 // if end of JSON document was expected, then return top element of stack as result:
jbe@124 87 if (mode == JSON_STATE_END) return 1;
jbe@136 88 // otherwise, the JSON document was malformed:
jbe@121 89 json_import_unexpected_eof:
jbe@121 90 lua_pushnil(L);
jbe@121 91 if (level == 0) lua_pushliteral(L, "Empty string");
jbe@121 92 else lua_pushliteral(L, "Unexpected end of JSON document");
jbe@121 93 return 2;
jbe@136 94 // new JSON object:
jbe@121 95 case '{':
jbe@136 96 // if a JSON object is not expected here, then return an error:
jbe@124 97 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
jbe@121 98 goto json_import_syntax_error;
jbe@136 99 // consume input character:
jbe@121 100 pos++;
jbe@136 101 // create JSON object on stack:
jbe@136 102 lua_newtable(L);
jbe@136 103 // set metatable of JSON object:
jbe@125 104 lua_pushvalue(L, JSON_UPVAL_METATABLE);
jbe@125 105 lua_setmetatable(L, -2);
jbe@136 106 // mark JSON object as JSON object (as opposed to JSON array):
jbe@127 107 lua_pushvalue(L, -1);
jbe@127 108 lua_pushliteral(L, "object");
jbe@127 109 lua_rawset(L, JSON_UPVAL_TYPES);
jbe@136 110 // create internal shadow table on stack:
jbe@136 111 lua_newtable(L);
jbe@136 112 // register internal shadow table:
jbe@123 113 lua_pushvalue(L, -2);
jbe@123 114 lua_pushvalue(L, -2);
jbe@130 115 lua_rawset(L, JSON_UPVAL_SHADOWTBL);
jbe@136 116 // increment level:
jbe@121 117 level++;
jbe@136 118 // expect object key (or end of object) and continue with loop:
jbe@136 119 mode = JSON_STATE_OBJECT_KEY;
jbe@121 120 goto json_import_loop;
jbe@136 121 // new JSON array:
jbe@121 122 case '[':
jbe@136 123 // if a JSON array is not expected here, then return an error:
jbe@124 124 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
jbe@121 125 goto json_import_syntax_error;
jbe@136 126 // consume input character:
jbe@121 127 pos++;
jbe@136 128 // create JSON array on stack:
jbe@136 129 lua_newtable(L);
jbe@136 130 // set metatable of JSON array:
jbe@125 131 lua_pushvalue(L, JSON_UPVAL_METATABLE);
jbe@125 132 lua_setmetatable(L, -2);
jbe@136 133 // mark JSON array as JSON array (as opposed to JSON object):
jbe@127 134 lua_pushvalue(L, -1);
jbe@127 135 lua_pushliteral(L, "array");
jbe@127 136 lua_rawset(L, JSON_UPVAL_TYPES);
jbe@136 137 // create internal shadow table on stack:
jbe@136 138 lua_newtable(L);
jbe@136 139 // register internal shadow table:
jbe@123 140 lua_pushvalue(L, -2);
jbe@123 141 lua_pushvalue(L, -2);
jbe@130 142 lua_rawset(L, JSON_UPVAL_SHADOWTBL);
jbe@136 143 // increment level:
jbe@121 144 level++;
jbe@136 145 // expect array value (or end of array) and continue with loop:
jbe@136 146 mode = JSON_STATE_ARRAY_VALUE;
jbe@121 147 goto json_import_loop;
jbe@136 148 // end of JSON object:
jbe@121 149 case '}':
jbe@136 150 // if end of JSON object is not expected here, then return an error:
jbe@124 151 if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR)
jbe@121 152 goto json_import_syntax_error;
jbe@136 153 // jump to common code for end of JSON object and JSON array:
jbe@121 154 goto json_import_close;
jbe@136 155 // end of JSON array:
jbe@121 156 case ']':
jbe@136 157 // if end of JSON array is not expected here, then return an error:
jbe@124 158 if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR)
jbe@121 159 goto json_import_syntax_error;
jbe@136 160 // continue with common code for end of JSON object and JSON array:
jbe@136 161 // common code for end of JSON object or JSON array:
jbe@121 162 json_import_close:
jbe@136 163 // consume input character:
jbe@121 164 pos++;
jbe@136 165 // pop shadow table:
jbe@136 166 lua_pop(L, 1);
jbe@136 167 // check if nested:
jbe@121 168 if (--level) {
jbe@136 169 // if nested, then check if outer(!) structure is an array or object:
jbe@136 170 lua_pushvalue(L, c == '}' ? -4 : -3);
jbe@136 171 lua_rawget(L, JSON_UPVAL_TYPES);
jbe@136 172 if (lua_tostring(L, -1)[0] == 'a') {
jbe@136 173 // select array value processing:
jbe@124 174 mode = JSON_STATE_ARRAY_VALUE;
jbe@121 175 } else {
jbe@136 176 // select object value processing:
jbe@124 177 mode = JSON_STATE_OBJECT_VALUE;
jbe@121 178 }
jbe@136 179 // pop JSON type from stack (from rawget JSON_UPVAL_TYPES):
jbe@136 180 lua_pop(L, 1);
jbe@136 181 // store value in outer structure:
jbe@121 182 goto json_import_process_value;
jbe@121 183 }
jbe@136 184 // if not nested, then expect end of JSON document and continue with loop:
jbe@136 185 mode = JSON_STATE_END;
jbe@121 186 goto json_import_loop;
jbe@136 187 // key terminator:
jbe@121 188 case ':':
jbe@136 189 // if key terminator is not expected here, then return an error:
jbe@124 190 if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR)
jbe@121 191 goto json_import_syntax_error;
jbe@136 192 // consume input character:
jbe@121 193 pos++;
jbe@136 194 // set state of parser and continue with loop:
jbe@124 195 mode = JSON_STATE_OBJECT_VALUE;
jbe@121 196 goto json_import_loop;
jbe@136 197 // value terminator (NOTE: trailing comma at end of value or key-value list is tolerated by this parser)
jbe@121 198 case ',':
jbe@136 199 // change parser state accordingly:
jbe@124 200 if (mode == JSON_STATE_OBJECT_SEPARATOR) {
jbe@124 201 mode = JSON_STATE_OBJECT_KEY;
jbe@124 202 } else if (mode == JSON_STATE_ARRAY_SEPARATOR) {
jbe@124 203 mode = JSON_STATE_ARRAY_VALUE;
jbe@121 204 } else {
jbe@136 205 // if value terminator is not expected here, then return an error:
jbe@136 206 goto json_import_syntax_error;
jbe@121 207 }
jbe@136 208 // consume input character:
jbe@121 209 pos++;
jbe@136 210 // continue with loop:
jbe@121 211 goto json_import_loop;
jbe@136 212 // string literal:
jbe@121 213 case '"':
jbe@136 214 // prepare buffer to decode string (with maximum possible length) and set write position to zero:
jbe@121 215 cbuf = luaL_buffinitsize(L, &luabuf, total-pos);
jbe@121 216 writepos = 0;
jbe@136 217 // consume quote character:
jbe@121 218 pos++;
jbe@136 219 // read next character until encountering end quote:
jbe@121 220 while ((c = str[pos++]) != '"') {
jbe@121 221 if (c == 0) {
jbe@136 222 // handle unexpected end-of-string:
jbe@121 223 goto json_import_unexpected_eof;
jbe@121 224 } else if (c < 32 || c == 127) {
jbe@136 225 // do not allow ASCII control characters:
jbe@136 226 // NOTE: illegal UTF-8 sequences and extended control characters are not sanitized
jbe@136 227 // by this parser to allow different encodings than Unicode
jbe@121 228 lua_pushnil(L);
jbe@121 229 lua_pushliteral(L, "Unexpected control character in JSON string");
jbe@121 230 return 2;
jbe@121 231 } else if (c == '\\') {
jbe@136 232 // read next char after backslash escape:
jbe@121 233 c = str[pos++];
jbe@121 234 switch (c) {
jbe@136 235 // unexpected end-of-string:
jbe@121 236 case 0:
jbe@121 237 goto json_import_unexpected_eof;
jbe@136 238 // unescaping of quotation mark, slash, and backslash:
jbe@121 239 case '"':
jbe@121 240 case '/':
jbe@121 241 case '\\':
jbe@121 242 cbuf[writepos++] = c;
jbe@121 243 break;
jbe@136 244 // unescaping of backspace:
jbe@121 245 case 'b':
jbe@121 246 cbuf[writepos++] = '\b';
jbe@121 247 break;
jbe@136 248 // unescaping of form-feed:
jbe@121 249 case 'f':
jbe@121 250 cbuf[writepos++] = '\f';
jbe@121 251 break;
jbe@136 252 // unescaping of new-line:
jbe@121 253 case 'n':
jbe@121 254 cbuf[writepos++] = '\n';
jbe@121 255 break;
jbe@136 256 // unescaping of carriage-return:
jbe@121 257 case 'r':
jbe@121 258 cbuf[writepos++] = '\r';
jbe@121 259 break;
jbe@136 260 // unescaping of tabulator:
jbe@121 261 case 't':
jbe@121 262 cbuf[writepos++] = '\t';
jbe@121 263 break;
jbe@136 264 // unescaping of UTF-16 characters
jbe@121 265 case 'u':
jbe@121 266 lua_pushnil(L);
jbe@121 267 lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet"); // TODO
jbe@121 268 return 2;
jbe@136 269 // unexpected escape sequence:
jbe@121 270 default:
jbe@121 271 lua_pushnil(L);
jbe@121 272 lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
jbe@121 273 return 2;
jbe@121 274 }
jbe@121 275 } else {
jbe@136 276 // normal character:
jbe@121 277 cbuf[writepos++] = c;
jbe@121 278 }
jbe@121 279 }
jbe@136 280 // process buffer to Lua string:
jbe@121 281 luaL_pushresultsize(&luabuf, writepos);
jbe@136 282 // continue with processing of decoded string:
jbe@121 283 goto json_import_process_value;
jbe@121 284 }
jbe@136 285 // process values whose type is is not deducible from a single character:
jbe@136 286 if ((c >= '0' && c <= '9') || c == '-' || c == '+') {
jbe@136 287 // numbers:
jbe@122 288 char *endptr;
jbe@122 289 double numval;
jbe@122 290 numval = strtod(str+pos, &endptr);
jbe@122 291 if (endptr == str+pos) goto json_import_syntax_error;
jbe@122 292 pos += endptr - (str+pos);
jbe@122 293 lua_pushnumber(L, numval);
jbe@122 294 } else if (!strncmp(str+pos, "true", 4)) {
jbe@136 295 // consume 4 input characters for "true":
jbe@121 296 pos += 4;
jbe@136 297 // put Lua true value on stack:
jbe@136 298 lua_pushboolean(L, 1);
jbe@121 299 } else if (!strncmp(str+pos, "false", 5)) {
jbe@136 300 // consume 5 input characters for "false":
jbe@121 301 pos += 5;
jbe@136 302 // put Lua false value on stack:
jbe@136 303 lua_pushboolean(L, 0);
jbe@121 304 } else if (!strncmp(str+pos, "null", 4)) {
jbe@136 305 // consume 4 input characters for "null":
jbe@136 306 pos += 4;
jbe@136 307 // put special null-marker on stack:
jbe@130 308 lua_pushvalue(L, JSON_UPVAL_NULLMARK);
jbe@121 309 } else {
jbe@136 310 // all other cases are a syntax error:
jbe@121 311 goto json_import_syntax_error;
jbe@121 312 }
jbe@136 313 // process a decoded value or key value pair (expected on top of Lua stack):
jbe@136 314 json_import_process_value:
jbe@121 315 switch (mode) {
jbe@136 316 // an object key has been read:
jbe@124 317 case JSON_STATE_OBJECT_KEY:
jbe@136 318 // if an object key is not a string, then this is a syntax error:
jbe@121 319 if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
jbe@136 320 // expect key terminator and continue with loop:
jbe@124 321 mode = JSON_STATE_OBJECT_KEY_TERMINATOR;
jbe@121 322 goto json_import_loop;
jbe@136 323 // a key value pair has been read:
jbe@124 324 case JSON_STATE_OBJECT_VALUE:
jbe@136 325 // store key value pair in outer shadow table:
jbe@130 326 lua_rawset(L, -3);
jbe@136 327 // expect value terminator (or end of object) and continue with loop:
jbe@124 328 mode = JSON_STATE_OBJECT_SEPARATOR;
jbe@121 329 goto json_import_loop;
jbe@136 330 // an array value has been read:
jbe@124 331 case JSON_STATE_ARRAY_VALUE:
jbe@136 332 // store value in outer shadow table:
jbe@136 333 lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
jbe@136 334 // expect value terminator (or end of object) and continue with loop:
jbe@124 335 mode = JSON_STATE_ARRAY_SEPARATOR;
jbe@121 336 goto json_import_loop;
jbe@136 337 // a single value has been read:
jbe@124 338 case JSON_STATE_VALUE:
jbe@136 339 // leave value on top of stack, expect end of JSON document, and continue with loop:
jbe@124 340 mode = JSON_STATE_END;
jbe@121 341 goto json_import_loop;
jbe@121 342 }
jbe@136 343 // syntax error handling (only reachable by goto statement):
jbe@136 344 json_import_syntax_error:
jbe@121 345 lua_pushnil(L);
jbe@121 346 lua_pushliteral(L, "Syntax error in JSON document");
jbe@121 347 return 2;
jbe@121 348 }
jbe@121 349
jbe@130 350 #define JSON_PATH_GET 1
jbe@130 351 #define JSON_PATH_TYPE 2
jbe@130 352 #define JSON_PATH_ISNULL 3
jbe@130 353
jbe@136 354 // gets a value, its type, or information
jbe@130 355 static int json_path(lua_State *L, int mode) {
jbe@130 356 int argc;
jbe@130 357 int idx = 2;
jbe@130 358 argc = lua_gettop(L);
jbe@126 359 lua_pushvalue(L, 1);
jbe@130 360 while (idx <= argc) {
jbe@132 361 if (lua_isnil(L, -1)) {
jbe@132 362 if (mode == JSON_PATH_ISNULL) lua_pushboolean(L, 0);
jbe@132 363 return 1;
jbe@132 364 }
jbe@130 365 lua_pushvalue(L, -1);
jbe@130 366 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
jbe@126 367 if (lua_isnil(L, -1)) {
jbe@126 368 lua_pop(L, 1);
jbe@130 369 if (lua_type(L, -1) == LUA_TTABLE) {
jbe@130 370 lua_pushvalue(L, idx++);
jbe@130 371 lua_gettable(L, -2);
jbe@130 372 } else {
jbe@130 373 lua_pushnil(L);
jbe@130 374 }
jbe@130 375 } else {
jbe@130 376 lua_replace(L, -2);
jbe@130 377 lua_pushvalue(L, idx++);
jbe@130 378 lua_rawget(L, -2);
jbe@126 379 }
jbe@130 380 lua_replace(L, -2);
jbe@126 381 }
jbe@130 382 switch (mode) {
jbe@130 383 case JSON_PATH_GET:
jbe@130 384 if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L);
jbe@130 385 return 1;
jbe@130 386 case JSON_PATH_TYPE:
jbe@130 387 if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) {
jbe@130 388 lua_pushliteral(L, "null");
jbe@130 389 return 1;
jbe@126 390 }
jbe@130 391 lua_pushvalue(L, -1);
jbe@130 392 lua_rawget(L, JSON_UPVAL_TYPES);
jbe@130 393 if (lua_isnil(L, -1)) lua_pushstring(L, lua_typename(L, lua_type(L, -2)));
jbe@130 394 return 1;
jbe@130 395 case JSON_PATH_ISNULL:
jbe@130 396 lua_pushboolean(L, lua_rawequal(L, -1, JSON_UPVAL_NULLMARK));
jbe@130 397 return 1;
jbe@126 398 }
jbe@130 399 return 0;
jbe@130 400 }
jbe@130 401
jbe@130 402 static int json_get(lua_State *L) {
jbe@130 403 return json_path(L, JSON_PATH_GET);
jbe@130 404 }
jbe@130 405
jbe@130 406 static int json_type(lua_State *L) {
jbe@130 407 return json_path(L, JSON_PATH_TYPE);
jbe@130 408 }
jbe@130 409
jbe@130 410 static int json_isnull(lua_State *L) {
jbe@130 411 return json_path(L, JSON_PATH_ISNULL);
jbe@130 412 }
jbe@130 413
jbe@131 414 static int json_setnull(lua_State *L) {
jbe@131 415 lua_settop(L, 2);
jbe@131 416 lua_pushvalue(L, JSON_UPVAL_METATABLE);
jbe@131 417 lua_setmetatable(L, 1);
jbe@131 418 lua_pushvalue(L, 1);
jbe@131 419 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
jbe@131 420 if (lua_isnil(L, -1)) {
jbe@131 421 lua_newtable(L);
jbe@131 422 lua_pushvalue(L, 1);
jbe@131 423 lua_pushvalue(L, -2);
jbe@131 424 lua_rawset(L, JSON_UPVAL_SHADOWTBL);
jbe@131 425 }
jbe@131 426 lua_pushvalue(L, 2);
jbe@131 427 lua_pushvalue(L, JSON_UPVAL_NULLMARK);
jbe@131 428 lua_rawset(L, -3);
jbe@131 429 return 0;
jbe@131 430 }
jbe@131 431
jbe@130 432 static int json_len(lua_State *L) {
jbe@130 433 lua_settop(L, 1);
jbe@130 434 lua_pushvalue(L, 1);
jbe@130 435 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
jbe@130 436 if (lua_isnil(L, -1)) lua_pop(L, 1);
jbe@130 437 lua_pushinteger(L, lua_rawlen(L, -1));
jbe@123 438 return 1;
jbe@123 439 }
jbe@123 440
jbe@130 441 static int json_index(lua_State *L) {
jbe@130 442 lua_settop(L, 2);
jbe@130 443 lua_pushvalue(L, 1);
jbe@130 444 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
jbe@130 445 if (lua_isnil(L, -1)) return 1;
jbe@130 446 lua_pushvalue(L, 2);
jbe@130 447 lua_rawget(L, -2);
jbe@130 448 if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L);
jbe@127 449 return 1;
jbe@127 450 }
jbe@127 451
jbe@130 452 static int json_newindex(lua_State *L) {
jbe@130 453 lua_settop(L, 3);
jbe@123 454 lua_pushvalue(L, 1);
jbe@130 455 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
jbe@130 456 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
jbe@130 457 lua_replace(L, 1);
jbe@130 458 lua_rawset(L, 1);
jbe@121 459 return 1;
jbe@121 460 }
jbe@121 461
jbe@135 462 static int json_pairs_iterfunc(lua_State *L) {
jbe@135 463 lua_settop(L, 2);
jbe@135 464 lua_pushvalue(L, 1);
jbe@135 465 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
jbe@135 466 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
jbe@135 467 lua_pushvalue(L, 2);
jbe@135 468 if (!lua_next(L, -2)) return 0;
jbe@135 469 if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) {
jbe@135 470 lua_pop(L, 1);
jbe@135 471 lua_pushnil(L);
jbe@135 472 }
jbe@135 473 return 2;
jbe@135 474 }
jbe@135 475
jbe@135 476 static int json_pairs(lua_State *L) {
jbe@135 477 lua_pushvalue(L, JSON_UPVAL_PAIRS_ITERFUNC);
jbe@135 478 lua_pushvalue(L, 1);
jbe@135 479 lua_pushnil(L);
jbe@135 480 return 3;
jbe@135 481 }
jbe@135 482
jbe@134 483 static int json_ipairs_iterfunc(lua_State *L) {
jbe@134 484 int idx;
jbe@134 485 lua_settop(L, 2);
jbe@134 486 idx = lua_tointeger(L, 2) + 1;
jbe@134 487 lua_pushvalue(L, 1);
jbe@134 488 lua_rawget(L, JSON_UPVAL_SHADOWTBL);
jbe@134 489 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
jbe@134 490 lua_rawgeti(L, -1, idx);
jbe@134 491 if (lua_isnil(L, -1)) return 0;
jbe@134 492 lua_pushinteger(L, idx);
jbe@134 493 if (lua_rawequal(L, -2, JSON_UPVAL_NULLMARK)) lua_pushnil(L);
jbe@134 494 else lua_pushvalue(L, -2);
jbe@134 495 return 2;
jbe@134 496 }
jbe@134 497
jbe@134 498 static int json_ipairs(lua_State *L) {
jbe@134 499 lua_pushvalue(L, JSON_UPVAL_IPAIRS_ITERFUNC);
jbe@134 500 lua_pushvalue(L, 1);
jbe@134 501 lua_pushinteger(L, 0);
jbe@134 502 return 3;
jbe@134 503 }
jbe@134 504
jbe@121 505 static const struct luaL_Reg json_module_functions[] = {
jbe@133 506 {"object", json_object},
jbe@133 507 {"array", json_array},
jbe@121 508 {"import", json_import},
jbe@130 509 {"get", json_get},
jbe@127 510 {"type", json_type},
jbe@123 511 {"isnull", json_isnull},
jbe@131 512 {"setnull", json_setnull},
jbe@121 513 {NULL, NULL}
jbe@121 514 };
jbe@121 515
jbe@126 516 static const struct luaL_Reg json_metatable_functions[] = {
jbe@130 517 {"__len", json_len},
jbe@130 518 {"__index", json_index},
jbe@130 519 {"__newindex", json_newindex},
jbe@135 520 {"__pairs", json_pairs},
jbe@134 521 {"__ipairs", json_ipairs},
jbe@126 522 {NULL, NULL}
jbe@126 523 };
jbe@126 524
jbe@121 525 int luaopen_json(lua_State *L) {
jbe@126 526 lua_settop(L, 0);
jbe@126 527 lua_newtable(L); // 1: library table on stack position
jbe@130 528 lua_newtable(L); // 2: table used as JSON NULL value in internal shadow tables
jbe@130 529 lua_newtable(L); // 3: ephemeron table to store shadow tables for each JSON object/array to allow NULL values returned as nil
jbe@130 530 lua_newtable(L); // 4: ephemeron table to store the type of the JSON object/array
jbe@130 531 lua_newtable(L); // 5: metatable for ephemeron tables
jbe@121 532 lua_pushliteral(L, "__mode");
jbe@121 533 lua_pushliteral(L, "k");
jbe@130 534 lua_rawset(L, 5);
jbe@130 535 lua_pushvalue(L, 5); // 6: cloned metatable reference
jbe@127 536 lua_setmetatable(L, 3);
jbe@130 537 lua_setmetatable(L, 4);
jbe@130 538 lua_newtable(L); // 5: metatable for JSON objects and JSON arrays
jbe@126 539 lua_pushvalue(L, 2);
jbe@126 540 lua_pushvalue(L, 3);
jbe@127 541 lua_pushvalue(L, 4);
jbe@130 542 lua_pushvalue(L, 5);
jbe@135 543 lua_pushcclosure(L, json_pairs_iterfunc, 4); // 6: iteration function for pairs
jbe@135 544 lua_pushvalue(L, 2);
jbe@135 545 lua_pushvalue(L, 3);
jbe@135 546 lua_pushvalue(L, 4);
jbe@135 547 lua_pushvalue(L, 5);
jbe@135 548 lua_pushcclosure(L, json_ipairs_iterfunc, 4); // 7: iteration function for ipairs
jbe@134 549 lua_pushvalue(L, 5);
jbe@134 550 lua_pushvalue(L, 2);
jbe@134 551 lua_pushvalue(L, 3);
jbe@134 552 lua_pushvalue(L, 4);
jbe@134 553 lua_pushvalue(L, 5);
jbe@134 554 lua_pushvalue(L, 6);
jbe@135 555 lua_pushvalue(L, 7);
jbe@135 556 luaL_setfuncs(L, json_metatable_functions, 6);
jbe@135 557 lua_setfield(L, 1, "metatable");
jbe@135 558 luaL_setfuncs(L, json_module_functions, 6);
jbe@121 559 return 1;
jbe@121 560 }

Impressum / About Us