| 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 objectmt;   // metatable for JSON objects | 
| jbe@145 | 35   JSON_REGENT arraymt;    // metatable for JSON arrays | 
| jbe@144 | 36 } json_registry; | 
| jbe@138 | 37 | 
| jbe@157 | 38 // returns the string "<JSON null marker>": | 
| jbe@157 | 39 static int json_nullmark_tostring(lua_State *L) { | 
| jbe@157 | 40   lua_pushliteral(L, "<JSON null marker>"); | 
| jbe@157 | 41   return 1; | 
| jbe@157 | 42 } | 
| jbe@157 | 43 | 
| jbe@169 | 44 #define json_convert_source_idx 1 | 
| jbe@169 | 45 #define json_convert_iterator_idx 2 | 
| jbe@169 | 46 #define json_convert_output_idx 3 | 
| jbe@169 | 47 #define json_convert_shadow_idx 4 | 
| jbe@172 | 48 #define json_convert_iterfun_idx 5 | 
| jbe@172 | 49 #define json_convert_itertbl_idx 6 | 
| jbe@169 | 50 | 
| jbe@169 | 51 // converts a Lua table to a  JSON object or JSON array: | 
| jbe@169 | 52 // (does never modify the argument, returns an empty object or array if argument is nil) | 
| jbe@169 | 53 static int json_convert(lua_State *L, int array) { | 
| jbe@171 | 54   int arrayidx = 0; | 
| jbe@171 | 55   // determine is argument is given: | 
| jbe@169 | 56   if (lua_isnoneornil(L, json_convert_source_idx)) { | 
| jbe@171 | 57     // if no argument is given (or if argument is nil), | 
| jbe@171 | 58     // create table with shadow table, and leave first table on top of stack: | 
| jbe@144 | 59     json_regfetch(L, shadowtbl); | 
| jbe@169 | 60     lua_newtable(L); | 
| jbe@169 | 61     lua_pushvalue(L, -1); | 
| jbe@169 | 62     lua_newtable(L); | 
| jbe@169 | 63     lua_rawset(L, -4); | 
| jbe@169 | 64   } else { | 
| jbe@171 | 65     // if an argument was given, | 
| jbe@171 | 66     // push its iterator function on stack position 2 if existent, | 
| jbe@169 | 67     // else push null for normal tables: | 
| jbe@171 | 68     lua_settop(L, 1); | 
| jbe@169 | 69     if (lua_getmetatable(L, json_convert_source_idx)) { | 
| jbe@169 | 70       lua_getfield(L, -1, array ? "__ipairs" : "__pairs"); | 
| jbe@169 | 71       if (lua_isnil(L, -1)) luaL_checktype(L, 1, LUA_TTABLE); | 
| jbe@169 | 72       else if (lua_type(L, -1) != LUA_TFUNCTION) | 
| jbe@169 | 73         return luaL_error(L, "%s metamethod is not a function", array ? "__ipairs" : "__pairs"); | 
| jbe@169 | 74       lua_replace(L, -2); | 
| jbe@169 | 75     } else { | 
| jbe@169 | 76       lua_pushnil(L); | 
| jbe@143 | 77     } | 
| jbe@171 | 78     // create result table on stack position 3: | 
| jbe@169 | 79     lua_newtable(L); | 
| jbe@169 | 80     // create shadow table on stack position 4: | 
| jbe@169 | 81     json_regfetch(L, shadowtbl); | 
| jbe@169 | 82     lua_newtable(L); | 
| jbe@169 | 83     lua_pushvalue(L, json_convert_output_idx); | 
| jbe@169 | 84     lua_pushvalue(L, -2); | 
| jbe@169 | 85     lua_rawset(L, -4); | 
| jbe@169 | 86     lua_replace(L, -2); | 
| jbe@171 | 87     // check if iterator function exists: | 
| jbe@169 | 88     if (lua_isnil(L, json_convert_iterator_idx)) { | 
| jbe@171 | 89       // if there is no iterator function, | 
| jbe@171 | 90       // distinguish between objects and arrays: | 
| jbe@171 | 91       if (array == 0) { | 
| jbe@171 | 92         // for an object, copy all string key value pairs to shadow table: | 
| jbe@171 | 93         for (lua_pushnil(L); lua_next(L, json_convert_source_idx); lua_pop(L, 1)) { | 
| jbe@171 | 94           if (lua_type(L, -2) == LUA_TSTRING) { | 
| jbe@171 | 95             lua_pushvalue(L, -2); | 
| jbe@171 | 96             lua_pushvalue(L, -2); | 
| jbe@171 | 97             lua_rawset(L, json_convert_shadow_idx); | 
| jbe@171 | 98           } | 
| jbe@171 | 99         } | 
| jbe@171 | 100       } else { | 
| jbe@171 | 101         // for an array, copy consecutive integer value pairs to shadow table: | 
| jbe@171 | 102         while (1) { | 
| jbe@171 | 103           // TODO: Lua 5.3 may support more elements | 
| jbe@171 | 104           if (arrayidx == INT_MAX) { | 
| jbe@171 | 105             lua_pushnumber(L, (size_t)INT_MAX+1); | 
| jbe@171 | 106             lua_rawget(L, json_convert_source_idx); | 
| jbe@171 | 107             if (lua_isnil(L, -1)) break; | 
| jbe@171 | 108             return luaL_error(L, "Array exceeded length of %d elements", INT_MAX); | 
| jbe@171 | 109           } | 
| jbe@171 | 110           arrayidx++; | 
| jbe@171 | 111           lua_rawgeti(L, json_convert_source_idx, arrayidx); | 
| jbe@171 | 112           if (lua_isnil(L, -1)) break; | 
| jbe@171 | 113           lua_rawseti(L, json_convert_shadow_idx, arrayidx); | 
| jbe@171 | 114         } | 
| jbe@169 | 115       } | 
| jbe@169 | 116     } else { | 
| jbe@172 | 117       // if there is an iterator function, | 
| jbe@172 | 118       // call iterator function with source value (first argument) | 
| jbe@172 | 119       // and store 3 result values on stack positions 5 through 7: | 
| jbe@172 | 120       lua_pushvalue(L, json_convert_iterator_idx); | 
| jbe@172 | 121       lua_pushvalue(L, 1); | 
| jbe@172 | 122       lua_call(L, 1, 3); | 
| jbe@174 | 123       // iterate through key value pairs and store them in shadow table | 
| jbe@174 | 124       // while replacing nil values with null-marker: | 
| jbe@172 | 125       while (1) { | 
| jbe@172 | 126         lua_pushvalue(L, json_convert_iterfun_idx); | 
| jbe@172 | 127         lua_pushvalue(L, json_convert_itertbl_idx); | 
| jbe@172 | 128         lua_pushvalue(L, -3); | 
| jbe@172 | 129         lua_remove(L, -4); | 
| jbe@172 | 130         lua_call(L, 2, 2); | 
| jbe@172 | 131         if (lua_isnil(L, -2)) break; | 
| jbe@172 | 132         if (lua_type(L, -2) == (array ? LUA_TNUMBER : LUA_TSTRING)) { | 
| jbe@172 | 133           lua_pushvalue(L, -2); | 
| jbe@174 | 134           if (lua_isnil(L, -2)) json_pushnullmark(L); | 
| jbe@174 | 135           else lua_pushvalue(L, -2); | 
| jbe@172 | 136           lua_rawset(L, json_convert_shadow_idx); | 
| jbe@172 | 137         } | 
| jbe@172 | 138         lua_pop(L, 1); | 
| jbe@172 | 139       } | 
| jbe@143 | 140     } | 
| jbe@171 | 141     // let result table be on top of stack: | 
| jbe@169 | 142     lua_settop(L, json_convert_output_idx); | 
| jbe@136 | 143   } | 
| jbe@171 | 144   // set metatable (for result table on top of stack): | 
| jbe@171 | 145   if (array == 0) json_regfetch(L, objectmt); | 
| jbe@171 | 146   else json_regfetch(L, arraymt); | 
| jbe@169 | 147   lua_setmetatable(L, -2); | 
| jbe@171 | 148   // return table on top of stack: | 
| jbe@136 | 149   return 1; | 
| jbe@136 | 150 } | 
| jbe@136 | 151 | 
| jbe@136 | 152 static int json_object(lua_State *L) { | 
| jbe@169 | 153   return json_convert(L, 0); | 
| jbe@136 | 154 } | 
| jbe@136 | 155 | 
| jbe@136 | 156 static int json_array(lua_State *L) { | 
| jbe@169 | 157   return json_convert(L, 1); | 
| jbe@136 | 158 } | 
| jbe@136 | 159 | 
| jbe@145 | 160 // internal states of JSON parser: | 
| jbe@124 | 161 #define JSON_STATE_VALUE 0 | 
| jbe@124 | 162 #define JSON_STATE_OBJECT_KEY 1 | 
| jbe@124 | 163 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2 | 
| jbe@124 | 164 #define JSON_STATE_OBJECT_VALUE 3 | 
| jbe@124 | 165 #define JSON_STATE_OBJECT_SEPARATOR 4 | 
| jbe@124 | 166 #define JSON_STATE_ARRAY_VALUE 5 | 
| jbe@124 | 167 #define JSON_STATE_ARRAY_SEPARATOR 6 | 
| jbe@124 | 168 #define JSON_STATE_END 7 | 
| jbe@121 | 169 | 
| jbe@145 | 170 // special Lua stack indicies for json_import function: | 
| jbe@138 | 171 #define json_import_objectmt_idx 2 | 
| jbe@138 | 172 #define json_import_arraymt_idx 3 | 
| jbe@138 | 173 #define json_import_shadowtbl_idx 4 | 
| jbe@138 | 174 | 
| jbe@168 | 175 // macros for hex decoding: | 
| jbe@168 | 176 #define json_utf16_surrogate(x) ((x) >= 0xD800 && (x) <= 0xDFFF) | 
| jbe@168 | 177 #define json_utf16_lead(x) ((x) >= 0xD800 && (x) <= 0xDBFF) | 
| jbe@168 | 178 #define json_utf16_tail(x) ((x) >= 0xDC00 && (x) <= 0xDFFF) | 
| jbe@167 | 179 #define json_import_readhex(x) \ | 
| jbe@167 | 180   do { \ | 
| jbe@167 | 181     x = 0; \ | 
| jbe@167 | 182     for (i=0; i<4; i++) { \ | 
| jbe@167 | 183       x <<= 4; \ | 
| jbe@167 | 184       c = str[pos++]; \ | 
| jbe@167 | 185       if (c >= '0' && c <= '9') x += c - '0'; \ | 
| jbe@167 | 186       else if (c >= 'A' && c <= 'F') x += c - 'A' + 10; \ | 
| jbe@167 | 187       else if (c >= 'a' && c <= 'f') x += c - 'a' + 10; \ | 
| jbe@167 | 188       else if (c == 0) goto json_import_unexpected_eof; \ | 
| jbe@167 | 189       else goto json_import_unexpected_escape; \ | 
| jbe@167 | 190     } \ | 
| jbe@167 | 191   } while (0) | 
| jbe@167 | 192 | 
| jbe@136 | 193 // decodes a JSON document: | 
| jbe@121 | 194 static int json_import(lua_State *L) { | 
| jbe@167 | 195   int i;             // loop variable | 
| jbe@136 | 196   const char *str;   // string to parse | 
| jbe@136 | 197   size_t total;      // total length of string to parse | 
| jbe@136 | 198   size_t pos = 0;    // current position in string to parse | 
| jbe@136 | 199   size_t level = 0;  // nested levels of objects/arrays currently being processed | 
| jbe@145 | 200   int mode = JSON_STATE_VALUE;  // state of parser (i.e. "what's expected next?") | 
| jbe@170 | 201   unsigned char c;     // variable to store a single character to be processed (unsigned!) | 
| jbe@145 | 202   luaL_Buffer luabuf;  // Lua buffer to decode JSON string values | 
| jbe@145 | 203   char *cbuf;          // C buffer to decode JSON string values | 
| jbe@162 | 204   size_t outlen;       // maximum length or write position of C buffer | 
| jbe@167 | 205   long codepoint;      // decoded UTF-16 character or higher codepoint | 
| jbe@167 | 206   long utf16tail;      // second decoded UTF-16 character (surrogate tail) | 
| jbe@152 | 207   size_t arraylen;     // variable to temporarily store the array length | 
| jbe@166 | 208   // require string as argument and convert to C string with length information: | 
| jbe@166 | 209   str = luaL_checklstring(L, 1, &total); | 
| jbe@166 | 210   // if string contains a NULL byte, this is a syntax error | 
| jbe@166 | 211   if (strlen(str) != total) goto json_import_syntax_error; | 
| jbe@147 | 212   // stack shall contain one function argument: | 
| jbe@138 | 213   lua_settop(L, 1); | 
| jbe@147 | 214   // push objectmt onto stack position 2: | 
| jbe@144 | 215   json_regfetch(L, objectmt); | 
| jbe@147 | 216   // push arraymt onto stack position 3: | 
| jbe@144 | 217   json_regfetch(L, arraymt); | 
| jbe@147 | 218   // push shadowtbl onto stack position 4: | 
| jbe@144 | 219   json_regfetch(L, shadowtbl); | 
| jbe@136 | 220   // main loop of parser: | 
| jbe@136 | 221   json_import_loop: | 
| jbe@136 | 222   // skip whitespace and store next character in variable 'c': | 
| jbe@146 | 223   while (c = str[pos], | 
| jbe@146 | 224     c == ' ' || | 
| jbe@146 | 225     c == '\f' || | 
| jbe@146 | 226     c == '\n' || | 
| jbe@146 | 227     c == '\r' || | 
| jbe@146 | 228     c == '\t' || | 
| jbe@146 | 229     c == '\v' | 
| jbe@146 | 230   ) pos++; | 
| jbe@170 | 231   // NOTE: variable c needs to be unsigned in the following code | 
| jbe@136 | 232   // switch statement to handle certain (single) characters: | 
| jbe@121 | 233   switch (c) { | 
| jbe@136 | 234   // handle end of JSON document: | 
| jbe@121 | 235   case 0: | 
| jbe@136 | 236     // if end of JSON document was expected, then return top element of stack as result: | 
| jbe@124 | 237     if (mode == JSON_STATE_END) return 1; | 
| jbe@136 | 238     // otherwise, the JSON document was malformed: | 
| jbe@167 | 239     if (level == 0) { | 
| jbe@167 | 240       lua_pushnil(L); | 
| jbe@167 | 241       lua_pushliteral(L, "Empty string"); | 
| jbe@167 | 242     } else { | 
| jbe@167 | 243       json_import_unexpected_eof: | 
| jbe@167 | 244       lua_pushnil(L); | 
| jbe@167 | 245       lua_pushliteral(L, "Unexpected end of JSON document"); | 
| jbe@167 | 246     } | 
| jbe@121 | 247     return 2; | 
| jbe@136 | 248   // new JSON object: | 
| jbe@121 | 249   case '{': | 
| jbe@136 | 250     // if a JSON object is not expected here, then return an error: | 
| jbe@146 | 251     if ( | 
| jbe@146 | 252       mode != JSON_STATE_VALUE && | 
| jbe@146 | 253       mode != JSON_STATE_OBJECT_VALUE && | 
| jbe@146 | 254       mode != JSON_STATE_ARRAY_VALUE | 
| jbe@146 | 255     ) goto json_import_syntax_error; | 
| jbe@136 | 256     // create JSON object on stack: | 
| jbe@136 | 257     lua_newtable(L); | 
| jbe@136 | 258     // set metatable of JSON object: | 
| jbe@138 | 259     lua_pushvalue(L, json_import_objectmt_idx); | 
| jbe@125 | 260     lua_setmetatable(L, -2); | 
| jbe@136 | 261     // create internal shadow table on stack: | 
| jbe@136 | 262     lua_newtable(L); | 
| jbe@146 | 263     // register internal shadow table: | 
| jbe@123 | 264     lua_pushvalue(L, -2); | 
| jbe@123 | 265     lua_pushvalue(L, -2); | 
| jbe@138 | 266     lua_rawset(L, json_import_shadowtbl_idx); | 
| jbe@146 | 267     // expect object key (or end of object) to follow: | 
| jbe@136 | 268     mode = JSON_STATE_OBJECT_KEY; | 
| jbe@146 | 269     // jump to common code for opening JSON object and JSON array: | 
| jbe@142 | 270     goto json_import_open; | 
| jbe@136 | 271   // new JSON array: | 
| jbe@121 | 272   case '[': | 
| jbe@136 | 273     // if a JSON array is not expected here, then return an error: | 
| jbe@146 | 274     if ( | 
| jbe@146 | 275       mode != JSON_STATE_VALUE && | 
| jbe@146 | 276       mode != JSON_STATE_OBJECT_VALUE && | 
| jbe@146 | 277       mode != JSON_STATE_ARRAY_VALUE | 
| jbe@146 | 278     ) goto json_import_syntax_error; | 
| jbe@136 | 279     // create JSON array on stack: | 
| jbe@136 | 280     lua_newtable(L); | 
| jbe@136 | 281     // set metatable of JSON array: | 
| jbe@138 | 282     lua_pushvalue(L, json_import_arraymt_idx); | 
| jbe@125 | 283     lua_setmetatable(L, -2); | 
| jbe@136 | 284     // create internal shadow table on stack: | 
| jbe@136 | 285     lua_newtable(L); | 
| jbe@146 | 286     // register internal shadow table: | 
| jbe@123 | 287     lua_pushvalue(L, -2); | 
| jbe@123 | 288     lua_pushvalue(L, -2); | 
| jbe@138 | 289     lua_rawset(L, json_import_shadowtbl_idx); | 
| jbe@140 | 290     // add nil as key (needed to keep stack balance) and as magic to detect arrays: | 
| jbe@140 | 291     lua_pushnil(L); | 
| jbe@146 | 292     // expect array value (or end of array) to follow: | 
| jbe@142 | 293     mode = JSON_STATE_ARRAY_VALUE; | 
| jbe@142 | 294     // continue with common code for opening JSON object and JSON array: | 
| jbe@146 | 295   // common code for opening JSON object or JSON array: | 
| jbe@142 | 296   json_import_open: | 
| jbe@142 | 297     // limit nested levels: | 
| jbe@142 | 298     if (level >= JSON_MAXDEPTH) { | 
| jbe@142 | 299       lua_pushnil(L); | 
| jbe@164 | 300       lua_pushfstring(L, "More than %d nested JSON levels", JSON_MAXDEPTH); | 
| jbe@142 | 301       return 2; | 
| jbe@142 | 302     } | 
| jbe@142 | 303     // additional buffer overflow protection: | 
| jbe@142 | 304     if (!lua_checkstack(L, LUA_MINSTACK)) | 
| jbe@142 | 305       return luaL_error(L, "Caught stack overflow in JSON import function (too many nested levels and stack size too small)"); | 
| jbe@136 | 306     // increment level: | 
| jbe@121 | 307     level++; | 
| jbe@142 | 308     // consume input character: | 
| jbe@142 | 309     pos++; | 
| jbe@121 | 310     goto json_import_loop; | 
| jbe@136 | 311   // end of JSON object: | 
| jbe@121 | 312   case '}': | 
| jbe@136 | 313     // if end of JSON object is not expected here, then return an error: | 
| jbe@146 | 314     if ( | 
| jbe@146 | 315       mode != JSON_STATE_OBJECT_KEY && | 
| jbe@146 | 316       mode != JSON_STATE_OBJECT_SEPARATOR | 
| jbe@146 | 317     ) goto json_import_syntax_error; | 
| jbe@136 | 318     // jump to common code for end of JSON object and JSON array: | 
| jbe@121 | 319     goto json_import_close; | 
| jbe@136 | 320   // end of JSON array: | 
| jbe@121 | 321   case ']': | 
| jbe@136 | 322     // if end of JSON array is not expected here, then return an error: | 
| jbe@146 | 323     if ( | 
| jbe@146 | 324       mode != JSON_STATE_ARRAY_VALUE && | 
| jbe@146 | 325       mode != JSON_STATE_ARRAY_SEPARATOR | 
| jbe@146 | 326     ) goto json_import_syntax_error; | 
| jbe@146 | 327     // pop nil key/magic (that was needed to keep stack balance): | 
| jbe@140 | 328     lua_pop(L, 1); | 
| jbe@136 | 329     // continue with common code for end of JSON object and JSON array: | 
| jbe@136 | 330   // common code for end of JSON object or JSON array: | 
| jbe@121 | 331   json_import_close: | 
| jbe@136 | 332     // consume input character: | 
| jbe@121 | 333     pos++; | 
| jbe@136 | 334     // pop shadow table: | 
| jbe@136 | 335     lua_pop(L, 1); | 
| jbe@136 | 336     // check if nested: | 
| jbe@121 | 337     if (--level) { | 
| jbe@146 | 338       // if nested, | 
| jbe@146 | 339       // check if outer(!) structure is an array or object: | 
| jbe@140 | 340       if (lua_isnil(L, -2)) { | 
| jbe@136 | 341         // select array value processing: | 
| jbe@124 | 342         mode = JSON_STATE_ARRAY_VALUE; | 
| jbe@121 | 343       } else { | 
| jbe@136 | 344         // select object value processing: | 
| jbe@124 | 345         mode = JSON_STATE_OBJECT_VALUE; | 
| jbe@121 | 346       } | 
| jbe@136 | 347       // store value in outer structure: | 
| jbe@121 | 348       goto json_import_process_value; | 
| jbe@121 | 349     } | 
| jbe@136 | 350     // if not nested, then expect end of JSON document and continue with loop: | 
| jbe@136 | 351     mode = JSON_STATE_END; | 
| jbe@121 | 352     goto json_import_loop; | 
| jbe@136 | 353   // key terminator: | 
| jbe@121 | 354   case ':': | 
| jbe@136 | 355     // if key terminator is not expected here, then return an error: | 
| jbe@124 | 356     if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR) | 
| jbe@121 | 357       goto json_import_syntax_error; | 
| jbe@136 | 358     // consume input character: | 
| jbe@121 | 359     pos++; | 
| jbe@146 | 360     // expect object value to follow: | 
| jbe@124 | 361     mode = JSON_STATE_OBJECT_VALUE; | 
| jbe@146 | 362     // continue with loop: | 
| jbe@121 | 363     goto json_import_loop; | 
| jbe@136 | 364   // value terminator (NOTE: trailing comma at end of value or key-value list is tolerated by this parser) | 
| jbe@121 | 365   case ',': | 
| jbe@146 | 366     // branch according to parser state: | 
| jbe@124 | 367     if (mode == JSON_STATE_OBJECT_SEPARATOR) { | 
| jbe@146 | 368       // expect an object key to follow: | 
| jbe@124 | 369       mode = JSON_STATE_OBJECT_KEY; | 
| jbe@124 | 370     } else if (mode == JSON_STATE_ARRAY_SEPARATOR) { | 
| jbe@146 | 371       // expect an array value to follow: | 
| jbe@124 | 372       mode = JSON_STATE_ARRAY_VALUE; | 
| jbe@121 | 373     } else { | 
| jbe@136 | 374        // if value terminator is not expected here, then return an error: | 
| jbe@136 | 375        goto json_import_syntax_error; | 
| jbe@121 | 376     } | 
| jbe@136 | 377     // consume input character: | 
| jbe@121 | 378     pos++; | 
| jbe@136 | 379     // continue with loop: | 
| jbe@121 | 380     goto json_import_loop; | 
| jbe@136 | 381   // string literal: | 
| jbe@121 | 382   case '"': | 
| jbe@146 | 383     // consume quote character: | 
| jbe@146 | 384     pos++; | 
| jbe@162 | 385     // find last character in input string: | 
| jbe@162 | 386     outlen = pos; | 
| jbe@162 | 387     while ((c = str[outlen]) != '"') { | 
| jbe@161 | 388       // consume one character: | 
| jbe@162 | 389       outlen++; | 
| jbe@161 | 390       // handle unexpected end of JSON document: | 
| jbe@161 | 391       if (c == 0) goto json_import_unexpected_eof; | 
| jbe@161 | 392       // consume one extra character when encountering an escaped quote: | 
| jbe@162 | 393       else if (c == '\\' && str[outlen] == '"') outlen++; | 
| jbe@161 | 394     } | 
| jbe@162 | 395     // determine buffer length: | 
| jbe@162 | 396     outlen -= pos; | 
| jbe@161 | 397     // check if string is non empty: | 
| jbe@162 | 398     if (outlen) { | 
| jbe@161 | 399       // prepare buffer to decode string (with maximum possible length) and set write position to zero: | 
| jbe@162 | 400       cbuf = luaL_buffinitsize(L, &luabuf, outlen); | 
| jbe@162 | 401       outlen = 0; | 
| jbe@161 | 402       // loop through the characters until encountering end quote: | 
| jbe@161 | 403       while ((c = str[pos++]) != '"') { | 
| jbe@162 | 404         // NOTE: unexpected end cannot happen anymore | 
| jbe@162 | 405         if (c < 32 || c == 127) { | 
| jbe@161 | 406           // do not allow ASCII control characters: | 
| jbe@161 | 407           // NOTE: illegal UTF-8 sequences and extended control characters are not sanitized | 
| jbe@161 | 408           //       by this parser to allow different encodings than Unicode | 
| jbe@161 | 409           lua_pushnil(L); | 
| jbe@161 | 410           lua_pushliteral(L, "Unexpected control character in JSON string"); | 
| jbe@161 | 411           return 2; | 
| jbe@161 | 412         } else if (c == '\\') { | 
| jbe@161 | 413           // read next char after backslash escape: | 
| jbe@161 | 414           c = str[pos++]; | 
| jbe@161 | 415           switch (c) { | 
| jbe@161 | 416           // unexpected end-of-string: | 
| jbe@161 | 417           case 0: | 
| jbe@161 | 418             goto json_import_unexpected_eof; | 
| jbe@161 | 419           // unescaping of quotation mark, slash, and backslash: | 
| jbe@161 | 420           case '"': | 
| jbe@161 | 421           case '/': | 
| jbe@161 | 422           case '\\': | 
| jbe@162 | 423             cbuf[outlen++] = c; | 
| jbe@161 | 424             break; | 
| jbe@161 | 425           // unescaping of backspace: | 
| jbe@162 | 426           case 'b': cbuf[outlen++] = '\b'; break; | 
| jbe@161 | 427           // unescaping of form-feed: | 
| jbe@162 | 428           case 'f': cbuf[outlen++] = '\f'; break; | 
| jbe@161 | 429           // unescaping of new-line: | 
| jbe@162 | 430           case 'n': cbuf[outlen++] = '\n'; break; | 
| jbe@161 | 431           // unescaping of carriage-return: | 
| jbe@162 | 432           case 'r': cbuf[outlen++] = '\r'; break; | 
| jbe@161 | 433           // unescaping of tabulator: | 
| jbe@162 | 434           case 't': cbuf[outlen++] = '\t'; break; | 
| jbe@161 | 435           // unescaping of UTF-16 characters | 
| jbe@161 | 436           case 'u': | 
| jbe@167 | 437             // decode 4 hex nibbles: | 
| jbe@167 | 438             json_import_readhex(codepoint); | 
| jbe@167 | 439             // handle surrogate character: | 
| jbe@167 | 440             if (json_utf16_surrogate(codepoint)) { | 
| jbe@167 | 441               // check if first surrogate is in valid range: | 
| jbe@167 | 442               if (json_utf16_lead(codepoint)) { | 
| jbe@167 | 443                 // require second surrogate: | 
| jbe@167 | 444                 if ((c = str[pos++]) != '\\' || (c = str[pos++]) != 'u') { | 
| jbe@167 | 445                   if (c == 0) goto json_import_unexpected_eof; | 
| jbe@167 | 446                   else goto json_import_wrong_surrogate; | 
| jbe@167 | 447                 } | 
| jbe@167 | 448                 // read 4 hex nibbles of second surrogate character: | 
| jbe@167 | 449                 json_import_readhex(utf16tail); | 
| jbe@167 | 450                 // check if second surrogate is in valid range: | 
| jbe@167 | 451                 if (!json_utf16_tail(utf16tail)) goto json_import_wrong_surrogate; | 
| jbe@167 | 452                 // calculate codepoint: | 
| jbe@167 | 453                 codepoint = 0x10000 + (utf16tail - 0xDC00) + (codepoint - 0xD800) * 0x400; | 
| jbe@167 | 454               } else { | 
| jbe@167 | 455                 // throw error for wrong surrogates: | 
| jbe@167 | 456                 json_import_wrong_surrogate: | 
| jbe@167 | 457                 lua_pushnil(L); | 
| jbe@167 | 458                 lua_pushliteral(L, "Illegal UTF-16 surrogate in JSON string escape sequence"); | 
| jbe@167 | 459                 return 2; | 
| jbe@167 | 460               } | 
| jbe@167 | 461             } | 
| jbe@167 | 462             // encode as UTF-8: | 
| jbe@167 | 463             if (codepoint < 0x80) { | 
| jbe@167 | 464               cbuf[outlen++] = (char)codepoint; | 
| jbe@167 | 465             } else if (codepoint < 0x800) { | 
| jbe@167 | 466               cbuf[outlen++] = (char)(0xc0 | (codepoint >> 6)); | 
| jbe@167 | 467               cbuf[outlen++] = (char)(0x80 | (codepoint & 0x3f)); | 
| jbe@167 | 468             } else if (codepoint < 0x10000) { | 
| jbe@167 | 469               cbuf[outlen++] = (char)(0xe0 | (codepoint >> 12)); | 
| jbe@167 | 470               cbuf[outlen++] = (char)(0x80 | ((codepoint >> 6) & 0x3f)); | 
| jbe@167 | 471               cbuf[outlen++] = (char)(0x80 | (codepoint & 0x3f)); | 
| jbe@167 | 472             } else { | 
| jbe@167 | 473               cbuf[outlen++] = (char)(0xf0 | (codepoint >> 18)); | 
| jbe@167 | 474               cbuf[outlen++] = (char)(0x80 | ((codepoint >> 12) & 0x3f)); | 
| jbe@167 | 475               cbuf[outlen++] = (char)(0x80 | ((codepoint >> 6) & 0x3f)); | 
| jbe@167 | 476               cbuf[outlen++] = (char)(0x80 | (codepoint & 0x3f)); | 
| jbe@167 | 477             } | 
| jbe@167 | 478             break; | 
| jbe@161 | 479           // unexpected escape sequence: | 
| jbe@161 | 480           default: | 
| jbe@167 | 481             json_import_unexpected_escape: | 
| jbe@161 | 482             lua_pushnil(L); | 
| jbe@161 | 483             lua_pushliteral(L, "Unexpected string escape sequence in JSON document"); | 
| jbe@161 | 484             return 2; | 
| jbe@161 | 485           } | 
| jbe@161 | 486         } else { | 
| jbe@161 | 487           // normal character: | 
| jbe@162 | 488           cbuf[outlen++] = c; | 
| jbe@121 | 489         } | 
| jbe@121 | 490       } | 
| jbe@161 | 491       // process buffer to Lua string: | 
| jbe@162 | 492       luaL_pushresultsize(&luabuf, outlen); | 
| jbe@161 | 493     } else { | 
| jbe@161 | 494       // if JSON string is empty, | 
| jbe@161 | 495       // push empty Lua string: | 
| jbe@161 | 496       lua_pushliteral(L, ""); | 
| jbe@167 | 497       // consume closing quote: | 
| jbe@167 | 498       pos++; | 
| jbe@121 | 499     } | 
| jbe@136 | 500     // continue with processing of decoded string: | 
| jbe@121 | 501     goto json_import_process_value; | 
| jbe@121 | 502   } | 
| jbe@136 | 503   // process values whose type is is not deducible from a single character: | 
| jbe@136 | 504   if ((c >= '0' && c <= '9') || c == '-' || c == '+') { | 
| jbe@146 | 505     // for numbers, | 
| jbe@146 | 506     // use strtod() call to parse a (double precision) floating point number: | 
| jbe@167 | 507     double numval; | 
| jbe@122 | 508     char *endptr; | 
| jbe@122 | 509     numval = strtod(str+pos, &endptr); | 
| jbe@146 | 510     // catch parsing errors: | 
| jbe@122 | 511     if (endptr == str+pos) goto json_import_syntax_error; | 
| jbe@146 | 512     // consume characters that were parsed: | 
| jbe@122 | 513     pos += endptr - (str+pos); | 
| jbe@146 | 514     // push parsed (double precision) floating point number on Lua stack: | 
| jbe@122 | 515     lua_pushnumber(L, numval); | 
| jbe@122 | 516   } else if (!strncmp(str+pos, "true", 4)) { | 
| jbe@136 | 517     // consume 4 input characters for "true": | 
| jbe@121 | 518     pos += 4; | 
| jbe@147 | 519     // put Lua true value onto stack: | 
| jbe@136 | 520     lua_pushboolean(L, 1); | 
| jbe@121 | 521   } else if (!strncmp(str+pos, "false", 5)) { | 
| jbe@136 | 522     // consume 5 input characters for "false": | 
| jbe@121 | 523     pos += 5; | 
| jbe@147 | 524     // put Lua false value onto stack: | 
| jbe@136 | 525     lua_pushboolean(L, 0); | 
| jbe@121 | 526   } else if (!strncmp(str+pos, "null", 4)) { | 
| jbe@136 | 527     // consume 4 input characters for "null": | 
| jbe@136 | 528     pos += 4; | 
| jbe@153 | 529     // different behavor for top-level and sub-levels: | 
| jbe@153 | 530     if (level) { | 
| jbe@153 | 531       // if sub-level, | 
| jbe@153 | 532       // push special null-marker onto stack: | 
| jbe@155 | 533       json_pushnullmark(L); | 
| jbe@153 | 534     } else { | 
| jbe@153 | 535       // if top-level, | 
| jbe@153 | 536       // push nil onto stack: | 
| jbe@153 | 537       lua_pushnil(L); | 
| jbe@153 | 538     } | 
| jbe@121 | 539   } else { | 
| jbe@136 | 540     // all other cases are a syntax error: | 
| jbe@121 | 541     goto json_import_syntax_error; | 
| jbe@121 | 542   } | 
| jbe@136 | 543   // process a decoded value or key value pair (expected on top of Lua stack): | 
| jbe@136 | 544   json_import_process_value: | 
| jbe@121 | 545   switch (mode) { | 
| jbe@136 | 546   // an object key has been read: | 
| jbe@124 | 547   case JSON_STATE_OBJECT_KEY: | 
| jbe@136 | 548     // if an object key is not a string, then this is a syntax error: | 
| jbe@121 | 549     if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error; | 
| jbe@146 | 550     // expect key terminator to follow: | 
| jbe@124 | 551     mode = JSON_STATE_OBJECT_KEY_TERMINATOR; | 
| jbe@146 | 552     // continue with loop: | 
| jbe@121 | 553     goto json_import_loop; | 
| jbe@136 | 554   // a key value pair has been read: | 
| jbe@124 | 555   case JSON_STATE_OBJECT_VALUE: | 
| jbe@136 | 556     // store key value pair in outer shadow table: | 
| jbe@130 | 557     lua_rawset(L, -3); | 
| jbe@146 | 558     // expect value terminator (or end of object) to follow: | 
| jbe@124 | 559     mode = JSON_STATE_OBJECT_SEPARATOR; | 
| jbe@146 | 560     // continue with loop: | 
| jbe@121 | 561     goto json_import_loop; | 
| jbe@136 | 562   // an array value has been read: | 
| jbe@124 | 563   case JSON_STATE_ARRAY_VALUE: | 
| jbe@152 | 564     // get current array length: | 
| jbe@152 | 565     arraylen = lua_rawlen(L, -3); | 
| jbe@152 | 566     // throw error if array would exceed INT_MAX elements: | 
| jbe@152 | 567     // TODO: Lua 5.3 may support more elements | 
| jbe@152 | 568     if (arraylen >= INT_MAX) { | 
| jbe@152 | 569       lua_pushnil(L); | 
| jbe@152 | 570       lua_pushfstring(L, "Array exceeded length of %d elements", INT_MAX); | 
| jbe@152 | 571     } | 
| jbe@136 | 572     // store value in outer shadow table: | 
| jbe@152 | 573     lua_rawseti(L, -3, arraylen + 1); | 
| jbe@146 | 574     // expect value terminator (or end of object) to follow: | 
| jbe@124 | 575     mode = JSON_STATE_ARRAY_SEPARATOR; | 
| jbe@146 | 576     // continue with loop | 
| jbe@121 | 577     goto json_import_loop; | 
| jbe@136 | 578   // a single value has been read: | 
| jbe@124 | 579   case JSON_STATE_VALUE: | 
| jbe@136 | 580     // leave value on top of stack, expect end of JSON document, and continue with loop: | 
| jbe@124 | 581     mode = JSON_STATE_END; | 
| jbe@121 | 582     goto json_import_loop; | 
| jbe@121 | 583   } | 
| jbe@146 | 584   // syntax error handling (reachable by goto statement): | 
| jbe@136 | 585   json_import_syntax_error: | 
| jbe@121 | 586   lua_pushnil(L); | 
| jbe@121 | 587   lua_pushliteral(L, "Syntax error in JSON document"); | 
| jbe@121 | 588   return 2; | 
| jbe@121 | 589 } | 
| jbe@121 | 590 | 
| jbe@146 | 591 // special Lua stack indicies for json_path function: | 
| jbe@138 | 592 #define json_path_shadowtbl_idx 1 | 
| jbe@146 | 593 | 
| jbe@146 | 594 // stack offset of arguments to json_path function: | 
| jbe@155 | 595 #define json_path_idxshift 1 | 
| jbe@138 | 596 | 
| jbe@146 | 597 // gets a value or its type from a JSON document (passed as first argument) | 
| jbe@147 | 598 // using a path (passed as variable number of keys after first argument): | 
| jbe@137 | 599 static int json_path(lua_State *L, int type_mode) { | 
| jbe@146 | 600   int stacktop;                      // stack index of top of stack (after shifting) | 
| jbe@146 | 601   int idx = 2 + json_path_idxshift;  // stack index of current argument to process | 
| jbe@173 | 602   // require at least one argument: | 
| jbe@173 | 603   luaL_checkany(L, 1); | 
| jbe@148 | 604   // insert shadowtbl into stack at position 1 (shifting the arguments): | 
| jbe@144 | 605   json_regfetch(L, shadowtbl); | 
| jbe@138 | 606   lua_insert(L, 1); | 
| jbe@146 | 607   // store stack index of top of stack: | 
| jbe@138 | 608   stacktop = lua_gettop(L); | 
| jbe@146 | 609   // use first argument as "current value" (stored on top of stack): | 
| jbe@138 | 610   lua_pushvalue(L, 1 + json_path_idxshift); | 
| jbe@146 | 611   // process each "path key" (2nd argument and following arguments): | 
| jbe@138 | 612   while (idx <= stacktop) { | 
| jbe@146 | 613     // if "current value" (on top of stack) is nil, then the path cannot be walked and nil is returned: | 
| jbe@137 | 614     if (lua_isnil(L, -1)) return 1; | 
| jbe@137 | 615     // try to get shadow table of "current value": | 
| jbe@130 | 616     lua_pushvalue(L, -1); | 
| jbe@138 | 617     lua_rawget(L, json_path_shadowtbl_idx); | 
| jbe@126 | 618     if (lua_isnil(L, -1)) { | 
| jbe@137 | 619       // if no shadow table is found, | 
| jbe@130 | 620       if (lua_type(L, -1) == LUA_TTABLE) { | 
| jbe@146 | 621         // and if "current value" is a table, | 
| jbe@146 | 622         // drop nil from stack: | 
| jbe@146 | 623         lua_pop(L, 1); | 
| jbe@137 | 624         // get "next value" using the "path key": | 
| jbe@130 | 625         lua_pushvalue(L, idx++); | 
| jbe@130 | 626         lua_gettable(L, -2); | 
| jbe@130 | 627       } else { | 
| jbe@137 | 628         // if "current value" is not a table, | 
| jbe@146 | 629         // then the path cannot be walked and nil (already on top of stack) is returned: | 
| jbe@137 | 630         return 1; | 
| jbe@130 | 631       } | 
| jbe@130 | 632     } else { | 
| jbe@137 | 633       // if a shadow table is found, | 
| jbe@137 | 634       // set "current value" to its shadow table: | 
| jbe@130 | 635       lua_replace(L, -2); | 
| jbe@137 | 636       // get "next value" using the "path key": | 
| jbe@130 | 637       lua_pushvalue(L, idx++); | 
| jbe@130 | 638       lua_rawget(L, -2); | 
| jbe@126 | 639     } | 
| jbe@137 | 640     // the "next value" replaces the "current value": | 
| jbe@130 | 641     lua_replace(L, -2); | 
| jbe@126 | 642   } | 
| jbe@137 | 643   if (!type_mode) { | 
| jbe@137 | 644     // if a value (and not its type) was requested, | 
| jbe@137 | 645     // check if value is the null-marker, and store nil on top of Lua stack in that case: | 
| jbe@155 | 646     if (json_isnullmark(L, -1)) lua_pushnil(L); | 
| jbe@137 | 647   } else { | 
| jbe@137 | 648     // if the type was requested, | 
| jbe@137 | 649     // check if value is the null-marker: | 
| jbe@155 | 650     if (json_isnullmark(L, -1)) { | 
| jbe@137 | 651       // if yes, store string "null" on top of Lua stack: | 
| jbe@130 | 652       lua_pushliteral(L, "null"); | 
| jbe@137 | 653     } else { | 
| jbe@137 | 654       // otherwise, | 
| jbe@138 | 655       // check if metatable indicates "object" or "array": | 
| jbe@138 | 656       if (lua_getmetatable(L, -1)) { | 
| jbe@144 | 657         json_regfetch(L, objectmt); | 
| jbe@138 | 658         if (lua_rawequal(L, -2, -1)) { | 
| jbe@146 | 659           // if value has metatable for JSON objects, | 
| jbe@138 | 660           // return string "object": | 
| jbe@138 | 661           lua_pushliteral(L, "object"); | 
| jbe@138 | 662           return 1; | 
| jbe@138 | 663         } | 
| jbe@144 | 664         json_regfetch(L, arraymt); | 
| jbe@138 | 665         if (lua_rawequal(L, -3, -1)) { | 
| jbe@146 | 666           // if value has metatable for JSON arrays, | 
| jbe@146 | 667           // return string "object": | 
| jbe@138 | 668           lua_pushliteral(L, "array"); | 
| jbe@138 | 669           return 1; | 
| jbe@138 | 670         } | 
| jbe@146 | 671         // remove 3 metatables (one of the value, two for comparison) from stack: | 
| jbe@138 | 672         lua_pop(L, 3); | 
| jbe@138 | 673       } | 
| jbe@138 | 674       // otherwise, get the Lua type: | 
| jbe@138 | 675       lua_pushstring(L, lua_typename(L, lua_type(L, -1))); | 
| jbe@126 | 676     } | 
| jbe@126 | 677   } | 
| jbe@137 | 678   // return the top most value on the Lua stack: | 
| jbe@137 | 679   return 1; | 
| jbe@130 | 680 } | 
| jbe@130 | 681 | 
| jbe@147 | 682 // gets a value from a JSON document (passed as first argument) | 
| jbe@147 | 683 // using a path (passed as variable number of keys after first argument): | 
| jbe@130 | 684 static int json_get(lua_State *L) { | 
| jbe@137 | 685   return json_path(L, 0); | 
| jbe@130 | 686 } | 
| jbe@130 | 687 | 
| jbe@147 | 688 // gets a value's type from a JSON document (passed as first argument) | 
| jbe@147 | 689 // using a path (variable number of keys after first argument): | 
| jbe@130 | 690 static int json_type(lua_State *L) { | 
| jbe@137 | 691   return json_path(L, 1); | 
| jbe@130 | 692 } | 
| jbe@130 | 693 | 
| jbe@173 | 694 // special Lua stack indicies for json_set function: | 
| jbe@173 | 695 #define json_set_shadowtbl_idx 1 | 
| jbe@173 | 696 #define json_set_objectmt_idx 2 | 
| jbe@173 | 697 #define json_set_arraymt_idx 3 | 
| jbe@173 | 698 | 
| jbe@173 | 699 // stack offset of arguments to json_set function: | 
| jbe@173 | 700 #define json_set_idxshift 3 | 
| jbe@173 | 701 | 
| jbe@173 | 702 // sets a value (passed as second argument) in a JSON document (passed as first argument) | 
| jbe@173 | 703 // using a path (variable number of keys starting at third argument): | 
| jbe@173 | 704 static int json_set(lua_State *L) { | 
| jbe@173 | 705   int stacktop;   // stack index of top of stack (after shifting) | 
| jbe@173 | 706   int idx = 3;    // stack index of current argument to process | 
| jbe@173 | 707   // require at least two arguments: | 
| jbe@173 | 708   luaL_checkany(L, 1); | 
| jbe@173 | 709   luaL_checkany(L, 2); | 
| jbe@173 | 710   // insert shadowtbl into stack at position 1 (shifting the arguments): | 
| jbe@173 | 711   json_regfetch(L, shadowtbl); | 
| jbe@173 | 712   lua_insert(L, 1); | 
| jbe@173 | 713   // insert objectmt into stack at position 2 (shifting the arguments): | 
| jbe@173 | 714   json_regfetch(L, objectmt); | 
| jbe@173 | 715   lua_insert(L, 2); | 
| jbe@173 | 716   // insert arraymt into stack at position 3 (shifting the arguments): | 
| jbe@173 | 717   json_regfetch(L, arraymt); | 
| jbe@173 | 718   lua_insert(L, 3); | 
| jbe@173 | 719   // store stack index of top of stack: | 
| jbe@173 | 720   stacktop = lua_gettop(L); | 
| jbe@173 | 721   // use nil as initial "parent value": | 
| jbe@173 | 722   lua_pushnil(L); | 
| jbe@173 | 723   // use first argument as "current value": | 
| jbe@173 | 724   lua_pushvalue(L, 1 + json_set_idxshift); | 
| jbe@173 | 725   // set all necessary values in path: | 
| jbe@173 | 726   for (idx = 3 + json_set_idxshift; idx<=stacktop; idx++) { | 
| jbe@173 | 727     // push metatable of "current value" onto stack: | 
| jbe@173 | 728     if (!lua_getmetatable(L, -1)) lua_pushnil(L); | 
| jbe@173 | 729     // distinguish according to type of path key: | 
| jbe@173 | 730     switch (lua_type(L, idx)) { | 
| jbe@173 | 731     case LUA_TSTRING: | 
| jbe@173 | 732       // if path key is a string, | 
| jbe@173 | 733       // check if "current value" is a JSON object (or table without metatable): | 
| jbe@173 | 734       if ( | 
| jbe@173 | 735         lua_rawequal(L, -1, json_set_objectmt_idx) || | 
| jbe@173 | 736         (lua_isnil(L, -1) && lua_type(L, -2) == LUA_TTABLE) | 
| jbe@173 | 737       ) { | 
| jbe@173 | 738         // if "current value" is acceptable, | 
| jbe@173 | 739         // pop metatable and leave "current value" on top of stack: | 
| jbe@173 | 740         lua_pop(L, 1); | 
| jbe@173 | 741       } else { | 
| jbe@173 | 742         // if "current value" is not acceptable: | 
| jbe@173 | 743         // pop metatable and "current value": | 
| jbe@173 | 744         lua_pop(L, 2); | 
| jbe@173 | 745         // throw error if parent element does not exist: | 
| jbe@173 | 746         if (lua_isnil(L, -1)) return luaL_error(L, "Root element is not a JSON object"); | 
| jbe@173 | 747         // push new JSON object as "current value" onto stack: | 
| jbe@173 | 748         lua_newtable(L); | 
| jbe@173 | 749         // create and register shadow table: | 
| jbe@173 | 750         lua_pushvalue(L, -1); | 
| jbe@173 | 751         lua_newtable(L); | 
| jbe@173 | 752         lua_rawset(L, json_set_shadowtbl_idx); | 
| jbe@173 | 753         // set metatable of JSON object: | 
| jbe@173 | 754         lua_pushvalue(L, json_set_objectmt_idx); | 
| jbe@173 | 755         lua_setmetatable(L, -2); | 
| jbe@173 | 756         // set entry in "parent value": | 
| jbe@173 | 757         lua_pushvalue(L, idx-1); | 
| jbe@173 | 758         lua_pushvalue(L, -2); | 
| jbe@173 | 759         lua_settable(L, -4); | 
| jbe@173 | 760       } | 
| jbe@173 | 761       break; | 
| jbe@173 | 762     case LUA_TNUMBER: | 
| jbe@173 | 763       // if path key is a number, | 
| jbe@173 | 764       // check if "current value" is a JSON array (or table without metatable): | 
| jbe@173 | 765       if ( | 
| jbe@173 | 766         lua_rawequal(L, -1, json_set_arraymt_idx) || | 
| jbe@173 | 767         (lua_isnil(L, -1) && lua_type(L, -2) == LUA_TTABLE) | 
| jbe@173 | 768       ) { | 
| jbe@173 | 769         // if "current value" is acceptable, | 
| jbe@173 | 770         // pop metatable and leave "current value" on top of stack: | 
| jbe@173 | 771         lua_pop(L, 1); | 
| jbe@173 | 772       } else { | 
| jbe@173 | 773         // if "current value" is not acceptable: | 
| jbe@173 | 774         // pop metatable and "current value": | 
| jbe@173 | 775         lua_pop(L, 2); | 
| jbe@173 | 776         // throw error if parent element does not exist: | 
| jbe@173 | 777         if (lua_isnil(L, -1)) return luaL_error(L, "Root element is not a JSON array"); | 
| jbe@173 | 778         // push new JSON array as "current value" onto stack: | 
| jbe@173 | 779         lua_newtable(L); | 
| jbe@173 | 780         // create and register shadow table: | 
| jbe@173 | 781         lua_pushvalue(L, -1); | 
| jbe@173 | 782         lua_newtable(L); | 
| jbe@173 | 783         lua_rawset(L, json_set_shadowtbl_idx); | 
| jbe@173 | 784         // set metatable of JSON array: | 
| jbe@173 | 785         lua_pushvalue(L, json_set_arraymt_idx); | 
| jbe@173 | 786         lua_setmetatable(L, -2); | 
| jbe@173 | 787         // set entry in "parent value": | 
| jbe@173 | 788         lua_pushvalue(L, idx-1); | 
| jbe@173 | 789         lua_pushvalue(L, -2); | 
| jbe@173 | 790         lua_settable(L, -4); | 
| jbe@173 | 791       } | 
| jbe@173 | 792       break; | 
| jbe@173 | 793     default: | 
| jbe@173 | 794       return luaL_error(L, "Invalid path key of type %s", lua_typename(L, lua_type(L, idx))); | 
| jbe@173 | 795     } | 
| jbe@173 | 796     // check if last path element is being processed: | 
| jbe@173 | 797     if (idx == stacktop) { | 
| jbe@173 | 798       // if the last path element is being processed, | 
| jbe@173 | 799       // set last path value in "current value" container: | 
| jbe@173 | 800       lua_pushvalue(L, idx); | 
| jbe@173 | 801       lua_pushvalue(L, 2 + json_set_idxshift); | 
| jbe@173 | 802       lua_settable(L, -3); | 
| jbe@173 | 803     } else { | 
| jbe@173 | 804       // if the processed path element is not the last, | 
| jbe@173 | 805       // use old "current value" as new "parent value" | 
| jbe@173 | 806       lua_remove(L, -2); | 
| jbe@173 | 807       // push new "current value" onto stack by performing a lookup: | 
| jbe@173 | 808       lua_pushvalue(L, idx); | 
| jbe@173 | 809       lua_gettable(L, -2); | 
| jbe@173 | 810     } | 
| jbe@173 | 811   } | 
| jbe@173 | 812   // return first argument for convenience: | 
| jbe@173 | 813   lua_settop(L, 1 + json_set_idxshift); | 
| jbe@173 | 814   return 1; | 
| jbe@173 | 815 } | 
| jbe@173 | 816 | 
| jbe@147 | 817 // returns the length of a JSON array (or zero for a table without numeric keys): | 
| jbe@130 | 818 static int json_len(lua_State *L) { | 
| jbe@147 | 819   // stack shall contain one function argument: | 
| jbe@130 | 820   lua_settop(L, 1); | 
| jbe@148 | 821   // try to get corresponding shadow table for first argument: | 
| jbe@144 | 822   json_regfetch(L, shadowtbl); | 
| jbe@130 | 823   lua_pushvalue(L, 1); | 
| jbe@138 | 824   lua_rawget(L, -2); | 
| jbe@147 | 825   // if shadow table does not exist, return length of argument, else length of shadow table: | 
| jbe@147 | 826   lua_pushnumber(L, lua_rawlen(L, lua_isnil(L, -1) ? 1 : -1)); | 
| jbe@123 | 827   return 1; | 
| jbe@123 | 828 } | 
| jbe@123 | 829 | 
| jbe@130 | 830 static int json_index(lua_State *L) { | 
| jbe@148 | 831   // stack shall contain two function arguments: | 
| jbe@130 | 832   lua_settop(L, 2); | 
| jbe@155 | 833   // get corresponding shadow table for first argument: | 
| jbe@144 | 834   json_regfetch(L, shadowtbl); | 
| jbe@130 | 835   lua_pushvalue(L, 1); | 
| jbe@155 | 836   lua_rawget(L, -2); | 
| jbe@148 | 837   // throw error if no shadow table was found: | 
| jbe@139 | 838   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); | 
| jbe@148 | 839   // use key passed as second argument to lookup value in shadow table: | 
| jbe@130 | 840   lua_pushvalue(L, 2); | 
| jbe@130 | 841   lua_rawget(L, -2); | 
| jbe@148 | 842   // if value is null-marker, then push nil onto stack: | 
| jbe@155 | 843   if (json_isnullmark(L, -1)) lua_pushnil(L); | 
| jbe@148 | 844   // return either looked up value, or nil | 
| jbe@127 | 845   return 1; | 
| jbe@127 | 846 } | 
| jbe@127 | 847 | 
| jbe@130 | 848 static int json_newindex(lua_State *L) { | 
| jbe@148 | 849   // stack shall contain three function arguments: | 
| jbe@130 | 850   lua_settop(L, 3); | 
| jbe@148 | 851   // get corresponding shadow table for first argument: | 
| jbe@144 | 852   json_regfetch(L, shadowtbl); | 
| jbe@123 | 853   lua_pushvalue(L, 1); | 
| jbe@143 | 854   lua_rawget(L, -2); | 
| jbe@148 | 855   // throw error if no shadow table was found: | 
| jbe@130 | 856   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); | 
| jbe@148 | 857   // replace first argument with shadow table: | 
| jbe@130 | 858   lua_replace(L, 1); | 
| jbe@148 | 859   // reset stack and use second and third argument to write to shadow table: | 
| jbe@139 | 860   lua_settop(L, 3); | 
| jbe@130 | 861   lua_rawset(L, 1); | 
| jbe@148 | 862   // return nothing: | 
| jbe@148 | 863   return 0; | 
| jbe@121 | 864 } | 
| jbe@121 | 865 | 
| jbe@135 | 866 static int json_pairs_iterfunc(lua_State *L) { | 
| jbe@149 | 867   // stack shall contain two function arguments: | 
| jbe@135 | 868   lua_settop(L, 2); | 
| jbe@155 | 869   // get corresponding shadow table for first argument: | 
| jbe@144 | 870   json_regfetch(L, shadowtbl); | 
| jbe@135 | 871   lua_pushvalue(L, 1); | 
| jbe@155 | 872   lua_rawget(L, -2); | 
| jbe@149 | 873   // throw error if no shadow table was found: | 
| jbe@135 | 874   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); | 
| jbe@149 | 875   // get next key value pair from shadow table (using previous key from argument 2) | 
| jbe@149 | 876   // and return nothing if there is no next pair: | 
| jbe@135 | 877   lua_pushvalue(L, 2); | 
| jbe@135 | 878   if (!lua_next(L, -2)) return 0; | 
| jbe@149 | 879   // replace null-marker with nil: | 
| jbe@155 | 880   if (json_isnullmark(L, -1)) { | 
| jbe@135 | 881     lua_pop(L, 1); | 
| jbe@135 | 882     lua_pushnil(L); | 
| jbe@135 | 883   } | 
| jbe@149 | 884   // return key and value (or key and nil, if null-marker was found): | 
| jbe@135 | 885   return 2; | 
| jbe@135 | 886 } | 
| jbe@135 | 887 | 
| jbe@149 | 888 // returns a triple such that 'for key, value in pairs(obj) do ... end' | 
| jbe@149 | 889 // iterates through all key value pairs (including JSON null keys represented as Lua nil): | 
| jbe@135 | 890 static int json_pairs(lua_State *L) { | 
| jbe@172 | 891   // require one argument to function | 
| jbe@172 | 892   luaL_checkany(L, 1); | 
| jbe@149 | 893   // return triple of function json_pairs_iterfunc, first argument, and nil: | 
| jbe@139 | 894   lua_pushcfunction(L, json_pairs_iterfunc); | 
| jbe@135 | 895   lua_pushvalue(L, 1); | 
| jbe@135 | 896   lua_pushnil(L); | 
| jbe@135 | 897   return 3; | 
| jbe@135 | 898 } | 
| jbe@135 | 899 | 
| jbe@134 | 900 static int json_ipairs_iterfunc(lua_State *L) { | 
| jbe@152 | 901   lua_Integer idx; | 
| jbe@149 | 902   // stack shall contain two function arguments: | 
| jbe@134 | 903   lua_settop(L, 2); | 
| jbe@149 | 904   // calculate new index by incrementing second argument: | 
| jbe@134 | 905   idx = lua_tointeger(L, 2) + 1; | 
| jbe@149 | 906   // get corresponding shadow table for first argument: | 
| jbe@155 | 907   json_regfetch(L, shadowtbl); | 
| jbe@134 | 908   lua_pushvalue(L, 1); | 
| jbe@155 | 909   lua_rawget(L, -2); | 
| jbe@149 | 910   // throw error if no shadow table was found: | 
| jbe@134 | 911   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); | 
| jbe@149 | 912   // do integer lookup in shadow table: | 
| jbe@134 | 913   lua_rawgeti(L, -1, idx); | 
| jbe@149 | 914   // return nothing if there was no value: | 
| jbe@134 | 915   if (lua_isnil(L, -1)) return 0; | 
| jbe@149 | 916   // return new index and | 
| jbe@149 | 917   // either the looked up value if it is not equal to the null-marker | 
| jbe@149 | 918   // or nil instead of null-marker: | 
| jbe@134 | 919   lua_pushinteger(L, idx); | 
| jbe@155 | 920   if (json_isnullmark(L, -2)) lua_pushnil(L); | 
| jbe@134 | 921   else lua_pushvalue(L, -2); | 
| jbe@134 | 922   return 2; | 
| jbe@134 | 923 } | 
| jbe@134 | 924 | 
| jbe@149 | 925 // returns a triple such that 'for idx, value in ipairs(ary) do ... end' | 
| jbe@149 | 926 // iterates through all values (including JSON null represented as Lua nil): | 
| jbe@134 | 927 static int json_ipairs(lua_State *L) { | 
| jbe@172 | 928   // require one argument to function | 
| jbe@172 | 929   luaL_checkany(L, 1); | 
| jbe@149 | 930   // return triple of function json_ipairs_iterfunc, first argument, and zero: | 
| jbe@139 | 931   lua_pushcfunction(L, json_ipairs_iterfunc); | 
| jbe@134 | 932   lua_pushvalue(L, 1); | 
| jbe@134 | 933   lua_pushinteger(L, 0); | 
| jbe@134 | 934   return 3; | 
| jbe@134 | 935 } | 
| jbe@134 | 936 | 
| jbe@163 | 937 typedef struct { | 
| jbe@163 | 938   size_t length; | 
| jbe@163 | 939   const char *data; | 
| jbe@163 | 940 } json_key_t; | 
| jbe@163 | 941 | 
| jbe@163 | 942 static int json_key_cmp(json_key_t *key1, json_key_t *key2) { | 
| jbe@163 | 943   size_t pos = 0; | 
| jbe@163 | 944   unsigned char c1, c2; | 
| jbe@163 | 945   while (1) { | 
| jbe@163 | 946     if (key1->length > pos) { | 
| jbe@163 | 947       if (key2->length > pos) { | 
| jbe@163 | 948         c1 = key1->data[pos]; | 
| jbe@163 | 949         c2 = key2->data[pos]; | 
| jbe@163 | 950         if (c1 < c2) return -1; | 
| jbe@163 | 951         else if (c1 > c2) return 1; | 
| jbe@163 | 952       } else { | 
| jbe@163 | 953         return 1; | 
| jbe@163 | 954       } | 
| jbe@163 | 955     } else { | 
| jbe@163 | 956       if (key2->length > pos) { | 
| jbe@163 | 957         return -1; | 
| jbe@163 | 958       } else { | 
| jbe@163 | 959         return 0; | 
| jbe@163 | 960       } | 
| jbe@163 | 961     } | 
| jbe@163 | 962     pos++; | 
| jbe@163 | 963   } | 
| jbe@163 | 964 } | 
| jbe@163 | 965 | 
| jbe@154 | 966 #define JSON_TABLETYPE_UNKNOWN 0 | 
| jbe@154 | 967 #define JSON_TABLETYPE_OBJECT 1 | 
| jbe@154 | 968 #define JSON_TABLETYPE_ARRAY 2 | 
| jbe@154 | 969 | 
| jbe@164 | 970 #define json_export_internal_indentstring_idx 1 | 
| jbe@164 | 971 #define json_export_internal_level_idx 2 | 
| jbe@164 | 972 #define json_export_internal_value_idx 3 | 
| jbe@164 | 973 #define json_export_internal_tmp_idx 4 | 
| jbe@164 | 974 | 
| jbe@164 | 975 static int json_export_internal(lua_State *L) { | 
| jbe@164 | 976   int level; | 
| jbe@164 | 977   int pretty; | 
| jbe@164 | 978   int i; | 
| jbe@154 | 979   lua_Number num; | 
| jbe@154 | 980   const char *str; | 
| jbe@154 | 981   unsigned char c; | 
| jbe@154 | 982   size_t strlen; | 
| jbe@154 | 983   size_t pos = 0; | 
| jbe@154 | 984   luaL_Buffer buf; | 
| jbe@154 | 985   char hexcode[7];  // backslash, character 'u', 4 hex digits, and terminating NULL byte | 
| jbe@154 | 986   int tabletype = JSON_TABLETYPE_UNKNOWN; | 
| jbe@164 | 987   int anyelement = 0; | 
| jbe@163 | 988   size_t keycount = 0; | 
| jbe@163 | 989   size_t keypos = 0; | 
| jbe@165 | 990   json_key_t *keybuf = NULL; | 
| jbe@154 | 991   lua_Integer idx; | 
| jbe@164 | 992   lua_settop(L, json_export_internal_value_idx); | 
| jbe@164 | 993   if (json_isnullmark(L, json_export_internal_value_idx)) { | 
| jbe@164 | 994     lua_pop(L, 1); | 
| jbe@157 | 995     lua_pushnil(L); | 
| jbe@157 | 996   } | 
| jbe@164 | 997   switch (lua_type(L, json_export_internal_value_idx)) { | 
| jbe@154 | 998   case LUA_TNIL: | 
| jbe@154 | 999     lua_pushliteral(L, "null"); | 
| jbe@154 | 1000     return 1; | 
| jbe@154 | 1001   case LUA_TNUMBER: | 
| jbe@164 | 1002     num = lua_tonumber(L, json_export_internal_value_idx); | 
| jbe@154 | 1003     if (isnan(num)) return luaL_error(L, "JSON export not possible for NaN value"); | 
| jbe@154 | 1004     if (isinf(num)) return luaL_error(L, "JSON export not possible for infinite numbers"); | 
| jbe@164 | 1005     lua_tostring(L, json_export_internal_value_idx); | 
| jbe@154 | 1006     return 1; | 
| jbe@154 | 1007   case LUA_TBOOLEAN: | 
| jbe@164 | 1008     if (lua_toboolean(L, json_export_internal_value_idx)) { | 
| jbe@164 | 1009       lua_pushliteral(L, "true"); | 
| jbe@164 | 1010     } else { | 
| jbe@164 | 1011       lua_pushliteral(L, "false"); | 
| jbe@164 | 1012     } | 
| jbe@154 | 1013     return 1; | 
| jbe@154 | 1014   case LUA_TSTRING: | 
| jbe@164 | 1015     str = lua_tolstring(L, 3, &strlen); | 
| jbe@154 | 1016     luaL_buffinit(L, &buf); | 
| jbe@154 | 1017     luaL_addchar(&buf, '"'); | 
| jbe@154 | 1018     while (pos < strlen) { | 
| jbe@154 | 1019       c = str[pos++]; | 
| jbe@154 | 1020       if (c == '"')       luaL_addstring(&buf, "\\\""); | 
| jbe@154 | 1021       else if (c == '\\') luaL_addstring(&buf, "\\\\"); | 
| jbe@154 | 1022       else if (c == 127)  luaL_addstring(&buf, "\\u007F"); | 
| jbe@154 | 1023       else if (c >= 32)   luaL_addchar(&buf, c); | 
| jbe@154 | 1024       else if (c == '\b') luaL_addstring(&buf, "\\b"); | 
| jbe@154 | 1025       else if (c == '\f') luaL_addstring(&buf, "\\f"); | 
| jbe@154 | 1026       else if (c == '\n') luaL_addstring(&buf, "\\n"); | 
| jbe@154 | 1027       else if (c == '\r') luaL_addstring(&buf, "\\r"); | 
| jbe@154 | 1028       else if (c == '\t') luaL_addstring(&buf, "\\t"); | 
| jbe@154 | 1029       else if (c == '\v') luaL_addstring(&buf, "\\v"); | 
| jbe@154 | 1030       else { | 
| jbe@154 | 1031         sprintf(hexcode, "\\u%04X", c); | 
| jbe@154 | 1032         luaL_addstring(&buf, hexcode); | 
| jbe@154 | 1033       } | 
| jbe@154 | 1034     } | 
| jbe@154 | 1035     luaL_addchar(&buf, '"'); | 
| jbe@154 | 1036     luaL_pushresult(&buf); | 
| jbe@154 | 1037     return 1; | 
| jbe@154 | 1038   case LUA_TTABLE: | 
| jbe@164 | 1039     if (lua_getmetatable(L, json_export_internal_value_idx)) { | 
| jbe@154 | 1040       json_regfetch(L, objectmt); | 
| jbe@154 | 1041       if (lua_rawequal(L, -2, -1)) { | 
| jbe@154 | 1042         tabletype = JSON_TABLETYPE_OBJECT; | 
| jbe@154 | 1043       } else { | 
| jbe@154 | 1044         json_regfetch(L, arraymt); | 
| jbe@164 | 1045         if (lua_rawequal(L, -3, -1)) { | 
| jbe@164 | 1046           tabletype = JSON_TABLETYPE_ARRAY; | 
| jbe@164 | 1047         } else { | 
| jbe@164 | 1048           return luaL_error(L, "JSON export not possible for tables with nonsupported metatable"); | 
| jbe@164 | 1049         } | 
| jbe@154 | 1050       } | 
| jbe@154 | 1051     } | 
| jbe@154 | 1052     json_regfetch(L, shadowtbl); | 
| jbe@164 | 1053     lua_pushvalue(L, json_export_internal_value_idx); | 
| jbe@154 | 1054     lua_rawget(L, -2); | 
| jbe@164 | 1055     if (!lua_isnil(L, -1)) lua_replace(L, json_export_internal_value_idx); | 
| jbe@164 | 1056     lua_settop(L, json_export_internal_value_idx); | 
| jbe@154 | 1057     if (tabletype == JSON_TABLETYPE_UNKNOWN) { | 
| jbe@164 | 1058       for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) { | 
| jbe@164 | 1059         switch (lua_type(L, -2)) { | 
| jbe@164 | 1060         case LUA_TSTRING: | 
| jbe@164 | 1061           keycount++; | 
| jbe@164 | 1062           if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_OBJECT; | 
| jbe@164 | 1063           else if (tabletype == JSON_TABLETYPE_ARRAY) goto json_export_tabletype_error; | 
| jbe@164 | 1064           break; | 
| jbe@164 | 1065         case LUA_TNUMBER: | 
| jbe@164 | 1066           if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_ARRAY; | 
| jbe@164 | 1067           else if (tabletype == JSON_TABLETYPE_OBJECT) goto json_export_tabletype_error; | 
| jbe@164 | 1068           break; | 
| jbe@154 | 1069         } | 
| jbe@154 | 1070       } | 
| jbe@154 | 1071     } | 
| jbe@164 | 1072     pretty = lua_toboolean(L, json_export_internal_indentstring_idx); | 
| jbe@164 | 1073     level = lua_tointeger(L, json_export_internal_level_idx) + 1; | 
| jbe@164 | 1074     if (level > JSON_MAXDEPTH) { | 
| jbe@164 | 1075       return luaL_error(L, "More than %d nested JSON levels", JSON_MAXDEPTH); | 
| jbe@164 | 1076     } | 
| jbe@154 | 1077     switch (tabletype) { | 
| jbe@154 | 1078     case JSON_TABLETYPE_OBJECT: | 
| jbe@164 | 1079       if (!keycount) { | 
| jbe@164 | 1080         for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) { | 
| jbe@164 | 1081           if (lua_type(L, -2) == LUA_TSTRING) keycount++; | 
| jbe@164 | 1082         } | 
| jbe@163 | 1083       } | 
| jbe@163 | 1084       if (keycount) { | 
| jbe@163 | 1085         keybuf = calloc(keycount, sizeof(json_key_t)); | 
| jbe@163 | 1086         if (!keybuf) return luaL_error(L, "Memory allocation failed in JSON library"); | 
| jbe@164 | 1087         for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) { | 
| jbe@163 | 1088           if (lua_type(L, -2) == LUA_TSTRING) { | 
| jbe@163 | 1089             json_key_t *key = keybuf + (keypos++); | 
| jbe@163 | 1090             key->data = lua_tolstring(L, -2, &key->length); | 
| jbe@163 | 1091           } | 
| jbe@163 | 1092         } | 
| jbe@163 | 1093         qsort(keybuf, keycount, sizeof(json_key_t), (void *)json_key_cmp); | 
| jbe@163 | 1094       } | 
| jbe@154 | 1095       luaL_buffinit(L, &buf); | 
| jbe@154 | 1096       luaL_addchar(&buf, '{'); | 
| jbe@163 | 1097       for (keypos=0; keypos<keycount; keypos++) { | 
| jbe@163 | 1098         json_key_t *key = keybuf + keypos; | 
| jbe@163 | 1099         if (keypos) luaL_addchar(&buf, ','); | 
| jbe@164 | 1100         if (pretty) { | 
| jbe@164 | 1101           luaL_addchar(&buf, '\n'); | 
| jbe@164 | 1102           for (i=0; i<level; i++) { | 
| jbe@164 | 1103             lua_pushvalue(L, json_export_internal_indentstring_idx); | 
| jbe@164 | 1104             luaL_addvalue(&buf); | 
| jbe@164 | 1105           } | 
| jbe@164 | 1106         } | 
| jbe@164 | 1107         lua_pushcfunction(L, json_export_internal); | 
| jbe@164 | 1108         lua_pushvalue(L, json_export_internal_indentstring_idx); | 
| jbe@164 | 1109         lua_pushinteger(L, level); | 
| jbe@163 | 1110         lua_pushlstring(L, key->data, key->length); | 
| jbe@164 | 1111         if (lua_pcall(L, 3, 1, 0)) { | 
| jbe@163 | 1112           if (keybuf) free(keybuf); | 
| jbe@163 | 1113           return lua_error(L); | 
| jbe@154 | 1114         } | 
| jbe@163 | 1115         luaL_addvalue(&buf); | 
| jbe@163 | 1116         luaL_addchar(&buf, ':'); | 
| jbe@164 | 1117         if (pretty) luaL_addchar(&buf, ' '); | 
| jbe@164 | 1118         lua_pushcfunction(L, json_export_internal); | 
| jbe@164 | 1119         lua_pushvalue(L, json_export_internal_indentstring_idx); | 
| jbe@164 | 1120         lua_pushinteger(L, level); | 
| jbe@163 | 1121         lua_pushlstring(L, key->data, key->length); | 
| jbe@164 | 1122         lua_rawget(L, json_export_internal_value_idx); | 
| jbe@164 | 1123         if (lua_pcall(L, 3, 1, 0)) { | 
| jbe@163 | 1124           if (keybuf) free(keybuf); | 
| jbe@163 | 1125           return lua_error(L); | 
| jbe@163 | 1126         } | 
| jbe@163 | 1127         luaL_addvalue(&buf); | 
| jbe@154 | 1128       } | 
| jbe@163 | 1129       if (keybuf) free(keybuf); | 
| jbe@164 | 1130       if (pretty && keycount != 0) { | 
| jbe@164 | 1131         luaL_addchar(&buf, '\n'); | 
| jbe@164 | 1132         for (i=0; i<level-1; i++) { | 
| jbe@164 | 1133           lua_pushvalue(L, json_export_internal_indentstring_idx); | 
| jbe@164 | 1134           luaL_addvalue(&buf); | 
| jbe@164 | 1135         } | 
| jbe@164 | 1136       } | 
| jbe@154 | 1137       luaL_addchar(&buf, '}'); | 
| jbe@164 | 1138       if (pretty && level == 1) luaL_addchar(&buf, '\n'); | 
| jbe@154 | 1139       luaL_pushresult(&buf); | 
| jbe@154 | 1140       return 1; | 
| jbe@154 | 1141     case JSON_TABLETYPE_ARRAY: | 
| jbe@164 | 1142       lua_settop(L, json_export_internal_tmp_idx); | 
| jbe@154 | 1143       luaL_buffinit(L, &buf); | 
| jbe@154 | 1144       luaL_addchar(&buf, '['); | 
| jbe@154 | 1145       for (idx = 1; ; idx++) { | 
| jbe@164 | 1146         lua_rawgeti(L, json_export_internal_value_idx, idx); | 
| jbe@154 | 1147         if (lua_isnil(L, -1)) { | 
| jbe@154 | 1148           lua_pop(L, 1); | 
| jbe@154 | 1149           break; | 
| jbe@154 | 1150         } | 
| jbe@164 | 1151         lua_replace(L, json_export_internal_tmp_idx); | 
| jbe@164 | 1152         if (anyelement) luaL_addchar(&buf, ','); | 
| jbe@164 | 1153         anyelement = 1; | 
| jbe@164 | 1154         if (pretty) { | 
| jbe@164 | 1155           luaL_addchar(&buf, '\n'); | 
| jbe@164 | 1156           for (i=0; i<level; i++) { | 
| jbe@164 | 1157             lua_pushvalue(L, json_export_internal_indentstring_idx); | 
| jbe@164 | 1158             luaL_addvalue(&buf); | 
| jbe@164 | 1159           } | 
| jbe@164 | 1160         } | 
| jbe@164 | 1161         lua_pushcfunction(L, json_export_internal); | 
| jbe@164 | 1162         lua_pushvalue(L, json_export_internal_indentstring_idx); | 
| jbe@164 | 1163         lua_pushinteger(L, level); | 
| jbe@164 | 1164         lua_pushvalue(L, json_export_internal_tmp_idx); | 
| jbe@164 | 1165         lua_call(L, 3, 1); | 
| jbe@154 | 1166         luaL_addvalue(&buf); | 
| jbe@154 | 1167       } | 
| jbe@164 | 1168       if (pretty && anyelement) { | 
| jbe@164 | 1169         luaL_addchar(&buf, '\n'); | 
| jbe@164 | 1170         for (i=0; i<level-1; i++) { | 
| jbe@164 | 1171           lua_pushvalue(L, json_export_internal_indentstring_idx); | 
| jbe@164 | 1172           luaL_addvalue(&buf); | 
| jbe@164 | 1173         } | 
| jbe@164 | 1174       } | 
| jbe@154 | 1175       luaL_addchar(&buf, ']'); | 
| jbe@164 | 1176       if (pretty && level == 1) luaL_addchar(&buf, '\n'); | 
| jbe@154 | 1177       luaL_pushresult(&buf); | 
| jbe@154 | 1178       return 1; | 
| jbe@154 | 1179     } | 
| jbe@154 | 1180     json_export_tabletype_error: | 
| jbe@154 | 1181     return luaL_error(L, "JSON export not possible for ambiguous table (cannot decide whether it is an object or array)"); | 
| jbe@154 | 1182   } | 
| jbe@166 | 1183   return luaL_error(L, "JSON export not possible for values of type \"%s\"", lua_typename(L, lua_type(L, json_export_internal_value_idx))); | 
| jbe@154 | 1184 } | 
| jbe@154 | 1185 | 
| jbe@164 | 1186 static int json_export(lua_State *L) { | 
| jbe@164 | 1187   lua_settop(L, 1); | 
| jbe@164 | 1188   lua_pushcfunction(L, json_export_internal); | 
| jbe@164 | 1189   lua_pushnil(L); | 
| jbe@164 | 1190   lua_pushinteger(L, 0); | 
| jbe@164 | 1191   lua_pushvalue(L, 1); | 
| jbe@164 | 1192   lua_call(L, 3, 1); | 
| jbe@164 | 1193   return 1; | 
| jbe@164 | 1194 } | 
| jbe@164 | 1195 | 
| jbe@164 | 1196 static int json_pretty(lua_State *L) { | 
| jbe@164 | 1197   lua_settop(L, 2); | 
| jbe@164 | 1198   lua_pushcfunction(L, json_export_internal); | 
| jbe@164 | 1199   if (lua_isnil(L, 2)) lua_pushliteral(L, "  "); | 
| jbe@164 | 1200   else lua_pushvalue(L, 2); | 
| jbe@164 | 1201   lua_pushinteger(L, 0); | 
| jbe@164 | 1202   lua_pushvalue(L, 1); | 
| jbe@164 | 1203   lua_call(L, 3, 1); | 
| jbe@164 | 1204   return 1; | 
| jbe@164 | 1205 } | 
| jbe@164 | 1206 | 
| jbe@149 | 1207 // functions in library module: | 
| jbe@121 | 1208 static const struct luaL_Reg json_module_functions[] = { | 
| jbe@133 | 1209   {"object", json_object}, | 
| jbe@173 | 1210   {"array",  json_array}, | 
| jbe@121 | 1211   {"import", json_import}, | 
| jbe@154 | 1212   {"export", json_export}, | 
| jbe@164 | 1213   {"pretty", json_pretty}, | 
| jbe@173 | 1214   {"get",    json_get}, | 
| jbe@173 | 1215   {"type",   json_type}, | 
| jbe@173 | 1216   {"set",    json_set}, | 
| jbe@121 | 1217   {NULL, NULL} | 
| jbe@121 | 1218 }; | 
| jbe@121 | 1219 | 
| jbe@149 | 1220 // metamethods for JSON objects, JSON arrays, and unknown JSON collections (object or array): | 
| jbe@126 | 1221 static const struct luaL_Reg json_metatable_functions[] = { | 
| jbe@130 | 1222   {"__len", json_len}, | 
| jbe@130 | 1223   {"__index", json_index}, | 
| jbe@130 | 1224   {"__newindex", json_newindex}, | 
| jbe@135 | 1225   {"__pairs", json_pairs}, | 
| jbe@134 | 1226   {"__ipairs", json_ipairs}, | 
| jbe@160 | 1227   {"__tostring", json_export}, | 
| jbe@126 | 1228   {NULL, NULL} | 
| jbe@126 | 1229 }; | 
| jbe@126 | 1230 | 
| jbe@157 | 1231 // metamethods for JSON null marker: | 
| jbe@157 | 1232 static const struct luaL_Reg json_nullmark_metamethods[] = { | 
| jbe@157 | 1233   {"__tostring", json_nullmark_tostring}, | 
| jbe@157 | 1234   {NULL, NULL} | 
| jbe@157 | 1235 }; | 
| jbe@157 | 1236 | 
| jbe@149 | 1237 // initializes json library: | 
| jbe@121 | 1238 int luaopen_json(lua_State *L) { | 
| jbe@149 | 1239   // empty stack: | 
| jbe@126 | 1240   lua_settop(L, 0); | 
| jbe@149 | 1241   // push library module onto stack position 1: | 
| jbe@149 | 1242   lua_newtable(L); | 
| jbe@149 | 1243   // register library functions: | 
| jbe@149 | 1244   luaL_setfuncs(L, json_module_functions, 0); | 
| jbe@149 | 1245   // create and store objectmt: | 
| jbe@138 | 1246   lua_newtable(L); | 
| jbe@138 | 1247   luaL_setfuncs(L, json_metatable_functions, 0); | 
| jbe@144 | 1248   json_regstore(L, objectmt); | 
| jbe@149 | 1249   // create and store arraymt: | 
| jbe@138 | 1250   lua_newtable(L); | 
| jbe@138 | 1251   luaL_setfuncs(L, json_metatable_functions, 0); | 
| jbe@144 | 1252   json_regstore(L, arraymt); | 
| jbe@149 | 1253   // create and store ephemeron table to store shadow tables for each JSON object/array | 
| jbe@149 | 1254   // to allow NULL values returned as nil | 
| jbe@149 | 1255   lua_newtable(L); | 
| jbe@138 | 1256   lua_newtable(L);  // metatable for ephemeron table | 
| jbe@121 | 1257   lua_pushliteral(L, "__mode"); | 
| jbe@121 | 1258   lua_pushliteral(L, "k"); | 
| jbe@138 | 1259   lua_rawset(L, -3); | 
| jbe@138 | 1260   lua_setmetatable(L, -2); | 
| jbe@144 | 1261   json_regstore(L, shadowtbl); | 
| jbe@157 | 1262   // set metatable of null marker and make it available through library module: | 
| jbe@157 | 1263   json_pushnullmark(L); | 
| jbe@157 | 1264   lua_newtable(L); | 
| jbe@157 | 1265   luaL_setfuncs(L, json_nullmark_metamethods, 0); | 
| jbe@157 | 1266   lua_setmetatable(L, -2); | 
| jbe@157 | 1267   lua_setfield(L, 1, "null"); | 
| jbe@157 | 1268   // return library module (that's expected on top of stack): | 
| jbe@121 | 1269   return 1; | 
| jbe@121 | 1270 } |