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