| 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@154
 | 
     5 #include <math.h>
 | 
| 
jbe@121
 | 
     6 
 | 
| 
jbe@144
 | 
     7 // maximum number of nested JSON values (objects and arrays):
 | 
| 
jbe@150
 | 
     8 // NOTE: The Lua reference states that the stack may typically contain at least
 | 
| 
jbe@150
 | 
     9 //       "a few thousand elements". Since every nested level consumes
 | 
| 
jbe@150
 | 
    10 //       3 elements on the Lua stack (the object/array, its shadow table,
 | 
| 
jbe@150
 | 
    11 //       a string key or a placeholder), we limit the number of nested levels
 | 
| 
jbe@150
 | 
    12 //       to 500. If a stack overflow would still happen in the import function,
 | 
| 
jbe@150
 | 
    13 //       this is detected nevertheless and an error is thrown (instead of
 | 
| 
jbe@150
 | 
    14 //       returning nil and an error string).
 | 
| 
jbe@150
 | 
    15 #define JSON_MAXDEPTH 500
 | 
| 
jbe@142
 | 
    16 
 | 
| 
jbe@155
 | 
    17 // generate dummy memory addresses that represents null values:
 | 
| 
jbe@155
 | 
    18 char json_nullmark;
 | 
| 
jbe@155
 | 
    19 #define json_isnullmark(L, i) (lua_touserdata((L), (i)) == &json_nullmark)
 | 
| 
jbe@155
 | 
    20 #define json_pushnullmark(L) lua_pushlightuserdata((L), &json_nullmark)
 | 
| 
jbe@155
 | 
    21 
 | 
| 
jbe@144
 | 
    22 // macros for usage of Lua registry:
 | 
| 
jbe@144
 | 
    23 #define JSON_REGENT char
 | 
| 
jbe@145
 | 
    24 #define JSON_REGPOINTER void *
 | 
| 
jbe@145
 | 
    25 #define json_regpointer(x) (&json_registry.x)
 | 
| 
jbe@151
 | 
    26 #define json_regfetchpointer(L, x) lua_rawgetp((L), LUA_REGISTRYINDEX, (x))
 | 
| 
jbe@151
 | 
    27 #define json_regfetch(L, x) json_regfetchpointer(L, json_regpointer(x))
 | 
| 
jbe@151
 | 
    28 #define json_regstore(L, x) lua_rawsetp(L, LUA_REGISTRYINDEX, json_regpointer(x))
 | 
| 
jbe@145
 | 
    29 
 | 
| 
jbe@144
 | 
    30 // generate dummy memory addresses that represent Lua objects
 | 
| 
jbe@145
 | 
    31 // via lightuserdata keys and LUA_REGISTRYINDEX:
 | 
| 
jbe@144
 | 
    32 static struct {
 | 
| 
jbe@145
 | 
    33   JSON_REGENT shadowtbl;  // ephemeron table that maps tables to their corresponding shadow table
 | 
| 
jbe@145
 | 
    34   JSON_REGENT unknownmt;  // metatable for tables that may be either JSON objects or JSON arrays
 | 
| 
jbe@145
 | 
    35   JSON_REGENT objectmt;   // metatable for JSON objects
 | 
| 
jbe@145
 | 
    36   JSON_REGENT arraymt;    // metatable for JSON arrays
 | 
| 
jbe@144
 | 
    37 } json_registry;
 | 
| 
jbe@138
 | 
    38 
 | 
| 
jbe@145
 | 
    39 // marks a Lua table as JSON object or JSON array:
 | 
| 
jbe@136
 | 
    40 // (returns its modified argument or a new table if argument is nil)
 | 
| 
jbe@145
 | 
    41 static int json_mark(lua_State *L, JSON_REGPOINTER mt) {
 | 
| 
jbe@145
 | 
    42   // check if argument is nil
 | 
| 
jbe@136
 | 
    43   if (lua_isnoneornil(L, 1)) {
 | 
| 
jbe@145
 | 
    44     // create new table at stack position 1:
 | 
| 
jbe@136
 | 
    45     lua_settop(L, 0);
 | 
| 
jbe@136
 | 
    46     lua_newtable(L);
 | 
| 
jbe@145
 | 
    47     // create shadow table (leaving previously created table on stack position 1):
 | 
| 
jbe@144
 | 
    48     json_regfetch(L, shadowtbl);
 | 
| 
jbe@136
 | 
    49     lua_pushvalue(L, 1);
 | 
| 
jbe@136
 | 
    50     lua_newtable(L);
 | 
| 
jbe@143
 | 
    51     lua_rawset(L, -3);
 | 
| 
jbe@143
 | 
    52   } else {
 | 
| 
jbe@145
 | 
    53     // push shadow table on top of stack:
 | 
| 
jbe@144
 | 
    54     json_regfetch(L, shadowtbl);
 | 
| 
jbe@143
 | 
    55     lua_pushvalue(L, 1);
 | 
| 
jbe@143
 | 
    56     lua_rawget(L, -2);
 | 
| 
jbe@145
 | 
    57     // if shadow table does not exist:
 | 
| 
jbe@143
 | 
    58     if (lua_isnil(L, -1)) {
 | 
| 
jbe@145
 | 
    59       // create shadow table and leave it on top of stack:
 | 
| 
jbe@143
 | 
    60       lua_newtable(L);
 | 
| 
jbe@143
 | 
    61       lua_pushvalue(L, 1);
 | 
| 
jbe@143
 | 
    62       lua_pushvalue(L, -2);
 | 
| 
jbe@143
 | 
    63       lua_rawset(L, -5);
 | 
| 
jbe@143
 | 
    64     }
 | 
| 
jbe@145
 | 
    65     // move elements from original table to shadow table (that's expected on top of stack):
 | 
| 
jbe@143
 | 
    66     for(lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
 | 
| 
jbe@143
 | 
    67       lua_pushvalue(L, -2);
 | 
| 
jbe@143
 | 
    68       lua_pushnil(L);
 | 
| 
jbe@143
 | 
    69       lua_rawset(L, 1);
 | 
| 
jbe@143
 | 
    70       lua_pushvalue(L, -2);
 | 
| 
jbe@143
 | 
    71       lua_pushvalue(L, -2);
 | 
| 
jbe@143
 | 
    72       lua_rawset(L, -5);
 | 
| 
jbe@143
 | 
    73     }
 | 
| 
jbe@136
 | 
    74   }
 | 
| 
jbe@138
 | 
    75   // discard everything but table to return:
 | 
| 
jbe@138
 | 
    76   lua_settop(L, 1);
 | 
| 
jbe@136
 | 
    77   // set metatable:
 | 
| 
jbe@145
 | 
    78   json_regfetchpointer(L, mt);
 | 
| 
jbe@136
 | 
    79   lua_setmetatable(L, 1);
 | 
| 
jbe@138
 | 
    80   // return table:
 | 
| 
jbe@136
 | 
    81   return 1;
 | 
| 
jbe@136
 | 
    82 }
 | 
| 
jbe@136
 | 
    83 
 | 
| 
jbe@136
 | 
    84 // marks a table as JSON object:
 | 
| 
jbe@136
 | 
    85 // (returns its modified argument or a new table if argument is nil)
 | 
| 
jbe@136
 | 
    86 static int json_object(lua_State *L) {
 | 
| 
jbe@145
 | 
    87   return json_mark(L, json_regpointer(objectmt));
 | 
| 
jbe@136
 | 
    88 }
 | 
| 
jbe@136
 | 
    89 
 | 
| 
jbe@136
 | 
    90 // marks a table as JSON array:
 | 
| 
jbe@136
 | 
    91 // (returns its modified argument or a new table if argument is nil)
 | 
| 
jbe@136
 | 
    92 static int json_array(lua_State *L) {
 | 
| 
jbe@145
 | 
    93   return json_mark(L, json_regpointer(arraymt));
 | 
| 
jbe@136
 | 
    94 }
 | 
| 
jbe@136
 | 
    95 
 | 
| 
jbe@145
 | 
    96 // internal states of JSON parser:
 | 
| 
jbe@124
 | 
    97 #define JSON_STATE_VALUE 0
 | 
| 
jbe@124
 | 
    98 #define JSON_STATE_OBJECT_KEY 1
 | 
| 
jbe@124
 | 
    99 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2
 | 
| 
jbe@124
 | 
   100 #define JSON_STATE_OBJECT_VALUE 3
 | 
| 
jbe@124
 | 
   101 #define JSON_STATE_OBJECT_SEPARATOR 4
 | 
| 
jbe@124
 | 
   102 #define JSON_STATE_ARRAY_VALUE 5
 | 
| 
jbe@124
 | 
   103 #define JSON_STATE_ARRAY_SEPARATOR 6
 | 
| 
jbe@124
 | 
   104 #define JSON_STATE_END 7
 | 
| 
jbe@121
 | 
   105 
 | 
| 
jbe@145
 | 
   106 // special Lua stack indicies for json_import function:
 | 
| 
jbe@138
 | 
   107 #define json_import_objectmt_idx 2
 | 
| 
jbe@138
 | 
   108 #define json_import_arraymt_idx 3
 | 
| 
jbe@138
 | 
   109 #define json_import_shadowtbl_idx 4
 | 
| 
jbe@138
 | 
   110 
 | 
| 
jbe@136
 | 
   111 // decodes a JSON document:
 | 
