webmcp
view libraries/json/json.c @ 148:5354d836e6fb
Minor bugfix in json_newindex function of JSON library (should return nothing)
| author | jbe | 
|---|---|
| date | Wed Jul 30 22:32:33 2014 +0200 (2014-07-30) | 
| parents | da5ab2c226dc | 
| children | 5229687c7601 | 
 line source
     1 #include <lua.h>
     2 #include <lauxlib.h>
     3 #include <stdlib.h>
     4 #include <string.h>
     6 // maximum number of nested JSON values (objects and arrays):
     7 #define JSON_MAXDEPTH 100
     9 // macros for usage of Lua registry:
    10 #define JSON_REGENT char
    11 #define JSON_REGPOINTER void *
    12 #define json_pushlightref(L, x) (lua_pushlightuserdata((L), &json_reference.x))
    13 #define json_regpointer(x) (&json_registry.x)
    14 #define json_regfetchpointer(L, x) (lua_pushlightuserdata((L), (x)), lua_rawget((L), LUA_REGISTRYINDEX))
    15 #define json_regfetch(L, x) (json_regfetchpointer(L, json_regpointer(x)))
    16 #define json_regstore(L, x) (lua_pushlightuserdata(L, json_regpointer(x)), lua_pushvalue(L, -2), lua_rawset(L, LUA_REGISTRYINDEX));
    18 // generate dummy memory addresses that represent non-modifiable lightuserdata (dummy) objects:
    19 static struct {
    20   JSON_REGENT nullmark;  // magic value to indicate JSON null value in shadow table
    21 } json_reference;
    24 // generate dummy memory addresses that represent Lua objects
    25 // via lightuserdata keys and LUA_REGISTRYINDEX:
    26 static struct {
    27   JSON_REGENT shadowtbl;  // ephemeron table that maps tables to their corresponding shadow table
    28   JSON_REGENT unknownmt;  // metatable for tables that may be either JSON objects or JSON arrays
    29   JSON_REGENT objectmt;   // metatable for JSON objects
    30   JSON_REGENT arraymt;    // metatable for JSON arrays
    31 } json_registry;
    33 // marks a Lua table as JSON object or JSON array:
    34 // (returns its modified argument or a new table if argument is nil)
    35 static int json_mark(lua_State *L, JSON_REGPOINTER mt) {
    36   // check if argument is nil
    37   if (lua_isnoneornil(L, 1)) {
    38     // create new table at stack position 1:
    39     lua_settop(L, 0);
    40     lua_newtable(L);
    41     // create shadow table (leaving previously created table on stack position 1):
    42     json_regfetch(L, shadowtbl);
    43     lua_pushvalue(L, 1);
    44     lua_newtable(L);
    45     lua_rawset(L, -3);
    46   } else {
    47     // push shadow table on top of stack:
    48     json_regfetch(L, shadowtbl);
    49     lua_pushvalue(L, 1);
    50     lua_rawget(L, -2);
    51     // if shadow table does not exist:
    52     if (lua_isnil(L, -1)) {
    53       // create shadow table and leave it on top of stack:
    54       lua_newtable(L);
    55       lua_pushvalue(L, 1);
    56       lua_pushvalue(L, -2);
    57       lua_rawset(L, -5);
    58     }
    59     // move elements from original table to shadow table (that's expected on top of stack):
    60     for(lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
    61       lua_pushvalue(L, -2);
    62       lua_pushnil(L);
    63       lua_rawset(L, 1);
    64       lua_pushvalue(L, -2);
    65       lua_pushvalue(L, -2);
    66       lua_rawset(L, -5);
    67     }
    68   }
    69   // discard everything but table to return:
    70   lua_settop(L, 1);
    71   // set metatable:
    72   json_regfetchpointer(L, mt);
    73   lua_setmetatable(L, 1);
    74   // return table:
    75   return 1;
    76 }
    78 // marks a table as JSON object:
    79 // (returns its modified argument or a new table if argument is nil)
    80 static int json_object(lua_State *L) {
    81   return json_mark(L, json_regpointer(objectmt));
    82 }
    84 // marks a table as JSON array:
    85 // (returns its modified argument or a new table if argument is nil)
    86 static int json_array(lua_State *L) {
    87   return json_mark(L, json_regpointer(arraymt));
    88 }
    90 // internal states of JSON parser:
    91 #define JSON_STATE_VALUE 0
    92 #define JSON_STATE_OBJECT_KEY 1
    93 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2
    94 #define JSON_STATE_OBJECT_VALUE 3
    95 #define JSON_STATE_OBJECT_SEPARATOR 4
    96 #define JSON_STATE_ARRAY_VALUE 5
    97 #define JSON_STATE_ARRAY_SEPARATOR 6
    98 #define JSON_STATE_END 7
   100 // special Lua stack indicies for json_import function:
   101 #define json_import_objectmt_idx 2
   102 #define json_import_arraymt_idx 3
   103 #define json_import_shadowtbl_idx 4
   104 #define json_import_nullmark_idx 5
   106 // decodes a JSON document:
   107 static int json_import(lua_State *L) {
   108   const char *str;   // string to parse
   109   size_t total;      // total length of string to parse
   110   size_t pos = 0;    // current position in string to parse
   111   size_t level = 0;  // nested levels of objects/arrays currently being processed
   112   int mode = JSON_STATE_VALUE;  // state of parser (i.e. "what's expected next?")
   113   char c;              // variable to store a single character to be processed
   114   luaL_Buffer luabuf;  // Lua buffer to decode JSON string values
   115   char *cbuf;          // C buffer to decode JSON string values
   116   size_t writepos;     // write position of decoded strings in C buffer
   117   // stack shall contain one function argument:
   118   lua_settop(L, 1);
   119   // push objectmt onto stack position 2:
   120   json_regfetch(L, objectmt);
   121   // push arraymt onto stack position 3:
   122   json_regfetch(L, arraymt);
   123   // push shadowtbl onto stack position 4:
   124   json_regfetch(L, shadowtbl);
   125   // push nullmark onto stack position 5:
   126   json_pushlightref(L, nullmark);
   127   // require string as first argument:
   128   str = luaL_checklstring(L, 1, &total);
   129   // if string contains a NULL byte, this is a syntax error
   130   if (strlen(str) != total) goto json_import_syntax_error;
   131   // main loop of parser:
   132   json_import_loop:
   133   // skip whitespace and store next character in variable 'c':
   134   while (c = str[pos],
   135     c == ' ' ||
   136     c == '\f' ||
   137     c == '\n' ||
   138     c == '\r' ||
   139     c == '\t' ||
   140     c == '\v'
   141   ) pos++;
   142   // switch statement to handle certain (single) characters:
   143   switch (c) {
   144   // handle end of JSON document:
   145   case 0:
   146     // if end of JSON document was expected, then return top element of stack as result:
   147     if (mode == JSON_STATE_END) return 1;
   148     // otherwise, the JSON document was malformed:
   149     json_import_unexpected_eof:
   150     lua_pushnil(L);
   151     if (level == 0) lua_pushliteral(L, "Empty string");
   152     else lua_pushliteral(L, "Unexpected end of JSON document");
   153     return 2;
   154   // new JSON object:
   155   case '{':
   156     // if a JSON object is not expected here, then return an error:
   157     if (
   158       mode != JSON_STATE_VALUE &&
   159       mode != JSON_STATE_OBJECT_VALUE &&
   160       mode != JSON_STATE_ARRAY_VALUE
   161     ) goto json_import_syntax_error;
   162     // create JSON object on stack:
   163     lua_newtable(L);
   164     // set metatable of JSON object:
   165     lua_pushvalue(L, json_import_objectmt_idx);
   166     lua_setmetatable(L, -2);
   167     // create internal shadow table on stack:
   168     lua_newtable(L);
   169     // register internal shadow table:
   170     lua_pushvalue(L, -2);
   171     lua_pushvalue(L, -2);
   172     lua_rawset(L, json_import_shadowtbl_idx);
   173     // expect object key (or end of object) to follow:
   174     mode = JSON_STATE_OBJECT_KEY;
   175     // jump to common code for opening JSON object and JSON array:
   176     goto json_import_open;
   177   // new JSON array:
   178   case '[':
   179     // if a JSON array is not expected here, then return an error:
   180     if (
   181       mode != JSON_STATE_VALUE &&
   182       mode != JSON_STATE_OBJECT_VALUE &&
   183       mode != JSON_STATE_ARRAY_VALUE
   184     ) goto json_import_syntax_error;
   185     // create JSON array on stack:
   186     lua_newtable(L);
   187     // set metatable of JSON array:
   188     lua_pushvalue(L, json_import_arraymt_idx);
   189     lua_setmetatable(L, -2);
   190     // create internal shadow table on stack:
   191     lua_newtable(L);
   192     // register internal shadow table:
   193     lua_pushvalue(L, -2);
   194     lua_pushvalue(L, -2);
   195     lua_rawset(L, json_import_shadowtbl_idx);
   196     // add nil as key (needed to keep stack balance) and as magic to detect arrays:
   197     lua_pushnil(L);
   198     // expect array value (or end of array) to follow:
   199     mode = JSON_STATE_ARRAY_VALUE;
   200     // continue with common code for opening JSON object and JSON array:
   201   // common code for opening JSON object or JSON array:
   202   json_import_open:
   203     // limit nested levels:
   204     if (level >= JSON_MAXDEPTH) {
   205       lua_pushnil(L);
   206       lua_pushliteral(L, "Too many nested JSON levels");
   207       return 2;
   208     }
   209     // additional buffer overflow protection:
   210     if (!lua_checkstack(L, LUA_MINSTACK))
   211       return luaL_error(L, "Caught stack overflow in JSON import function (too many nested levels and stack size too small)");
   212     // increment level:
   213     level++;
   214     // consume input character:
   215     pos++;
   216     goto json_import_loop;
   217   // end of JSON object:
   218   case '}':
   219     // if end of JSON object is not expected here, then return an error:
   220     if (
   221       mode != JSON_STATE_OBJECT_KEY &&
   222       mode != JSON_STATE_OBJECT_SEPARATOR
   223     ) goto json_import_syntax_error;
   224     // jump to common code for end of JSON object and JSON array:
   225     goto json_import_close;
   226   // end of JSON array:
   227   case ']':
   228     // if end of JSON array is not expected here, then return an error:
   229     if (
   230       mode != JSON_STATE_ARRAY_VALUE &&
   231       mode != JSON_STATE_ARRAY_SEPARATOR
   232     ) goto json_import_syntax_error;
   233     // pop nil key/magic (that was needed to keep stack balance):
   234     lua_pop(L, 1);
   235     // continue with common code for end of JSON object and JSON array:
   236   // common code for end of JSON object or JSON array:
   237   json_import_close:
   238     // consume input character:
   239     pos++;
   240     // pop shadow table:
   241     lua_pop(L, 1);
   242     // check if nested:
   243     if (--level) {
   244       // if nested,
   245       // check if outer(!) structure is an array or object:
   246       if (lua_isnil(L, -2)) {
   247         // select array value processing:
   248         mode = JSON_STATE_ARRAY_VALUE;
   249       } else {
   250         // select object value processing:
   251         mode = JSON_STATE_OBJECT_VALUE;
   252       }
   253       // store value in outer structure:
   254       goto json_import_process_value;
   255     }
   256     // if not nested, then expect end of JSON document and continue with loop:
   257     mode = JSON_STATE_END;
   258     goto json_import_loop;
   259   // key terminator:
   260   case ':':
   261     // if key terminator is not expected here, then return an error:
   262     if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR)
   263       goto json_import_syntax_error;
   264     // consume input character:
   265     pos++;
   266     // expect object value to follow:
   267     mode = JSON_STATE_OBJECT_VALUE;
   268     // continue with loop:
   269     goto json_import_loop;
   270   // value terminator (NOTE: trailing comma at end of value or key-value list is tolerated by this parser)
   271   case ',':
   272     // branch according to parser state:
   273     if (mode == JSON_STATE_OBJECT_SEPARATOR) {
   274       // expect an object key to follow:
   275       mode = JSON_STATE_OBJECT_KEY;
   276     } else if (mode == JSON_STATE_ARRAY_SEPARATOR) {
   277       // expect an array value to follow:
   278       mode = JSON_STATE_ARRAY_VALUE;
   279     } else {
   280        // if value terminator is not expected here, then return an error:
   281        goto json_import_syntax_error;
   282     }
   283     // consume input character:
   284     pos++;
   285     // continue with loop:
   286     goto json_import_loop;
   287   // string literal:
   288   case '"':
   289     // consume quote character:
   290     pos++;
   291     // prepare buffer to decode string (with maximum possible length) and set write position to zero:
   292     cbuf = luaL_buffinitsize(L, &luabuf, total-pos);
   293     writepos = 0;
   294     // loop through the characters until encountering end quote:
   295     while ((c = str[pos++]) != '"') {
   296       if (c == 0) {
   297         // handle unexpected end of JSON document:
   298         goto json_import_unexpected_eof;
   299       } else if (c < 32 || c == 127) {
   300         // do not allow ASCII control characters:
   301         // NOTE: illegal UTF-8 sequences and extended control characters are not sanitized
   302         //       by this parser to allow different encodings than Unicode
   303         lua_pushnil(L);
   304         lua_pushliteral(L, "Unexpected control character in JSON string");
   305         return 2;
   306       } else if (c == '\\') {
   307         // read next char after backslash escape:
   308         c = str[pos++];
   309         switch (c) {
   310         // unexpected end-of-string:
   311         case 0:
   312           goto json_import_unexpected_eof;
   313         // unescaping of quotation mark, slash, and backslash:
   314         case '"':
   315         case '/':
   316         case '\\':
   317           cbuf[writepos++] = c;
   318           break;
   319         // unescaping of backspace:
   320         case 'b': cbuf[writepos++] = '\b'; break;
   321         // unescaping of form-feed:
   322         case 'f': cbuf[writepos++] = '\f'; break;
   323         // unescaping of new-line:
   324         case 'n': cbuf[writepos++] = '\n'; break;
   325         // unescaping of carriage-return:
   326         case 'r': cbuf[writepos++] = '\r'; break;
   327         // unescaping of tabulator:
   328         case 't': cbuf[writepos++] = '\t'; break;
   329         // unescaping of UTF-16 characters
   330         case 'u':
   331           lua_pushnil(L);
   332           lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet");  // TODO
   333           return 2;
   334         // unexpected escape sequence:
   335         default:
   336           lua_pushnil(L);
   337           lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
   338           return 2;
   339         }
   340       } else {
   341         // normal character:
   342         cbuf[writepos++] = c;
   343       }
   344     }
   345     // process buffer to Lua string:
   346     luaL_pushresultsize(&luabuf, writepos);
   347     // continue with processing of decoded string:
   348     goto json_import_process_value;
   349   }
   350   // process values whose type is is not deducible from a single character:
   351   if ((c >= '0' && c <= '9') || c == '-' || c == '+') {
   352     // for numbers,
   353     // use strtod() call to parse a (double precision) floating point number:
   354     char *endptr;
   355     double numval;
   356     numval = strtod(str+pos, &endptr);
   357     // catch parsing errors:
   358     if (endptr == str+pos) goto json_import_syntax_error;
   359     // consume characters that were parsed:
   360     pos += endptr - (str+pos);
   361     // push parsed (double precision) floating point number on Lua stack:
   362     lua_pushnumber(L, numval);
   363   } else if (!strncmp(str+pos, "true", 4)) {
   364     // consume 4 input characters for "true":
   365     pos += 4;
   366     // put Lua true value onto stack:
   367     lua_pushboolean(L, 1);
   368   } else if (!strncmp(str+pos, "false", 5)) {
   369     // consume 5 input characters for "false":
   370     pos += 5;
   371     // put Lua false value onto stack:
   372     lua_pushboolean(L, 0);
   373   } else if (!strncmp(str+pos, "null", 4)) {
   374     // consume 4 input characters for "null":
   375     pos += 4;
   376     // put special null-marker onto stack:
   377     lua_pushvalue(L, json_import_nullmark_idx);
   378   } else {
   379     // all other cases are a syntax error:
   380     goto json_import_syntax_error;
   381   }
   382   // process a decoded value or key value pair (expected on top of Lua stack):
   383   json_import_process_value:
   384   switch (mode) {
   385   // an object key has been read:
   386   case JSON_STATE_OBJECT_KEY:
   387     // if an object key is not a string, then this is a syntax error:
   388     if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
   389     // expect key terminator to follow:
   390     mode = JSON_STATE_OBJECT_KEY_TERMINATOR;
   391     // continue with loop:
   392     goto json_import_loop;
   393   // a key value pair has been read:
   394   case JSON_STATE_OBJECT_VALUE:
   395     // store key value pair in outer shadow table:
   396     lua_rawset(L, -3);
   397     // expect value terminator (or end of object) to follow:
   398     mode = JSON_STATE_OBJECT_SEPARATOR;
   399     // continue with loop:
   400     goto json_import_loop;
   401   // an array value has been read:
   402   case JSON_STATE_ARRAY_VALUE:
   403     // store value in outer shadow table:
   404     lua_rawseti(L, -3, lua_rawlen(L, -3) + 1);
   405     // expect value terminator (or end of object) to follow:
   406     mode = JSON_STATE_ARRAY_SEPARATOR;
   407     // continue with loop
   408     goto json_import_loop;
   409   // a single value has been read:
   410   case JSON_STATE_VALUE:
   411     // leave value on top of stack, expect end of JSON document, and continue with loop:
   412     mode = JSON_STATE_END;
   413     goto json_import_loop;
   414   }
   415   // syntax error handling (reachable by goto statement):
   416   json_import_syntax_error:
   417   lua_pushnil(L);
   418   lua_pushliteral(L, "Syntax error in JSON document");
   419   return 2;
   420 }
   422 // special Lua stack indicies for json_path function:
   423 #define json_path_shadowtbl_idx 1
   424 #define json_path_nullmark_idx 2
   426 // stack offset of arguments to json_path function:
   427 #define json_path_idxshift 2
   429 // gets a value or its type from a JSON document (passed as first argument)
   430 // using a path (passed as variable number of keys after first argument):
   431 static int json_path(lua_State *L, int type_mode) {
   432   int stacktop;                      // stack index of top of stack (after shifting)
   433   int idx = 2 + json_path_idxshift;  // stack index of current argument to process
   434   // insert shadowtbl into stack at position 1 (shifting the arguments):
   435   json_regfetch(L, shadowtbl);
   436   lua_insert(L, 1);
   437   // insert nullmark into stack at position 2 (shifting the arguments):
   438   json_pushlightref(L, nullmark);
   439   lua_insert(L, 2);
   440   // store stack index of top of stack:
   441   stacktop = lua_gettop(L);
   442   // use first argument as "current value" (stored on top of stack):
   443   lua_pushvalue(L, 1 + json_path_idxshift);
   444   // process each "path key" (2nd argument and following arguments):
   445   while (idx <= stacktop) {
   446     // if "current value" (on top of stack) is nil, then the path cannot be walked and nil is returned:
   447     if (lua_isnil(L, -1)) return 1;
   448     // try to get shadow table of "current value":
   449     lua_pushvalue(L, -1);
   450     lua_rawget(L, json_path_shadowtbl_idx);
   451     if (lua_isnil(L, -1)) {
   452       // if no shadow table is found,
   453       if (lua_type(L, -1) == LUA_TTABLE) {
   454         // and if "current value" is a table,
   455         // drop nil from stack:
   456         lua_pop(L, 1);
   457         // get "next value" using the "path key":
   458         lua_pushvalue(L, idx++);
   459         lua_gettable(L, -2);
   460       } else {
   461         // if "current value" is not a table,
   462         // then the path cannot be walked and nil (already on top of stack) is returned:
   463         return 1;
   464       }
   465     } else {
   466       // if a shadow table is found,
   467       // set "current value" to its shadow table:
   468       lua_replace(L, -2);
   469       // get "next value" using the "path key":
   470       lua_pushvalue(L, idx++);
   471       lua_rawget(L, -2);
   472     }
   473     // the "next value" replaces the "current value":
   474     lua_replace(L, -2);
   475   }
   476   if (!type_mode) {
   477     // if a value (and not its type) was requested,
   478     // check if value is the null-marker, and store nil on top of Lua stack in that case:
   479     if (lua_rawequal(L, -1, json_path_nullmark_idx)) lua_pushnil(L);
   480   } else {
   481     // if the type was requested,
   482     // check if value is the null-marker:
   483     if (lua_rawequal(L, -1, json_path_nullmark_idx)) {
   484       // if yes, store string "null" on top of Lua stack:
   485       lua_pushliteral(L, "null");
   486     } else {
   487       // otherwise,
   488       // check if metatable indicates "object" or "array":
   489       if (lua_getmetatable(L, -1)) {
   490         json_regfetch(L, objectmt);
   491         if (lua_rawequal(L, -2, -1)) {
   492           // if value has metatable for JSON objects,
   493           // return string "object":
   494           lua_pushliteral(L, "object");
   495           return 1;
   496         }
   497         json_regfetch(L, arraymt);
   498         if (lua_rawequal(L, -3, -1)) {
   499           // if value has metatable for JSON arrays,
   500           // return string "object":
   501           lua_pushliteral(L, "array");
   502           return 1;
   503         }
   504         // remove 3 metatables (one of the value, two for comparison) from stack:
   505         lua_pop(L, 3);
   506       }
   507       // otherwise, get the Lua type:
   508       lua_pushstring(L, lua_typename(L, lua_type(L, -1)));
   509     }
   510   }
   511   // return the top most value on the Lua stack:
   512   return 1;
   513 }
   515 // gets a value from a JSON document (passed as first argument)
   516 // using a path (passed as variable number of keys after first argument):
   517 static int json_get(lua_State *L) {
   518   return json_path(L, 0);
   519 }
   521 // gets a value's type from a JSON document (passed as first argument)
   522 // using a path (variable number of keys after first argument):
   523 static int json_type(lua_State *L) {
   524   return json_path(L, 1);
   525 }
   527 // checks if a value in a JSON document (first argument) is
   528 // explicitly set to null:
   529 static int json_isnull(lua_State *L) {
   530   const char *jsontype;
   531   // call json_type function with variable arguments:
   532   lua_pushcfunction(L, json_type);
   533   lua_insert(L, 1);
   534   lua_call(L, lua_gettop(L) - 1, 1);
   535   // return true if result equals to string "null", otherwise return false:
   536   jsontype = lua_tostring(L, -1);
   537   if (jsontype && !strcmp(jsontype, "null")) lua_pushboolean(L, 1);
   538   else lua_pushboolean(L, 0);
   539   return 1;
   540 }
   542 // special Lua stack indicies for json_setnull function:
   543 #define json_setnull_unknownmt_idx 3
   544 #define json_setnull_objectmt_idx 4
   545 #define json_setnull_arraymt_idx 5
   546 #define json_setnull_shadowtbl_idx 6
   548 // sets a value in a JSON object or JSON array explicitly to null:
   549 // NOTE: JSON null is different than absence of a key
   550 static int json_setnull(lua_State *L) {
   551   // stack shall contain two function arguments:
   552   lua_settop(L, 2);
   553   // push unknownmt onto stack position 3:
   554   json_regfetch(L, unknownmt);
   555   // push objectmt onto stack position 4:
   556   json_regfetch(L, objectmt);
   557   // push arraymt onto stack position 5:
   558   json_regfetch(L, arraymt);
   559   // push shadowtbl onto stack position 6:
   560   json_regfetch(L, shadowtbl);
   561   // set metatable if necessary (leaves unknown number of elements on stack):
   562   if (
   563     !lua_getmetatable(L, 1) || (
   564       !lua_rawequal(L, -1, json_setnull_unknownmt_idx) &&
   565       !lua_rawequal(L, -1, json_setnull_objectmt_idx) &&
   566       !lua_rawequal(L, -1, json_setnull_arraymt_idx)
   567     )
   568   ) {
   569     lua_pushvalue(L, json_setnull_unknownmt_idx);
   570     lua_setmetatable(L, 1);
   571   }
   572   // try to get shadow table:
   573   lua_pushvalue(L, 1);
   574   lua_rawget(L, json_setnull_shadowtbl_idx);
   575   if (lua_isnil(L, -1)) {
   576     // if no shadow table is found,
   577     // create new shadow table (and leave it on top of stack):
   578     lua_newtable(L);
   579     // register shadow table:
   580     lua_pushvalue(L, 1);
   581     lua_pushvalue(L, -2);
   582     lua_rawset(L, json_setnull_shadowtbl_idx);
   583   }
   584   // push key (second argument) and null-marker after shadow table onto stack:
   585   lua_pushvalue(L, 2);
   586   json_pushlightref(L, nullmark);
   587   // store key and null-marker in shadow table:
   588   lua_rawset(L, -3);
   589   // return nothing:
   590   return 0;
   591 }
   593 // returns the length of a JSON array (or zero for a table without numeric keys):
   594 static int json_len(lua_State *L) {
   595   // stack shall contain one function argument:
   596   lua_settop(L, 1);
   597   // try to get corresponding shadow table for first argument:
   598   json_regfetch(L, shadowtbl);
   599   lua_pushvalue(L, 1);
   600   lua_rawget(L, -2);
   601   // if shadow table does not exist, return length of argument, else length of shadow table:
   602   lua_pushnumber(L, lua_rawlen(L, lua_isnil(L, -1) ? 1 : -1));
   603   return 1;
   604 }
   606 // special Lua stack indicies for json_index function:
   607 #define json_index_nullmark_idx 3
   608 #define json_index_shadowtbl_idx 4
   610 static int json_index(lua_State *L) {
   611   // stack shall contain two function arguments:
   612   lua_settop(L, 2);
   613   // push nullmark onto stack position 3:
   614   json_pushlightref(L, nullmark);
   615   // push shadowtbl onto stack position 4:
   616   json_regfetch(L, shadowtbl);
   617   // get corresponding shadow table for first argument:
   618   lua_pushvalue(L, 1);
   619   lua_rawget(L, json_index_shadowtbl_idx);
   620   // throw error if no shadow table was found:
   621   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
   622   // use key passed as second argument to lookup value in shadow table:
   623   lua_pushvalue(L, 2);
   624   lua_rawget(L, -2);
   625   // if value is null-marker, then push nil onto stack:
   626   if (lua_rawequal(L, -1, json_index_nullmark_idx)) lua_pushnil(L);
   627   // return either looked up value, or nil
   628   return 1;
   629 }
   631 static int json_newindex(lua_State *L) {
   632   // stack shall contain three function arguments:
   633   lua_settop(L, 3);
   634   // get corresponding shadow table for first argument:
   635   json_regfetch(L, shadowtbl);
   636   lua_pushvalue(L, 1);
   637   lua_rawget(L, -2);
   638   // throw error if no shadow table was found:
   639   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
   640   // replace first argument with shadow table:
   641   lua_replace(L, 1);
   642   // reset stack and use second and third argument to write to shadow table:
   643   lua_settop(L, 3);
   644   lua_rawset(L, 1);
   645   // return nothing:
   646   return 0;
   647 }
   649 // special Lua stack indicies for json_pairs_iterfunc function:
   650 #define json_pairs_iterfunc_nullmark_idx 3
   651 #define json_pairs_iterfunc_shadowtbl_idx 4
   653 static int json_pairs_iterfunc(lua_State *L) {
   654   lua_settop(L, 2);
   655   json_pushlightref(L, nullmark);  // on stack position 3
   656   json_regfetch(L, shadowtbl);
   657   lua_pushvalue(L, 1);
   658   lua_rawget(L, json_pairs_iterfunc_shadowtbl_idx);
   659   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
   660   lua_pushvalue(L, 2);
   661   if (!lua_next(L, -2)) return 0;
   662   if (lua_rawequal(L, -1, json_pairs_iterfunc_nullmark_idx)) {
   663     lua_pop(L, 1);
   664     lua_pushnil(L);
   665   }
   666   return 2;
   667 }
   669 static int json_pairs(lua_State *L) {
   670   lua_pushcfunction(L, json_pairs_iterfunc);
   671   lua_pushvalue(L, 1);
   672   lua_pushnil(L);
   673   return 3;
   674 }
   676 // special Lua stack indicies for json_ipairs_iterfunc function:
   677 #define json_ipairs_iterfunc_nullmark_idx 3
   678 #define json_ipairs_iterfunc_shadowtbl_idx 4
   680 static int json_ipairs_iterfunc(lua_State *L) {
   681   int idx;
   682   lua_settop(L, 2);
   683   json_pushlightref(L, nullmark);  // on stack position 3
   684   json_regfetch(L, shadowtbl);
   685   idx = lua_tointeger(L, 2) + 1;
   686   lua_pushvalue(L, 1);
   687   lua_rawget(L, json_ipairs_iterfunc_shadowtbl_idx);
   688   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
   689   lua_rawgeti(L, -1, idx);
   690   if (lua_isnil(L, -1)) return 0;
   691   lua_pushinteger(L, idx);
   692   if (lua_rawequal(L, -2, json_ipairs_iterfunc_nullmark_idx)) lua_pushnil(L);
   693   else lua_pushvalue(L, -2);
   694   return 2;
   695 }
   697 static int json_ipairs(lua_State *L) {
   698   lua_pushcfunction(L, json_ipairs_iterfunc);
   699   lua_pushvalue(L, 1);
   700   lua_pushinteger(L, 0);
   701   return 3;
   702 }
   704 static const struct luaL_Reg json_module_functions[] = {
   705   {"object", json_object},
   706   {"array", json_array},
   707   {"import", json_import},
   708   {"get", json_get},
   709   {"type", json_type},
   710   {"isnull", json_isnull},
   711   {"setnull", json_setnull},
   712   {NULL, NULL}
   713 };
   715 static const struct luaL_Reg json_metatable_functions[] = {
   716   {"__len", json_len},
   717   {"__index", json_index},
   718   {"__newindex", json_newindex},
   719   {"__pairs", json_pairs},
   720   {"__ipairs", json_ipairs},
   721   {NULL, NULL}
   722 };
   724 int luaopen_json(lua_State *L) {
   725   lua_settop(L, 0);
   726   lua_newtable(L);  // library
   727   lua_newtable(L);
   728   luaL_setfuncs(L, json_metatable_functions, 0);
   729   json_regstore(L, unknownmt);
   730   lua_setfield(L, 1, "ambiguous_mt");
   731   lua_newtable(L);
   732   luaL_setfuncs(L, json_metatable_functions, 0);
   733   json_regstore(L, objectmt);
   734   lua_setfield(L, 1, "object_mt");
   735   lua_newtable(L);
   736   luaL_setfuncs(L, json_metatable_functions, 0);
   737   json_regstore(L, arraymt);
   738   lua_setfield(L, 1, "array_mt");
   739   lua_newtable(L);  // ephemeron table to store shadow tables for each JSON object/array to allow NULL values returned as nil
   740   lua_newtable(L);  // metatable for ephemeron table
   741   lua_pushliteral(L, "__mode");
   742   lua_pushliteral(L, "k");
   743   lua_rawset(L, -3);
   744   lua_setmetatable(L, -2);
   745   json_regstore(L, shadowtbl);
   746   lua_settop(L, 1);
   747   luaL_setfuncs(L, json_module_functions, 0);
   748   return 1;
   749 }
