webmcp

annotate libraries/json/json.c @ 137:f490b78827d6

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

Impressum / About Us