| 
jbe@121
 | 
   112 static int json_import(lua_State *L) {
 | 
| 
jbe@136
 | 
   113   const char *str;   // string to parse
 | 
| 
jbe@136
 | 
   114   size_t total;      // total length of string to parse
 | 
| 
jbe@136
 | 
   115   size_t pos = 0;    // current position in string to parse
 | 
| 
jbe@136
 | 
   116   size_t level = 0;  // nested levels of objects/arrays currently being processed
 | 
| 
jbe@145
 | 
   117   int mode = JSON_STATE_VALUE;  // state of parser (i.e. "what's expected next?")
 | 
| 
jbe@136
 | 
   118   char c;              // variable to store a single character to be processed
 | 
| 
jbe@145
 | 
   119   luaL_Buffer luabuf;  // Lua buffer to decode JSON string values
 | 
| 
jbe@145
 | 
   120   char *cbuf;          // C buffer to decode JSON string values
 | 
| 
jbe@136
 | 
   121   size_t writepos;     // write position of decoded strings in C buffer
 | 
| 
jbe@152
 | 
   122   size_t arraylen;     // variable to temporarily store the array length
 | 
| 
jbe@147
 | 
   123   // stack shall contain one function argument:
 | 
| 
jbe@138
 | 
   124   lua_settop(L, 1);
 | 
| 
jbe@147
 | 
   125   // push objectmt onto stack position 2:
 | 
| 
jbe@144
 | 
   126   json_regfetch(L, objectmt);
 | 
| 
jbe@147
 | 
   127   // push arraymt onto stack position 3:
 | 
| 
jbe@144
 | 
   128   json_regfetch(L, arraymt);
 | 
| 
jbe@147
 | 
   129   // push shadowtbl onto stack position 4:
 | 
| 
jbe@144
 | 
   130   json_regfetch(L, shadowtbl);
 | 
| 
jbe@136
 | 
   131   // require string as first argument:
 | 
| 
jbe@136
 | 
   132   str = luaL_checklstring(L, 1, &total);
 | 
| 
jbe@136
 | 
   133   // if string contains a NULL byte, this is a syntax error
 | 
| 
jbe@136
 | 
   134   if (strlen(str) != total) goto json_import_syntax_error;
 | 
| 
jbe@136
 | 
   135   // main loop of parser:
 | 
| 
jbe@136
 | 
   136   json_import_loop:
 | 
| 
jbe@136
 | 
   137   // skip whitespace and store next character in variable 'c':
 | 
| 
jbe@146
 | 
   138   while (c = str[pos],
 | 
| 
jbe@146
 | 
   139     c == ' ' ||
 | 
| 
jbe@146
 | 
   140     c == '\f' ||
 | 
| 
jbe@146
 | 
   141     c == '\n' ||
 | 
| 
jbe@146
 | 
   142     c == '\r' ||
 | 
| 
jbe@146
 | 
   143     c == '\t' ||
 | 
| 
jbe@146
 | 
   144     c == '\v'
 | 
| 
jbe@146
 | 
   145   ) pos++;
 | 
| 
jbe@136
 | 
   146   // switch statement to handle certain (single) characters:
 | 
| 
jbe@121
 | 
   147   switch (c) {
 | 
| 
jbe@136
 | 
   148   // handle end of JSON document:
 | 
| 
jbe@121
 | 
   149   case 0:
 | 
| 
jbe@136
 | 
   150     // if end of JSON document was expected, then return top element of stack as result:
 | 
| 
jbe@124
 | 
   151     if (mode == JSON_STATE_END) return 1;
 | 
| 
jbe@136
 | 
   152     // otherwise, the JSON document was malformed:
 | 
| 
jbe@121
 | 
   153     json_import_unexpected_eof:
 | 
| 
jbe@121
 | 
   154     lua_pushnil(L);
 | 
| 
jbe@121
 | 
   155     if (level == 0) lua_pushliteral(L, "Empty string");
 | 
| 
jbe@121
 | 
   156     else lua_pushliteral(L, "Unexpected end of JSON document");
 | 
| 
jbe@121
 | 
   157     return 2;
 | 
| 
jbe@136
 | 
   158   // new JSON object:
 | 
| 
jbe@121
 | 
   159   case '{':
 | 
| 
jbe@136
 | 
   160     // if a JSON object is not expected here, then return an error:
 | 
| 
jbe@146
 | 
   161     if (
 | 
| 
jbe@146
 | 
   162       mode != JSON_STATE_VALUE &&
 | 
| 
jbe@146
 | 
   163       mode != JSON_STATE_OBJECT_VALUE &&
 | 
| 
jbe@146
 | 
   164       mode != JSON_STATE_ARRAY_VALUE
 | 
| 
jbe@146
 | 
   165     ) goto json_import_syntax_error;
 | 
| 
jbe@136
 | 
   166     // create JSON object on stack:
 | 
| 
jbe@136
 | 
   167     lua_newtable(L);
 | 
| 
jbe@136
 | 
   168     // set metatable of JSON object:
 | 
| 
jbe@138
 | 
   169     lua_pushvalue(L, json_import_objectmt_idx);
 | 
| 
jbe@125
 | 
   170     lua_setmetatable(L, -2);
 | 
| 
jbe@136
 | 
   171     // create internal shadow table on stack:
 | 
| 
jbe@136
 | 
   172     lua_newtable(L);
 | 
| 
jbe@146
 | 
   173     // register internal shadow table:
 | 
| 
jbe@123
 | 
   174     lua_pushvalue(L, -2);
 | 
| 
jbe@123
 | 
   175     lua_pushvalue(L, -2);
 | 
| 
jbe@138
 | 
   176     lua_rawset(L, json_import_shadowtbl_idx);
 | 
| 
jbe@146
 | 
   177     // expect object key (or end of object) to follow:
 | 
| 
jbe@136
 | 
   178     mode = JSON_STATE_OBJECT_KEY;
 | 
| 
jbe@146
 | 
   179     // jump to common code for opening JSON object and JSON array:
 | 
| 
jbe@142
 | 
   180     goto json_import_open;
 | 
| 
jbe@136
 | 
   181   // new JSON array:
 | 
| 
jbe@121
 | 
   182   case '[':
 | 
| 
jbe@136
 | 
   183     // if a JSON array is not expected here, then return an error:
 | 
| 
jbe@146
 | 
   184     if (
 | 
| 
jbe@146
 | 
   185       mode != JSON_STATE_VALUE &&
 | 
| 
jbe@146
 | 
   186       mode != JSON_STATE_OBJECT_VALUE &&
 | 
| 
jbe@146
 | 
   187       mode != JSON_STATE_ARRAY_VALUE
 | 
| 
jbe@146
 | 
   188     ) goto json_import_syntax_error;
 | 
| 
jbe@136
 | 
   189     // create JSON array on stack:
 | 
| 
jbe@136
 | 
   190     lua_newtable(L);
 | 
| 
jbe@136
 | 
   191     // set metatable of JSON array:
 | 
| 
jbe@138
 | 
   192     lua_pushvalue(L, json_import_arraymt_idx);
 | 
| 
jbe@125
 | 
   193     lua_setmetatable(L, -2);
 | 
| 
jbe@136
 | 
   194     // create internal shadow table on stack:
 | 
| 
jbe@136
 | 
   195     lua_newtable(L);
 | 
| 
jbe@146
 | 
   196     // register internal shadow table:
 | 
| 
jbe@123
 | 
   197     lua_pushvalue(L, -2);
 | 
| 
jbe@123
 | 
   198     lua_pushvalue(L, -2);
 | 
| 
jbe@138
 | 
   199     lua_rawset(L, json_import_shadowtbl_idx);
 | 
| 
jbe@140
 | 
   200     // add nil as key (needed to keep stack balance) and as magic to detect arrays:
 | 
| 
jbe@140
 | 
   201     lua_pushnil(L);
 | 
| 
jbe@146
 | 
   202     // expect array value (or end of array) to follow:
 | 
| 
jbe@142
 | 
   203     mode = JSON_STATE_ARRAY_VALUE;
 | 
| 
jbe@142
 | 
   204     // continue with common code for opening JSON object and JSON array:
 | 
| 
jbe@146
 | 
   205   // common code for opening JSON object or JSON array:
 | 
| 
jbe@142
 | 
   206   json_import_open:
 | 
| 
jbe@142
 | 
   207     // limit nested levels:
 | 
| 
jbe@142
 | 
   208     if (level >= JSON_MAXDEPTH) {
 | 
| 
jbe@142
 | 
   209       lua_pushnil(L);
 | 
| 
jbe@142
 | 
   210       lua_pushliteral(L, "Too many nested JSON levels");
 | 
| 
jbe@142
 | 
   211       return 2;
 | 
| 
jbe@142
 | 
   212     }
 | 
| 
jbe@142
 | 
   213     // additional buffer overflow protection:
 | 
| 
jbe@142
 | 
   214     if (!lua_checkstack(L, LUA_MINSTACK))
 | 
| 
jbe@142
 | 
   215       return luaL_error(L, "Caught stack overflow in JSON import function (too many nested levels and stack size too small)");
 | 
| 
jbe@136
 | 
   216     // increment level:
 | 
| 
jbe@121
 | 
   217     level++;
 | 
| 
jbe@142
 | 
   218     // consume input character:
 | 
| 
jbe@142
 | 
   219     pos++;
 | 
| 
jbe@121
 | 
   220     goto json_import_loop;
 | 
| 
jbe@136
 | 
   221   // end of JSON object:
 | 
| 
jbe@121
 | 
   222   case '}':
 | 
| 
jbe@136
 | 
   223     // if end of JSON object is not expected here, then return an error:
 | 
| 
jbe@146
 | 
   224     if (
 | 
| 
jbe@146
 | 
   225       mode != JSON_STATE_OBJECT_KEY &&
 | 
| 
jbe@146
 | 
   226       mode != JSON_STATE_OBJECT_SEPARATOR
 | 
| 
jbe@146
 | 
   227     ) goto json_import_syntax_error;
 | 
| 
jbe@136
 | 
   228     // jump to common code for end of JSON object and JSON array:
 | 
| 
jbe@121
 | 
   229     goto json_import_close;
 | 
| 
jbe@136
 | 
   230   // end of JSON array:
 | 
| 
jbe@121
 | 
   231   case ']':
 | 
| 
jbe@136
 | 
   232     // if end of JSON array is not expected here, then return an error:
 | 
| 
jbe@146
 | 
   233     if (
 | 
| 
jbe@146
 | 
   234       mode != JSON_STATE_ARRAY_VALUE &&
 | 
| 
jbe@146
 | 
   235       mode != JSON_STATE_ARRAY_SEPARATOR
 | 
| 
jbe@146
 | 
   236     ) goto json_import_syntax_error;
 | 
| 
jbe@146
 | 
   237     // pop nil key/magic (that was needed to keep stack balance):
 | 
| 
jbe@140
 | 
   238     lua_pop(L, 1);
 | 
| 
jbe@136
 | 
   239     // continue with common code for end of JSON object and JSON array:
 | 
| 
jbe@136
 | 
   240   // common code for end of JSON object or JSON array:
 | 
| 
jbe@121
 | 
   241   json_import_close:
 | 
| 
jbe@136
 | 
   242     // consume input character:
 | 
| 
jbe@121
 | 
   243     pos++;
 | 
| 
jbe@136
 | 
   244     // pop shadow table:
 | 
| 
jbe@136
 | 
   245     lua_pop(L, 1);
 | 
| 
jbe@136
 | 
   246     // check if nested:
 | 
| 
jbe@121
 | 
   247     if (--level) {
 | 
| 
jbe@146
 | 
   248       // if nested,
 | 
| 
jbe@146
 | 
   249       // check if outer(!) structure is an array or object:
 | 
| 
jbe@140
 | 
   250       if (lua_isnil(L, -2)) {
 | 
| 
jbe@136
 | 
   251         // select array value processing:
 | 
| 
jbe@124
 | 
   252         mode = JSON_STATE_ARRAY_VALUE;
 | 
| 
jbe@121
 | 
   253       } else {
 | 
| 
jbe@136
 | 
   254         // select object value processing:
 | 
| 
jbe@124
 | 
   255         mode = JSON_STATE_OBJECT_VALUE;
 | 
| 
jbe@121
 | 
   256       }
 | 
| 
jbe@136
 | 
   257       // store value in outer structure:
 | 
| 
jbe@121
 | 
   258       goto json_import_process_value;
 | 
| 
jbe@121
 | 
   259     }
 | 
| 
jbe@136
 | 
   260     // if not nested, then expect end of JSON document and continue with loop:
 | 
| 
jbe@136
 | 
   261     mode = JSON_STATE_END;
 | 
| 
jbe@121
 | 
   262     goto json_import_loop;
 | 
| 
jbe@136
 | 
   263   // key terminator:
 | 
| 
jbe@121
 | 
   264   case ':':
 | 
| 
jbe@136
 | 
   265     // if key terminator is not expected here, then return an error:
 | 
| 
jbe@124
 | 
   266     if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR)
 | 
| 
jbe@121
 | 
   267       goto json_import_syntax_error;
 | 
| 
jbe@136
 | 
   268     // consume input character:
 | 
| 
jbe@121
 | 
   269     pos++;
 | 
| 
jbe@146
 | 
   270     // expect object value to follow:
 | 
| 
jbe@124
 | 
   271     mode = JSON_STATE_OBJECT_VALUE;
 | 
| 
jbe@146
 | 
   272     // continue with loop:
 | 
| 
jbe@121
 | 
   273     goto json_import_loop;
 | 
| 
jbe@136
 | 
   274   // value terminator (NOTE: trailing comma at end of value or key-value list is tolerated by this parser)
 | 
| 
jbe@121
 | 
   275   case ',':
 | 
| 
jbe@146
 | 
   276     // branch according to parser state:
 | 
| 
jbe@124
 | 
   277     if (mode == JSON_STATE_OBJECT_SEPARATOR) {
 | 
| 
jbe@146
 | 
   278       // expect an object key to follow:
 | 
| 
jbe@124
 | 
   279       mode = JSON_STATE_OBJECT_KEY;
 | 
| 
jbe@124
 | 
   280     } else if (mode == JSON_STATE_ARRAY_SEPARATOR) {
 | 
| 
jbe@146
 | 
   281       // expect an array value to follow:
 | 
| 
jbe@124
 | 
   282       mode = JSON_STATE_ARRAY_VALUE;
 | 
| 
jbe@121
 | 
   283     } else {
 | 
| 
jbe@136
 | 
   284        // if value terminator is not expected here, then return an error:
 | 
| 
jbe@136
 | 
   285        goto json_import_syntax_error;
 | 
| 
jbe@121
 | 
   286     }
 | 
| 
jbe@136
 | 
   287     // consume input character:
 | 
| 
jbe@121
 | 
   288     pos++;
 | 
| 
jbe@136
 | 
   289     // continue with loop:
 | 
| 
jbe@121
 | 
   290     goto json_import_loop;
 | 
| 
jbe@136
 | 
   291   // string literal:
 | 
| 
jbe@121
 | 
   292   case '"':
 | 
| 
jbe@146
 | 
   293     // consume quote character:
 | 
| 
jbe@146
 | 
   294     pos++;
 | 
| 
jbe@136
 | 
   295     // prepare buffer to decode string (with maximum possible length) and set write position to zero:
 | 
| 
jbe@121
 | 
   296     cbuf = luaL_buffinitsize(L, &luabuf, total-pos);
 | 
| 
jbe@121
 | 
   297     writepos = 0;
 | 
| 
jbe@146
 | 
   298     // loop through the characters until encountering end quote:
 | 
| 
jbe@121
 | 
   299     while ((c = str[pos++]) != '"') {
 | 
| 
jbe@121
 | 
   300       if (c == 0) {
 | 
| 
jbe@146
 | 
   301         // handle unexpected end of JSON document:
 | 
| 
jbe@121
 | 
   302         goto json_import_unexpected_eof;
 | 
| 
jbe@121
 | 
   303       } else if (c < 32 || c == 127) {
 | 
| 
jbe@136
 | 
   304         // do not allow ASCII control characters:
 | 
| 
jbe@136
 | 
   305         // NOTE: illegal UTF-8 sequences and extended control characters are not sanitized
 | 
| 
jbe@136
 | 
   306         //       by this parser to allow different encodings than Unicode
 | 
| 
jbe@121
 | 
   307         lua_pushnil(L);
 | 
| 
jbe@121
 | 
   308         lua_pushliteral(L, "Unexpected control character in JSON string");
 | 
| 
jbe@121
 | 
   309         return 2;
 | 
| 
jbe@121
 | 
   310       } else if (c == '\\') {
 | 
| 
jbe@136
 | 
   311         // read next char after backslash escape:
 | 
| 
jbe@121
 | 
   312         c = str[pos++];
 | 
| 
jbe@121
 | 
   313         switch (c) {
 | 
| 
jbe@136
 | 
   314         // unexpected end-of-string:
 | 
| 
jbe@121
 | 
   315         case 0:
 | 
| 
jbe@121
 | 
   316           goto json_import_unexpected_eof;
 | 
| 
jbe@136
 | 
   317         // unescaping of quotation mark, slash, and backslash:
 | 
| 
jbe@121
 | 
   318         case '"':
 | 
| 
jbe@121
 | 
   319         case '/':
 | 
| 
jbe@121
 | 
   320         case '\\':
 | 
| 
jbe@121
 | 
   321           cbuf[writepos++] = c;
 | 
| 
jbe@121
 | 
   322           break;
 | 
| 
jbe@136
 | 
   323         // unescaping of backspace:
 | 
| 
jbe@146
 | 
   324         case 'b': cbuf[writepos++] = '\b'; break;
 | 
| 
jbe@136
 | 
   325         // unescaping of form-feed:
 | 
| 
jbe@146
 | 
   326         case 'f': cbuf[writepos++] = '\f'; break;
 | 
| 
jbe@136
 | 
   327         // unescaping of new-line:
 | 
| 
jbe@146
 | 
   328         case 'n': cbuf[writepos++] = '\n'; break;
 | 
| 
jbe@136
 | 
   329         // unescaping of carriage-return:
 | 
| 
jbe@146
 | 
   330         case 'r': cbuf[writepos++] = '\r'; break;
 | 
| 
jbe@136
 | 
   331         // unescaping of tabulator:
 | 
| 
jbe@146
 | 
   332         case 't': cbuf[writepos++] = '\t'; break;
 | 
| 
jbe@136
 | 
   333         // unescaping of UTF-16 characters
 | 
| 
jbe@121
 | 
   334         case 'u':
 | 
| 
jbe@121
 | 
   335           lua_pushnil(L);
 | 
| 
jbe@121
 | 
   336           lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet");  // TODO
 | 
| 
jbe@121
 | 
   337           return 2;
 | 
| 
jbe@136
 | 
   338         // unexpected escape sequence:
 | 
| 
jbe@121
 | 
   339         default:
 | 
| 
jbe@121
 | 
   340           lua_pushnil(L);
 | 
| 
jbe@121
 | 
   341           lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
 | 
| 
jbe@121
 | 
   342           return 2;
 | 
| 
jbe@121
 | 
   343         }
 | 
| 
jbe@121
 | 
   344       } else {
 | 
| 
jbe@136
 | 
   345         // normal character:
 | 
| 
jbe@121
 | 
   346         cbuf[writepos++] = c;
 | 
| 
jbe@121
 | 
   347       }
 | 
| 
jbe@121
 | 
   348     }
 | 
| 
jbe@136
 | 
   349     // process buffer to Lua string:
 | 
| 
jbe@121
 | 
   350     luaL_pushresultsize(&luabuf, writepos);
 | 
| 
jbe@136
 | 
   351     // continue with processing of decoded string:
 | 
| 
jbe@121
 | 
   352     goto json_import_process_value;
 | 
| 
jbe@121
 | 
   353   }
 | 
