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