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