| 
jbe@136
 | 
   354   // process values whose type is is not deducible from a single character:
 | 
| 
jbe@136
 | 
   355   if ((c >= '0' && c <= '9') || c == '-' || c == '+') {
 | 
| 
jbe@146
 | 
   356     // for numbers,
 | 
| 
jbe@146
 | 
   357     // use strtod() call to parse a (double precision) floating point number:
 | 
| 
jbe@122
 | 
   358     char *endptr;
 | 
| 
jbe@122
 | 
   359     double numval;
 | 
| 
jbe@122
 | 
   360     numval = strtod(str+pos, &endptr);
 | 
| 
jbe@146
 | 
   361     // catch parsing errors:
 | 
| 
jbe@122
 | 
   362     if (endptr == str+pos) goto json_import_syntax_error;
 | 
| 
jbe@146
 | 
   363     // consume characters that were parsed:
 | 
| 
jbe@122
 | 
   364     pos += endptr - (str+pos);
 | 
| 
jbe@146
 | 
   365     // push parsed (double precision) floating point number on Lua stack:
 | 
| 
jbe@122
 | 
   366     lua_pushnumber(L, numval);
 | 
| 
jbe@122
 | 
   367   } else if (!strncmp(str+pos, "true", 4)) {
 | 
| 
jbe@136
 | 
   368     // consume 4 input characters for "true":
 | 
| 
jbe@121
 | 
   369     pos += 4;
 | 
| 
jbe@147
 | 
   370     // put Lua true value onto stack:
 | 
| 
jbe@136
 | 
   371     lua_pushboolean(L, 1);
 | 
| 
jbe@121
 | 
   372   } else if (!strncmp(str+pos, "false", 5)) {
 | 
| 
jbe@136
 | 
   373     // consume 5 input characters for "false":
 | 
| 
jbe@121
 | 
   374     pos += 5;
 | 
| 
jbe@147
 | 
   375     // put Lua false value onto stack:
 | 
| 
jbe@136
 | 
   376     lua_pushboolean(L, 0);
 | 
| 
jbe@121
 | 
   377   } else if (!strncmp(str+pos, "null", 4)) {
 | 
| 
jbe@136
 | 
   378     // consume 4 input characters for "null":
 | 
| 
jbe@136
 | 
   379     pos += 4;
 | 
| 
jbe@153
 | 
   380     // different behavor for top-level and sub-levels:
 | 
| 
jbe@153
 | 
   381     if (level) {
 | 
| 
jbe@153
 | 
   382       // if sub-level,
 | 
| 
jbe@153
 | 
   383       // push special null-marker onto stack:
 | 
| 
jbe@155
 | 
   384       json_pushnullmark(L);
 | 
| 
jbe@153
 | 
   385     } else {
 | 
| 
jbe@153
 | 
   386       // if top-level,
 | 
| 
jbe@153
 | 
   387       // push nil onto stack:
 | 
| 
jbe@153
 | 
   388       lua_pushnil(L);
 | 
| 
jbe@153
 | 
   389     }
 | 
| 
jbe@121
 | 
   390   } else {
 | 
| 
jbe@136
 | 
   391     // all other cases are a syntax error:
 | 
| 
jbe@121
 | 
   392     goto json_import_syntax_error;
 | 
| 
jbe@121
 | 
   393   }
 | 
