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