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