| 
jbe@136
 | 
   394   // process a decoded value or key value pair (expected on top of Lua stack):
 | 
| 
jbe@136
 | 
   395   json_import_process_value:
 | 
| 
jbe@121
 | 
   396   switch (mode) {
 | 
| 
jbe@136
 | 
   397   // an object key has been read:
 | 
| 
jbe@124
 | 
   398   case JSON_STATE_OBJECT_KEY:
 | 
| 
jbe@136
 | 
   399     // if an object key is not a string, then this is a syntax error:
 | 
| 
jbe@121
 | 
   400     if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
 | 
| 
jbe@146
 | 
   401     // expect key terminator to follow:
 | 
| 
jbe@124
 | 
   402     mode = JSON_STATE_OBJECT_KEY_TERMINATOR;
 | 
| 
jbe@146
 | 
   403     // continue with loop:
 | 
| 
jbe@121
 | 
   404     goto json_import_loop;
 | 
| 
jbe@136
 | 
   405   // a key value pair has been read:
 | 
| 
jbe@124
 | 
   406   case JSON_STATE_OBJECT_VALUE:
 | 
| 
jbe@136
 | 
   407     // store key value pair in outer shadow table:
 | 
| 
jbe@130
 | 
   408     lua_rawset(L, -3);
 | 
| 
jbe@146
 | 
   409     // expect value terminator (or end of object) to follow:
 | 
| 
jbe@124
 | 
   410     mode = JSON_STATE_OBJECT_SEPARATOR;
 | 
| 
jbe@146
 | 
   411     // continue with loop:
 | 
| 
jbe@121
 | 
   412     goto json_import_loop;
 | 
| 
jbe@136
 | 
   413   // an array value has been read:
 | 
| 
jbe@124
 | 
   414   case JSON_STATE_ARRAY_VALUE:
 | 
| 
jbe@152
 | 
   415     // get current array length:
 | 
| 
jbe@152
 | 
   416     arraylen = lua_rawlen(L, -3);
 | 
| 
jbe@152
 | 
   417     // throw error if array would exceed INT_MAX elements:
 | 
| 
jbe@152
 | 
   418     // TODO: Lua 5.3 may support more elements
 | 
| 
jbe@152
 | 
   419     if (arraylen >= INT_MAX) {
 | 
| 
jbe@152
 | 
   420       lua_pushnil(L);
 | 
| 
jbe@152
 | 
   421       lua_pushfstring(L, "Array exceeded length of %d elements", INT_MAX);
 | 
| 
jbe@152
 | 
   422     }
 | 
| 
jbe@136
 | 
   423     // store value in outer shadow table:
 | 
| 
jbe@152
 | 
   424     lua_rawseti(L, -3, arraylen + 1);
 | 
| 
jbe@146
 | 
   425     // expect value terminator (or end of object) to follow:
 | 
| 
jbe@124
 | 
   426     mode = JSON_STATE_ARRAY_SEPARATOR;
 | 
| 
jbe@146
 | 
   427     // continue with loop
 | 
| 
jbe@121
 | 
   428     goto json_import_loop;
 | 
| 
jbe@136
 | 
   429   // a single value has been read:
 | 
| 
jbe@124
 | 
   430   case JSON_STATE_VALUE:
 | 
| 
jbe@136
 | 
   431     // leave value on top of stack, expect end of JSON document, and continue with loop:
 | 
| 
jbe@124
 | 
   432     mode = JSON_STATE_END;
 | 
| 
jbe@121
 | 
   433     goto json_import_loop;
 | 
| 
jbe@121
 | 
   434   }
 | 
| 
jbe@146
 | 
   435   // syntax error handling (reachable by goto statement):
 | 
| 
jbe@136
 | 
   436   json_import_syntax_error:
 | 
| 
jbe@121
 | 
   437   lua_pushnil(L);
 | 
| 
jbe@121
 | 
   438   lua_pushliteral(L, "Syntax error in JSON document");
 | 
| 
jbe@121
 | 
   439   return 2;
 | 
| 
jbe@121
 | 
   440 }
 | 
| 
jbe@121
 | 
   441 
 | 
| 
jbe@146
 | 
   442 // special Lua stack indicies for json_path function:
 | 
| 
jbe@138
 | 
   443 #define json_path_shadowtbl_idx 1
 | 
| 
jbe@146
 | 
   444 
 | 
| 
jbe@146
 | 
   445 // stack offset of arguments to json_path function:
 | 
| 
jbe@155
 | 
   446 #define json_path_idxshift 1
 | 
| 
jbe@138
 | 
   447 
 | 
| 
jbe@146
 | 
   448 // gets a value or its type from a JSON document (passed as first argument)
 | 
| 
jbe@147
 | 
   449 // using a path (passed as variable number of keys after first argument):
 | 
| 
jbe@137
 | 
   450 static int json_path(lua_State *L, int type_mode) {
 | 
| 
jbe@146
 | 
   451   int stacktop;                      // stack index of top of stack (after shifting)
 | 
| 
jbe@146
 | 
   452   int idx = 2 + json_path_idxshift;  // stack index of current argument to process
 | 
| 
jbe@148
 | 
   453   // insert shadowtbl into stack at position 1 (shifting the arguments):
 | 
| 
jbe@144
 | 
   454   json_regfetch(L, shadowtbl);
 | 
| 
jbe@138
 | 
   455   lua_insert(L, 1);
 | 
| 
jbe@146
 | 
   456   // store stack index of top of stack:
 | 
| 
jbe@138
 | 
   457   stacktop = lua_gettop(L);
 | 
| 
jbe@146
 | 
   458   // use first argument as "current value" (stored on top of stack):
 | 
| 
jbe@138
 | 
   459   lua_pushvalue(L, 1 + json_path_idxshift);
 | 
| 
jbe@146
 | 
   460   // process each "path key" (2nd argument and following arguments):
 | 
| 
jbe@138
 | 
   461   while (idx <= stacktop) {
 | 
| 
jbe@146
 | 
   462     // if "current value" (on top of stack) is nil, then the path cannot be walked and nil is returned:
 | 
| 
jbe@137
 | 
   463     if (lua_isnil(L, -1)) return 1;
 | 
| 
jbe@137
 | 
   464     // try to get shadow table of "current value":
 | 
| 
jbe@130
 | 
   465     lua_pushvalue(L, -1);
 | 
| 
jbe@138
 | 
   466     lua_rawget(L, json_path_shadowtbl_idx);
 | 
| 
jbe@126
 | 
   467     if (lua_isnil(L, -1)) {
 | 
| 
jbe@137
 | 
   468       // if no shadow table is found,
 | 
| 
jbe@130
 | 
   469       if (lua_type(L, -1) == LUA_TTABLE) {
 | 
| 
jbe@146
 | 
   470         // and if "current value" is a table,
 | 
| 
jbe@146
 | 
   471         // drop nil from stack:
 | 
| 
jbe@146
 | 
   472         lua_pop(L, 1);
 | 
| 
jbe@137
 | 
   473         // get "next value" using the "path key":
 | 
| 
jbe@130
 | 
   474         lua_pushvalue(L, idx++);
 | 
| 
jbe@130
 | 
   475         lua_gettable(L, -2);
 | 
| 
jbe@130
 | 
   476       } else {
 | 
| 
jbe@137
 | 
   477         // if "current value" is not a table,
 | 
| 
jbe@146
 | 
   478         // then the path cannot be walked and nil (already on top of stack) is returned:
 | 
| 
jbe@137
 | 
   479         return 1;
 | 
| 
jbe@130
 | 
   480       }
 | 
| 
jbe@130
 | 
   481     } else {
 | 
| 
jbe@137
 | 
   482       // if a shadow table is found,
 | 
| 
jbe@137
 | 
   483       // set "current value" to its shadow table:
 | 
| 
jbe@130
 | 
   484       lua_replace(L, -2);
 | 
| 
jbe@137
 | 
   485       // get "next value" using the "path key":
 | 
| 
jbe@130
 | 
   486       lua_pushvalue(L, idx++);
 | 
| 
jbe@130
 | 
   487       lua_rawget(L, -2);
 | 
| 
jbe@126
 | 
   488     }
 | 
| 
jbe@137
 | 
   489     // the "next value" replaces the "current value":
 | 
| 
jbe@130
 | 
   490     lua_replace(L, -2);
 | 
| 
jbe@126
 | 
   491   }
 | 
| 
jbe@137
 | 
   492   if (!type_mode) {
 | 
| 
jbe@137
 | 
   493     // if a value (and not its type) was requested,
 | 
| 
jbe@137
 | 
   494     // check if value is the null-marker, and store nil on top of Lua stack in that case:
 | 
| 
jbe@155
 | 
   495     if (json_isnullmark(L, -1)) lua_pushnil(L);
 | 
| 
jbe@137
 | 
   496   } else {
 | 
| 
jbe@137
 | 
   497     // if the type was requested,
 | 
| 
jbe@137
 | 
   498     // check if value is the null-marker:
 | 
| 
jbe@155
 | 
   499     if (json_isnullmark(L, -1)) {
 | 
| 
jbe@137
 | 
   500       // if yes, store string "null" on top of Lua stack:
 | 
| 
jbe@130
 | 
   501       lua_pushliteral(L, "null");
 | 
| 
jbe@137
 | 
   502     } else {
 | 
| 
jbe@137
 | 
   503       // otherwise,
 | 
| 
jbe@138
 | 
   504       // check if metatable indicates "object" or "array":
 | 
| 
jbe@138
 | 
   505       if (lua_getmetatable(L, -1)) {
 | 
| 
jbe@144
 | 
   506         json_regfetch(L, objectmt);
 | 
| 
jbe@138
 | 
   507         if (lua_rawequal(L, -2, -1)) {
 | 
| 
jbe@146
 | 
   508           // if value has metatable for JSON objects,
 | 
| 
jbe@138
 | 
   509           // return string "object":
 | 
| 
jbe@138
 | 
   510           lua_pushliteral(L, "object");
 | 
| 
jbe@138
 | 
   511           return 1;
 | 
| 
jbe@138
 | 
   512         }
 | 
| 
jbe@144
 | 
   513         json_regfetch(L, arraymt);
 | 
| 
jbe@138
 | 
   514         if (lua_rawequal(L, -3, -1)) {
 | 
| 
jbe@146
 | 
   515           // if value has metatable for JSON arrays,
 | 
| 
jbe@146
 | 
   516           // return string "object":
 | 
| 
jbe@138
 | 
   517           lua_pushliteral(L, "array");
 | 
| 
jbe@138
 | 
   518           return 1;
 | 
| 
jbe@138
 | 
   519         }
 | 
| 
jbe@146
 | 
   520         // remove 3 metatables (one of the value, two for comparison) from stack:
 | 
| 
jbe@138
 | 
   521         lua_pop(L, 3);
 | 
| 
jbe@138
 | 
   522       }
 | 
| 
jbe@138
 | 
   523       // otherwise, get the Lua type:
 | 
| 
jbe@138
 | 
   524       lua_pushstring(L, lua_typename(L, lua_type(L, -1)));
 | 
| 
jbe@126
 | 
   525     }
 | 
| 
jbe@126
 | 
   526   }
 | 
| 
jbe@137
 | 
   527   // return the top most value on the Lua stack:
 | 
| 
jbe@137
 | 
   528   return 1;
 | 
| 
jbe@130
 | 
   529 }
 | 
| 
jbe@130
 | 
   530 
 | 
| 
jbe@147
 | 
   531 // gets a value from a JSON document (passed as first argument)
 | 
| 
jbe@147
 | 
   532 // using a path (passed as variable number of keys after first argument):
 | 
| 
jbe@130
 | 
   533 static int json_get(lua_State *L) {
 | 
| 
jbe@137
 | 
   534   return json_path(L, 0);
 | 
| 
jbe@130
 | 
   535 }
 | 
| 
jbe@130
 | 
   536 
 | 
| 
jbe@147
 | 
   537 // gets a value's type from a JSON document (passed as first argument)
 | 
| 
jbe@147
 | 
   538 // using a path (variable number of keys after first argument):
 | 
| 
jbe@130
 | 
   539 static int json_type(lua_State *L) {
 | 
| 
jbe@137
 | 
   540   return json_path(L, 1);
 | 
| 
jbe@130
 | 
   541 }
 | 
| 
jbe@130
 | 
   542 
 | 
| 
jbe@147
 | 
   543 // checks if a value in a JSON document (first argument) is
 | 
| 
jbe@147
 | 
   544 // explicitly set to null:
 | 
| 
jbe@130
 | 
   545 static int json_isnull(lua_State *L) {
 | 
| 
jbe@137
 | 
   546   const char *jsontype;
 | 
| 
jbe@147
 | 
   547   // call json_type function with variable arguments:
 | 
| 
jbe@138
 | 
   548   lua_pushcfunction(L, json_type);
 | 
| 
jbe@137
 | 
   549   lua_insert(L, 1);
 | 
| 
jbe@137
 | 
   550   lua_call(L, lua_gettop(L) - 1, 1);
 | 
| 
jbe@147
 | 
   551   // return true if result equals to string "null", otherwise return false:
 | 
| 
jbe@137
 | 
   552   jsontype = lua_tostring(L, -1);
 | 
| 
jbe@137
 | 
   553   if (jsontype && !strcmp(jsontype, "null")) lua_pushboolean(L, 1);
 | 
| 
jbe@137
 | 
   554   else lua_pushboolean(L, 0);
 | 
| 
jbe@137
 | 
   555   return 1;
 | 
| 
jbe@130
 | 
   556 }
 | 
| 
jbe@130
 | 
   557 
 | 
| 
jbe@146
 | 
   558 // special Lua stack indicies for json_setnull function:
 | 
| 
jbe@138
 | 
   559 #define json_setnull_unknownmt_idx 3
 | 
| 
jbe@138
 | 
   560 #define json_setnull_objectmt_idx 4
 | 
| 
jbe@138
 | 
   561 #define json_setnull_arraymt_idx 5
 | 
| 
jbe@138
 | 
   562 #define json_setnull_shadowtbl_idx 6
 | 
| 
jbe@138
 | 
   563 
 | 
| 
jbe@147
 | 
   564 // sets a value in a JSON object or JSON array explicitly to null:
 | 
| 
jbe@147
 | 
   565 // NOTE: JSON null is different than absence of a key
 | 
| 
jbe@131
 | 
   566 static int json_setnull(lua_State *L) {
 | 
| 
jbe@147
 | 
   567   // stack shall contain two function arguments:
 | 
| 
jbe@131
 | 
   568   lua_settop(L, 2);
 | 
| 
jbe@148
 | 
   569   // push unknownmt onto stack position 3:
 | 
| 
jbe@144
 | 
   570   json_regfetch(L, unknownmt);
 | 
| 
jbe@148
 | 
   571   // push objectmt onto stack position 4:
 | 
| 
jbe@144
 | 
   572   json_regfetch(L, objectmt);
 | 
| 
jbe@148
 | 
   573   // push arraymt onto stack position 5:
 | 
| 
jbe@144
 | 
   574   json_regfetch(L, arraymt);
 | 
| 
jbe@148
 | 
   575   // push shadowtbl onto stack position 6:
 | 
| 
jbe@144
 | 
   576   json_regfetch(L, shadowtbl);
 | 
| 
jbe@147
 | 
   577   // set metatable if necessary (leaves unknown number of elements on stack):
 | 
| 
jbe@138
 | 
   578   if (
 | 
| 
jbe@147
 | 
   579     !lua_getmetatable(L, 1) || (
 | 
| 
jbe@147
 | 
   580       !lua_rawequal(L, -1, json_setnull_unknownmt_idx) &&
 | 
| 
jbe@147
 | 
   581       !lua_rawequal(L, -1, json_setnull_objectmt_idx) &&
 | 
| 
jbe@147
 | 
   582       !lua_rawequal(L, -1, json_setnull_arraymt_idx)
 | 
| 
jbe@147
 | 
   583     )
 | 
| 
jbe@138
 | 
   584   ) {
 | 
| 
jbe@138
 | 
   585     lua_pushvalue(L, json_setnull_unknownmt_idx);
 | 
| 
jbe@138
 | 
   586     lua_setmetatable(L, 1);
 | 
| 
jbe@138
 | 
   587   }
 | 
| 
jbe@147
 | 
   588   // try to get shadow table:
 | 
| 
jbe@131
 | 
   589   lua_pushvalue(L, 1);
 | 
| 
jbe@138
 | 
   590   lua_rawget(L, json_setnull_shadowtbl_idx);
 | 
| 
jbe@131
 | 
   591   if (lua_isnil(L, -1)) {
 | 
| 
jbe@147
 | 
   592     // if no shadow table is found,
 | 
| 
jbe@147
 | 
   593     // create new shadow table (and leave it on top of stack):
 | 
| 
jbe@131
 | 
   594     lua_newtable(L);
 | 
| 
jbe@147
 | 
   595     // register shadow table:
 | 
| 
jbe@131
 | 
   596     lua_pushvalue(L, 1);
 | 
| 
jbe@131
 | 
   597     lua_pushvalue(L, -2);
 | 
| 
jbe@138
 | 
   598     lua_rawset(L, json_setnull_shadowtbl_idx);
 | 
| 
jbe@131
 | 
   599   }
 | 
| 
jbe@147
 | 
   600   // push key (second argument) and null-marker after shadow table onto stack:
 | 
| 
jbe@131
 | 
   601   lua_pushvalue(L, 2);
 | 
| 
jbe@155
 | 
   602   json_pushnullmark(L);
 | 
| 
jbe@147
 | 
   603   // store key and null-marker in shadow table:
 | 
| 
jbe@131
 | 
   604   lua_rawset(L, -3);
 | 
| 
jbe@147
 | 
   605   // return nothing:
 | 
| 
jbe@131
 | 
   606   return 0;
 | 
| 
jbe@131
 | 
   607 }
 | 
| 
jbe@131
 | 
   608 
 | 
| 
jbe@147
 | 
   609 // returns the length of a JSON array (or zero for a table without numeric keys):
 | 
| 
jbe@130
 | 
   610 static int json_len(lua_State *L) {
 | 
| 
jbe@147
 | 
   611   // stack shall contain one function argument:
 | 
| 
jbe@130
 | 
   612   lua_settop(L, 1);
 | 
| 
jbe@148
 | 
   613   // try to get corresponding shadow table for first argument:
 | 
| 
jbe@144
 | 
   614   json_regfetch(L, shadowtbl);
 | 
| 
jbe@130
 | 
   615   lua_pushvalue(L, 1);
 | 
| 
jbe@138
 | 
   616   lua_rawget(L, -2);
 | 
| 
jbe@147
 | 
   617   // if shadow table does not exist, return length of argument, else length of shadow table:
 | 
| 
jbe@147
 | 
   618   lua_pushnumber(L, lua_rawlen(L, lua_isnil(L, -1) ? 1 : -1));
 | 
| 
jbe@123
 | 
   619   return 1;
 | 
| 
jbe@123
 | 
   620 }
 | 
| 
jbe@123
 | 
   621 
 | 
| 
jbe@130
 | 
   622 static int json_index(lua_State *L) {
 | 
| 
jbe@148
 | 
   623   // stack shall contain two function arguments:
 | 
| 
jbe@130
 | 
   624   lua_settop(L, 2);
 | 
| 
jbe@155
 | 
   625   // get corresponding shadow table for first argument:
 | 
| 
jbe@144
 | 
   626   json_regfetch(L, shadowtbl);
 | 
| 
jbe@130
 | 
   627   lua_pushvalue(L, 1);
 | 
| 
jbe@155
 | 
   628   lua_rawget(L, -2);
 | 
| 
jbe@148
 | 
   629   // throw error if no shadow table was found:
 | 
| 
jbe@139
 | 
   630   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
 | 
| 
jbe@148
 | 
   631   // use key passed as second argument to lookup value in shadow table:
 | 
| 
jbe@130
 | 
   632   lua_pushvalue(L, 2);
 | 
| 
jbe@130
 | 
   633   lua_rawget(L, -2);
 | 
| 
jbe@148
 | 
   634   // if value is null-marker, then push nil onto stack:
 | 
| 
jbe@155
 | 
   635   if (json_isnullmark(L, -1)) lua_pushnil(L);
 | 
| 
jbe@148
 | 
   636   // return either looked up value, or nil
 | 
| 
jbe@127
 | 
   637   return 1;
 | 
| 
jbe@127
 | 
   638 }
 | 
| 
jbe@127
 | 
   639 
 | 
| 
jbe@130
 | 
   640 static int json_newindex(lua_State *L) {
 | 
| 
jbe@148
 | 
   641   // stack shall contain three function arguments:
 | 
| 
jbe@130
 | 
   642   lua_settop(L, 3);
 | 
| 
jbe@148
 | 
   643   // get corresponding shadow table for first argument:
 | 
| 
jbe@144
 | 
   644   json_regfetch(L, shadowtbl);
 | 
| 
jbe@123
 | 
   645   lua_pushvalue(L, 1);
 | 
| 
jbe@143
 | 
   646   lua_rawget(L, -2);
 | 
| 
jbe@148
 | 
   647   // throw error if no shadow table was found:
 | 
| 
jbe@130
 | 
   648   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
 | 
| 
jbe@148
 | 
   649   // replace first argument with shadow table:
 | 
| 
jbe@130
 | 
   650   lua_replace(L, 1);
 | 
| 
jbe@148
 | 
   651   // reset stack and use second and third argument to write to shadow table:
 | 
| 
jbe@139
 | 
   652   lua_settop(L, 3);
 | 
| 
jbe@130
 | 
   653   lua_rawset(L, 1);
 | 
| 
jbe@148
 | 
   654   // return nothing:
 | 
| 
jbe@148
 | 
   655   return 0;
 | 
| 
jbe@121
 | 
   656 }
 | 
| 
jbe@121
 | 
   657 
 | 
| 
jbe@135
 | 
   658 static int json_pairs_iterfunc(lua_State *L) {
 | 
| 
jbe@149
 | 
   659   // stack shall contain two function arguments:
 | 
| 
jbe@135
 | 
   660   lua_settop(L, 2);
 | 
| 
jbe@155
 | 
   661   // get corresponding shadow table for first argument:
 | 
| 
jbe@144
 | 
   662   json_regfetch(L, shadowtbl);
 | 
| 
jbe@135
 | 
   663   lua_pushvalue(L, 1);
 | 
| 
jbe@155
 | 
   664   lua_rawget(L, -2);
 | 
| 
jbe@149
 | 
   665   // throw error if no shadow table was found:
 | 
| 
jbe@135
 | 
   666   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
 | 
| 
jbe@149
 | 
   667   // get next key value pair from shadow table (using previous key from argument 2)
 | 
| 
jbe@149
 | 
   668   // and return nothing if there is no next pair:
 | 
| 
jbe@135
 | 
   669   lua_pushvalue(L, 2);
 | 
| 
jbe@135
 | 
   670   if (!lua_next(L, -2)) return 0;
 | 
| 
jbe@149
 | 
   671   // replace null-marker with nil:
 | 
| 
jbe@155
 | 
   672   if (json_isnullmark(L, -1)) {
 | 
| 
jbe@135
 | 
   673     lua_pop(L, 1);
 | 
| 
jbe@135
 | 
   674     lua_pushnil(L);
 | 
| 
jbe@135
 | 
   675   }
 | 
| 
jbe@149
 | 
   676   // return key and value (or key and nil, if null-marker was found):
 | 
| 
jbe@135
 | 
   677   return 2;
 | 
| 
jbe@135
 | 
   678 }
 | 
| 
jbe@135
 | 
   679 
 | 
| 
jbe@149
 | 
   680 // returns a triple such that 'for key, value in pairs(obj) do ... end'
 | 
| 
jbe@149
 | 
   681 // iterates through all key value pairs (including JSON null keys represented as Lua nil):
 | 
| 
jbe@135
 | 
   682 static int json_pairs(lua_State *L) {
 | 
| 
jbe@149
 | 
   683   // return triple of function json_pairs_iterfunc, first argument, and nil:
 | 
| 
jbe@139
 | 
   684   lua_pushcfunction(L, json_pairs_iterfunc);
 | 
| 
jbe@135
 | 
   685   lua_pushvalue(L, 1);
 | 
| 
jbe@135
 | 
   686   lua_pushnil(L);
 | 
| 
jbe@135
 | 
   687   return 3;
 | 
| 
jbe@135
 | 
   688 }
 | 
| 
jbe@135
 | 
   689 
 | 
| 
jbe@134
 | 
   690 static int json_ipairs_iterfunc(lua_State *L) {
 | 
| 
jbe@152
 | 
   691   lua_Integer idx;
 | 
| 
jbe@149
 | 
   692   // stack shall contain two function arguments:
 | 
| 
jbe@134
 | 
   693   lua_settop(L, 2);
 | 
| 
jbe@149
 | 
   694   // calculate new index by incrementing second argument:
 | 
| 
jbe@134
 | 
   695   idx = lua_tointeger(L, 2) + 1;
 | 
| 
jbe@149
 | 
   696   // get corresponding shadow table for first argument:
 | 
| 
jbe@155
 | 
   697   json_regfetch(L, shadowtbl);
 | 
| 
jbe@134
 | 
   698   lua_pushvalue(L, 1);
 | 
| 
jbe@155
 | 
   699   lua_rawget(L, -2);
 | 
| 
jbe@149
 | 
   700   // throw error if no shadow table was found:
 | 
| 
jbe@134
 | 
   701   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
 | 
| 
jbe@149
 | 
   702   // do integer lookup in shadow table:
 | 
| 
jbe@134
 | 
   703   lua_rawgeti(L, -1, idx);
 | 
| 
jbe@149
 | 
   704   // return nothing if there was no value:
 | 
| 
jbe@134
 | 
   705   if (lua_isnil(L, -1)) return 0;
 | 
| 
jbe@149
 | 
   706   // return new index and
 | 
| 
jbe@149
 | 
   707   // either the looked up value if it is not equal to the null-marker
 | 
| 
jbe@149
 | 
   708   // or nil instead of null-marker:
 | 
| 
jbe@134
 | 
   709   lua_pushinteger(L, idx);
 | 
| 
jbe@155
 | 
   710   if (json_isnullmark(L, -2)) lua_pushnil(L);
 | 
| 
jbe@134
 | 
   711   else lua_pushvalue(L, -2);
 | 
| 
jbe@134
 | 
   712   return 2;
 | 
| 
jbe@134
 | 
   713 }
 | 
| 
jbe@134
 | 
   714 
 | 
| 
jbe@149
 | 
   715 // returns a triple such that 'for idx, value in ipairs(ary) do ... end'
 | 
| 
jbe@149
 | 
   716 // iterates through all values (including JSON null represented as Lua nil):
 | 
| 
jbe@134
 | 
   717 static int json_ipairs(lua_State *L) {
 | 
| 
jbe@149
 | 
   718   // return triple of function json_ipairs_iterfunc, first argument, and zero:
 | 
| 
jbe@139
 | 
   719   lua_pushcfunction(L, json_ipairs_iterfunc);
 | 
| 
jbe@134
 | 
   720   lua_pushvalue(L, 1);
 | 
| 
jbe@134
 | 
   721   lua_pushinteger(L, 0);
 | 
| 
jbe@134
 | 
   722   return 3;
 | 
| 
jbe@134
 | 
   723 }
 | 
| 
jbe@134
 | 
   724 
 | 
| 
jbe@154
 | 
   725 #define JSON_TABLETYPE_UNKNOWN 0
 | 
| 
jbe@154
 | 
   726 #define JSON_TABLETYPE_OBJECT 1
 | 
| 
jbe@154
 | 
   727 #define JSON_TABLETYPE_ARRAY 2
 | 
| 
jbe@154
 | 
   728 
 | 
| 
jbe@154
 | 
   729 static int json_export(lua_State *L) {
 | 
| 
jbe@154
 | 
   730   lua_Number num;
 | 
| 
jbe@154
 | 
   731   const char *str;
 | 
| 
jbe@154
 | 
   732   unsigned char c;
 | 
| 
jbe@154
 | 
   733   size_t strlen;
 | 
| 
jbe@154
 | 
   734   size_t pos = 0;
 | 
| 
jbe@154
 | 
   735   luaL_Buffer buf;
 | 
| 
jbe@154
 | 
   736   char hexcode[7];  // backslash, character 'u', 4 hex digits, and terminating NULL byte
 | 
| 
jbe@154
 | 
   737   int luatype;
 | 
| 
jbe@154
 | 
   738   int tabletype = JSON_TABLETYPE_UNKNOWN;
 | 
| 
jbe@154
 | 
   739   int needsep = 0;
 | 
| 
jbe@154
 | 
   740   lua_Integer idx;
 | 
| 
jbe@154
 | 
   741   lua_settop(L, 1);
 | 
| 
jbe@154
 | 
   742   switch (lua_type(L, 1)) {
 | 
| 
jbe@154
 | 
   743   case LUA_TNIL:
 | 
| 
jbe@154
 | 
   744     lua_pushliteral(L, "null");
 | 
| 
jbe@154
 | 
   745     return 1;
 | 
| 
jbe@154
 | 
   746   case LUA_TNUMBER:
 | 
| 
jbe@154
 | 
   747     num = lua_tonumber(L, 1);
 | 
| 
jbe@154
 | 
   748     if (isnan(num)) return luaL_error(L, "JSON export not possible for NaN value");
 | 
| 
jbe@154
 | 
   749     if (isinf(num)) return luaL_error(L, "JSON export not possible for infinite numbers");
 | 
| 
jbe@154
 | 
   750     lua_tostring(L, 1);
 | 
| 
jbe@154
 | 
   751     return 1;
 | 
| 
jbe@154
 | 
   752   case LUA_TBOOLEAN:
 | 
| 
jbe@154
 | 
   753     if (lua_toboolean(L, 1)) lua_pushliteral(L, "true");
 | 
| 
jbe@154
 | 
   754     else lua_pushliteral(L, "false");
 | 
| 
jbe@154
 | 
   755     return 1;
 | 
| 
jbe@154
 | 
   756   case LUA_TSTRING:
 | 
| 
jbe@154
 | 
   757     str = lua_tolstring(L, 1, &strlen);
 | 
| 
jbe@154
 | 
   758     luaL_buffinit(L, &buf);
 | 
| 
jbe@154
 | 
   759     luaL_addchar(&buf, '"');
 | 
| 
jbe@154
 | 
   760     while (pos < strlen) {
 | 
| 
jbe@154
 | 
   761       c = str[pos++];
 | 
| 
jbe@154
 | 
   762       if (c == '"')       luaL_addstring(&buf, "\\\"");
 | 
| 
jbe@154
 | 
   763       else if (c == '\\') luaL_addstring(&buf, "\\\\");
 | 
| 
jbe@154
 | 
   764       else if (c == 127)  luaL_addstring(&buf, "\\u007F");
 | 
| 
jbe@154
 | 
   765       else if (c >= 32)   luaL_addchar(&buf, c);
 | 
| 
jbe@154
 | 
   766       else if (c == '\b') luaL_addstring(&buf, "\\b");
 | 
| 
jbe@154
 | 
   767       else if (c == '\f') luaL_addstring(&buf, "\\f");
 | 
| 
jbe@154
 | 
   768       else if (c == '\n') luaL_addstring(&buf, "\\n");
 | 
| 
jbe@154
 | 
   769       else if (c == '\r') luaL_addstring(&buf, "\\r");
 | 
| 
jbe@154
 | 
   770       else if (c == '\t') luaL_addstring(&buf, "\\t");
 | 
| 
jbe@154
 | 
   771       else if (c == '\v') luaL_addstring(&buf, "\\v");
 | 
| 
jbe@154
 | 
   772       else {
 | 
| 
jbe@154
 | 
   773         sprintf(hexcode, "\\u%04X", c);
 | 
| 
jbe@154
 | 
   774         luaL_addstring(&buf, hexcode);
 | 
| 
jbe@154
 | 
   775       }
 | 
| 
jbe@154
 | 
   776     }
 | 
| 
jbe@154
 | 
   777     luaL_addchar(&buf, '"');
 | 
| 
jbe@154
 | 
   778     luaL_pushresult(&buf);
 | 
| 
jbe@154
 | 
   779     return 1;
 | 
| 
jbe@154
 | 
   780   case LUA_TTABLE:
 | 
| 
jbe@154
 | 
   781     if (lua_getmetatable(L, 1)) {
 | 
| 
jbe@154
 | 
   782       json_regfetch(L, objectmt);
 | 
| 
jbe@154
 | 
   783       if (lua_rawequal(L, -2, -1)) {
 | 
| 
jbe@154
 | 
   784         tabletype = JSON_TABLETYPE_OBJECT;
 | 
| 
jbe@154
 | 
   785       } else {
 | 
| 
jbe@154
 | 
   786         json_regfetch(L, arraymt);
 | 
| 
jbe@154
 | 
   787         if (lua_rawequal(L, -3, -1)) tabletype = JSON_TABLETYPE_ARRAY;
 | 
| 
jbe@154
 | 
   788       }
 | 
| 
jbe@154
 | 
   789     }
 | 
| 
jbe@154
 | 
   790     json_regfetch(L, shadowtbl);
 | 
| 
jbe@154
 | 
   791     lua_pushvalue(L, 1);
 | 
| 
jbe@154
 | 
   792     lua_rawget(L, -2);
 | 
| 
jbe@154
 | 
   793     if (!lua_isnil(L, -1)) lua_replace(L, 1);
 | 
| 
jbe@154
 | 
   794     lua_settop(L, 1);
 | 
| 
jbe@154
 | 
   795     if (tabletype == JSON_TABLETYPE_UNKNOWN) {
 | 
| 
jbe@154
 | 
   796       for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
 | 
| 
jbe@154
 | 
   797         luatype = lua_type(L, -2);
 | 
| 
jbe@154
 | 
   798         if (tabletype == JSON_TABLETYPE_UNKNOWN) {
 | 
| 
jbe@154
 | 
   799           if (luatype == LUA_TSTRING) tabletype = JSON_TABLETYPE_OBJECT;
 | 
| 
jbe@154
 | 
   800           else if (luatype == LUA_TNUMBER) tabletype = JSON_TABLETYPE_ARRAY;
 | 
| 
jbe@154
 | 
   801         } else if (
 | 
| 
jbe@154
 | 
   802           (tabletype == JSON_TABLETYPE_OBJECT && luatype == LUA_TNUMBER) ||
 | 
| 
jbe@154
 | 
   803           (tabletype == JSON_TABLETYPE_ARRAY && luatype == LUA_TSTRING)
 | 
| 
jbe@154
 | 
   804         ) {
 | 
| 
jbe@154
 | 
   805           goto json_export_tabletype_error;
 | 
| 
jbe@154
 | 
   806         }
 | 
| 
jbe@154
 | 
   807       }
 | 
| 
jbe@154
 | 
   808     }
 | 
| 
jbe@154
 | 
   809     switch (tabletype) {
 | 
| 
jbe@154
 | 
   810     case JSON_TABLETYPE_OBJECT:
 | 
| 
jbe@154
 | 
   811       lua_settop(L, 3);
 | 
| 
jbe@154
 | 
   812       luaL_buffinit(L, &buf);
 | 
| 
jbe@154
 | 
   813       luaL_addchar(&buf, '{');
 | 
| 
jbe@154
 | 
   814       for (lua_pushnil(L); lua_next(L, 1); ) {
 | 
| 
jbe@154
 | 
   815         if (lua_type(L, -2) == LUA_TSTRING) {
 | 
| 
jbe@154
 | 
   816           lua_replace(L, 3);
 | 
| 
jbe@154
 | 
   817           lua_replace(L, 2);
 | 
| 
jbe@154
 | 
   818           if (needsep) luaL_addchar(&buf, ',');
 | 
| 
jbe@154
 | 
   819           else needsep = 1;
 | 
| 
jbe@154
 | 
   820           lua_pushcfunction(L, json_export);
 | 
| 
jbe@154
 | 
   821           lua_pushvalue(L, 2);
 | 
| 
jbe@154
 | 
   822           lua_call(L, 1, 1);
 | 
| 
jbe@154
 | 
   823           luaL_addvalue(&buf);
 | 
| 
jbe@154
 | 
   824           luaL_addchar(&buf, ':');
 | 
| 
jbe@155
 | 
   825           if (json_isnullmark(L, 3)) {
 | 
| 
jbe@154
 | 
   826             luaL_addstring(&buf, "null");
 | 
| 
jbe@154
 | 
   827           } else {
 | 
| 
jbe@154
 | 
   828             lua_pushcfunction(L, json_export);
 | 
| 
jbe@154
 | 
   829             lua_pushvalue(L, 3);
 | 
| 
jbe@154
 | 
   830             lua_call(L, 1, 1);
 | 
| 
jbe@154
 | 
   831             luaL_addvalue(&buf);
 | 
| 
jbe@154
 | 
   832           }
 | 
| 
jbe@154
 | 
   833           lua_pushvalue(L, 2);
 | 
| 
jbe@154
 | 
   834         } else {
 | 
| 
jbe@154
 | 
   835           lua_pop(L, 1);
 | 
| 
jbe@154
 | 
   836         }
 | 
| 
jbe@154
 | 
   837       }
 | 
| 
jbe@154
 | 
   838       luaL_addchar(&buf, '}');
 | 
| 
jbe@154
 | 
   839       luaL_pushresult(&buf);
 | 
| 
jbe@154
 | 
   840       return 1;
 | 
| 
jbe@154
 | 
   841     case JSON_TABLETYPE_ARRAY:
 | 
| 
jbe@154
 | 
   842       lua_settop(L, 2);
 | 
| 
jbe@154
 | 
   843       luaL_buffinit(L, &buf);
 | 
| 
jbe@154
 | 
   844       luaL_addchar(&buf, '[');
 | 
| 
jbe@154
 | 
   845       for (idx = 1; ; idx++) {
 | 
| 
jbe@154
 | 
   846         lua_rawgeti(L, 1, idx);
 | 
| 
jbe@154
 | 
   847         if (lua_isnil(L, -1)) {
 | 
| 
jbe@154
 | 
   848           lua_pop(L, 1);
 | 
| 
jbe@154
 | 
   849           break;
 | 
| 
jbe@154
 | 
   850         }
 | 
| 
jbe@154
 | 
   851         lua_replace(L, 2);
 | 
| 
jbe@154
 | 
   852         if (needsep) luaL_addchar(&buf, ',');
 | 
| 
jbe@154
 | 
   853         else needsep = 1;
 | 
| 
jbe@154
 | 
   854         lua_pushcfunction(L, json_export);
 | 
| 
jbe@154
 | 
   855         lua_pushvalue(L, 2);
 | 
| 
jbe@154
 | 
   856         lua_call(L, 1, 1);
 | 
| 
jbe@154
 | 
   857         luaL_addvalue(&buf);
 | 
| 
jbe@154
 | 
   858       }
 | 
| 
jbe@154
 | 
   859       luaL_addchar(&buf, ']');
 | 
| 
jbe@154
 | 
   860       luaL_pushresult(&buf);
 | 
| 
jbe@154
 | 
   861       return 1;
 | 
| 
jbe@154
 | 
   862     }
 | 
| 
jbe@154
 | 
   863     json_export_tabletype_error:
 | 
| 
jbe@154
 | 
   864     return luaL_error(L, "JSON export not possible for ambiguous table (cannot decide whether it is an object or array)");
 | 
| 
jbe@154
 | 
   865   }
 | 
| 
jbe@154
 | 
   866   return luaL_error(L, "JSON export not possible for values of type \"%s\"", lua_typename(L, lua_type(L, 1)));
 | 
| 
jbe@154
 | 
   867 }
 | 
| 
jbe@154
 | 
   868 
 | 
| 
jbe@149
 | 
   869 // functions in library module:
 | 
| 
jbe@121
 | 
   870 static const struct luaL_Reg json_module_functions[] = {
 | 
| 
jbe@133
 | 
   871   {"object", json_object},
 | 
| 
jbe@133
 | 
   872   {"array", json_array},
 | 
| 
jbe@121
 | 
   873   {"import", json_import},
 | 
| 
jbe@154
 | 
   874   {"export", json_export},
 | 
| 
jbe@130
 | 
   875   {"get", json_get},
 | 
| 
jbe@127
 | 
   876   {"type", json_type},
 | 
| 
jbe@123
 | 
   877   {"isnull", json_isnull},
 | 
| 
jbe@131
 | 
   878   {"setnull", json_setnull},
 | 
| 
jbe@121
 | 
   879   {NULL, NULL}
 | 
| 
jbe@121
 | 
   880 };
 | 
| 
jbe@121
 | 
   881 
 | 
| 
jbe@149
 | 
   882 // metamethods for JSON objects, JSON arrays, and unknown JSON collections (object or array):
 | 
| 
jbe@126
 | 
   883 static const struct luaL_Reg json_metatable_functions[] = {
 | 
| 
jbe@130
 | 
   884   {"__len", json_len},
 | 
| 
jbe@130
 | 
   885   {"__index", json_index},
 | 
| 
jbe@130
 | 
   886   {"__newindex", json_newindex},
 | 
| 
jbe@135
 | 
   887   {"__pairs", json_pairs},
 | 
| 
jbe@134
 | 
   888   {"__ipairs", json_ipairs},
 | 
| 
jbe@126
 | 
   889   {NULL, NULL}
 | 
| 
jbe@126
 | 
   890 };
 | 
| 
jbe@126
 | 
   891 
 | 
| 
jbe@149
 | 
   892 // initializes json library:
 | 
| 
jbe@121
 | 
   893 int luaopen_json(lua_State *L) {
 | 
| 
jbe@149
 | 
   894   // empty stack:
 | 
| 
jbe@126
 | 
   895   lua_settop(L, 0);
 | 
| 
jbe@149
 | 
   896   // push library module onto stack position 1:
 | 
| 
jbe@149
 | 
   897   lua_newtable(L);
 | 
| 
jbe@149
 | 
   898   // register library functions:
 | 
| 
jbe@149
 | 
   899   luaL_setfuncs(L, json_module_functions, 0);
 | 
| 
jbe@149
 | 
   900   // create and store unknownmt:
 | 
| 
jbe@138
 | 
   901   lua_newtable(L);
 | 
| 
jbe@138
 | 
   902   luaL_setfuncs(L, json_metatable_functions, 0);
 | 
| 
jbe@144
 | 
   903   json_regstore(L, unknownmt);
 | 
| 
jbe@149
 | 
   904   // create and store objectmt:
 | 
| 
jbe@138
 | 
   905   lua_newtable(L);
 | 
| 
jbe@138
 | 
   906   luaL_setfuncs(L, json_metatable_functions, 0);
 | 
| 
jbe@144
 | 
   907   json_regstore(L, objectmt);
 | 
| 
jbe@149
 | 
   908   // create and store arraymt:
 | 
| 
jbe@138
 | 
   909   lua_newtable(L);
 | 
| 
jbe@138
 | 
   910   luaL_setfuncs(L, json_metatable_functions, 0);
 | 
| 
jbe@144
 | 
   911   json_regstore(L, arraymt);
 | 
| 
jbe@149
 | 
   912   // create and store ephemeron table to store shadow tables for each JSON object/array
 | 
| 
jbe@149
 | 
   913   // to allow NULL values returned as nil
 | 
| 
jbe@149
 | 
   914   lua_newtable(L);
 | 
| 
jbe@138
 | 
   915   lua_newtable(L);  // metatable for ephemeron table
 | 
| 
jbe@121
 | 
   916   lua_pushliteral(L, "__mode");
 | 
| 
jbe@121
 | 
   917   lua_pushliteral(L, "k");
 | 
| 
jbe@138
 | 
   918   lua_rawset(L, -3);
 | 
| 
jbe@138
 | 
   919   lua_setmetatable(L, -2);
 | 
| 
jbe@144
 | 
   920   json_regstore(L, shadowtbl);
 | 
| 
jbe@149
 | 
   921   // return library module stored on lowest stack position:
 | 
| 
jbe@138
 | 
   922   lua_settop(L, 1);
 | 
| 
jbe@121
 | 
   923   return 1;
 | 
| 
jbe@121
 | 
   924 }
 |