| rev | line source | 
| jbe@121 | 1 #include <lua.h> | 
| jbe@121 | 2 #include <lauxlib.h> | 
| jbe@122 | 3 #include <stdlib.h> | 
| jbe@121 | 4 #include <string.h> | 
| jbe@121 | 5 | 
| jbe@137 | 6 #define JSON_UPVAL_LIBRARY   lua_upvalueindex(1) | 
| jbe@137 | 7 #define JSON_UPVAL_NULLMARK  lua_upvalueindex(2) | 
| jbe@137 | 8 #define JSON_UPVAL_SHADOWTBL lua_upvalueindex(3) | 
| jbe@137 | 9 #define JSON_UPVAL_TYPES     lua_upvalueindex(4) | 
| jbe@137 | 10 #define JSON_UPVAL_METATABLE lua_upvalueindex(5) | 
| jbe@137 | 11 #define JSON_UPVAL_PAIRS_ITERFUNC  lua_upvalueindex(6) | 
| jbe@137 | 12 #define JSON_UPVAL_IPAIRS_ITERFUNC lua_upvalueindex(7) | 
| jbe@123 | 13 | 
| jbe@136 | 14 // marks a table as JSON object or JSON array: | 
| jbe@136 | 15 // (returns its modified argument or a new table if argument is nil) | 
| jbe@136 | 16 static int json_mark(lua_State *L, const char *marker) { | 
| jbe@136 | 17   // if argument is nil, then create new table: | 
| jbe@136 | 18   if (lua_isnoneornil(L, 1)) { | 
| jbe@136 | 19     lua_settop(L, 0); | 
| jbe@136 | 20     lua_newtable(L); | 
| jbe@136 | 21     // skip testing of existing shadow table: | 
| jbe@136 | 22     goto json_object_create_shadow_table; | 
| jbe@136 | 23   } | 
| jbe@136 | 24   lua_pushvalue(L, 1); | 
| jbe@136 | 25   lua_rawget(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@136 | 26   if (lua_isnil(L, -1)) { | 
| jbe@136 | 27     json_object_create_shadow_table: | 
| jbe@136 | 28     // set shadow table: | 
| jbe@136 | 29     lua_pushvalue(L, 1); | 
| jbe@136 | 30     lua_newtable(L); | 
| jbe@136 | 31     lua_rawset(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@136 | 32   } | 
| jbe@136 | 33   // set metatable: | 
| jbe@136 | 34   lua_pushvalue(L, JSON_UPVAL_METATABLE); | 
| jbe@136 | 35   lua_setmetatable(L, 1); | 
| jbe@136 | 36   // mark table as JSON object or as JSON array | 
| jbe@136 | 37   lua_pushvalue(L, 1); | 
| jbe@136 | 38   lua_pushstring(L, marker); | 
| jbe@136 | 39   lua_rawset(L, JSON_UPVAL_TYPES); | 
| jbe@136 | 40   return 1; | 
| jbe@136 | 41 } | 
| jbe@136 | 42 | 
| jbe@136 | 43 // marks a table as JSON object: | 
| jbe@136 | 44 // (returns its modified argument or a new table if argument is nil) | 
| jbe@136 | 45 static int json_object(lua_State *L) { | 
| jbe@136 | 46   return json_mark(L, "object"); | 
| jbe@136 | 47 } | 
| jbe@136 | 48 | 
| jbe@136 | 49 // marks a table as JSON array: | 
| jbe@136 | 50 // (returns its modified argument or a new table if argument is nil) | 
| jbe@136 | 51 static int json_array(lua_State *L) { | 
| jbe@136 | 52   return json_mark(L, "array"); | 
| jbe@136 | 53 } | 
| jbe@136 | 54 | 
| jbe@124 | 55 #define JSON_STATE_VALUE 0 | 
| jbe@124 | 56 #define JSON_STATE_OBJECT_KEY 1 | 
| jbe@124 | 57 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2 | 
| jbe@124 | 58 #define JSON_STATE_OBJECT_VALUE 3 | 
| jbe@124 | 59 #define JSON_STATE_OBJECT_SEPARATOR 4 | 
| jbe@124 | 60 #define JSON_STATE_ARRAY_VALUE 5 | 
| jbe@124 | 61 #define JSON_STATE_ARRAY_SEPARATOR 6 | 
| jbe@124 | 62 #define JSON_STATE_END 7 | 
| jbe@121 | 63 | 
| jbe@136 | 64 // decodes a JSON document: | 
| jbe@121 | 65 static int json_import(lua_State *L) { | 
| jbe@136 | 66   const char *str;   // string to parse | 
| jbe@136 | 67   size_t total;      // total length of string to parse | 
| jbe@136 | 68   size_t pos = 0;    // current position in string to parse | 
| jbe@136 | 69   size_t level = 0;  // nested levels of objects/arrays currently being processed | 
| jbe@136 | 70   int mode = JSON_STATE_VALUE;  // state of parser | 
| jbe@136 | 71   char c;              // variable to store a single character to be processed | 
| jbe@136 | 72   luaL_Buffer luabuf;  // Lua buffer to decode (possibly escaped) strings | 
| jbe@136 | 73   char *cbuf;          // C buffer to decode (possibly escaped) strings | 
| jbe@136 | 74   size_t writepos;     // write position of decoded strings in C buffer | 
| jbe@136 | 75   // require string as first argument: | 
| jbe@136 | 76   str = luaL_checklstring(L, 1, &total); | 
| jbe@136 | 77   // if string contains a NULL byte, this is a syntax error | 
| jbe@136 | 78   if (strlen(str) != total) goto json_import_syntax_error; | 
| jbe@136 | 79   // main loop of parser: | 
| jbe@136 | 80   json_import_loop: | 
| jbe@136 | 81   // skip whitespace and store next character in variable 'c': | 
| jbe@121 | 82   while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++; | 
| jbe@136 | 83   // switch statement to handle certain (single) characters: | 
| jbe@121 | 84   switch (c) { | 
| jbe@136 | 85   // handle end of JSON document: | 
| jbe@121 | 86   case 0: | 
| jbe@136 | 87     // if end of JSON document was expected, then return top element of stack as result: | 
| jbe@124 | 88     if (mode == JSON_STATE_END) return 1; | 
| jbe@136 | 89     // otherwise, the JSON document was malformed: | 
| jbe@121 | 90     json_import_unexpected_eof: | 
| jbe@121 | 91     lua_pushnil(L); | 
| jbe@121 | 92     if (level == 0) lua_pushliteral(L, "Empty string"); | 
| jbe@121 | 93     else lua_pushliteral(L, "Unexpected end of JSON document"); | 
| jbe@121 | 94     return 2; | 
| jbe@136 | 95   // new JSON object: | 
| jbe@121 | 96   case '{': | 
| jbe@136 | 97     // if a JSON object is not expected here, then return an error: | 
| jbe@124 | 98     if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE) | 
| jbe@121 | 99       goto json_import_syntax_error; | 
| jbe@136 | 100     // consume input character: | 
| jbe@121 | 101     pos++; | 
| jbe@136 | 102     // create JSON object on stack: | 
| jbe@136 | 103     lua_newtable(L); | 
| jbe@136 | 104     // set metatable of JSON object: | 
| jbe@125 | 105     lua_pushvalue(L, JSON_UPVAL_METATABLE); | 
| jbe@125 | 106     lua_setmetatable(L, -2); | 
| jbe@136 | 107     // mark JSON object as JSON object (as opposed to JSON array): | 
| jbe@127 | 108     lua_pushvalue(L, -1); | 
| jbe@127 | 109     lua_pushliteral(L, "object"); | 
| jbe@127 | 110     lua_rawset(L, JSON_UPVAL_TYPES); | 
| jbe@136 | 111     // create internal shadow table on stack: | 
| jbe@136 | 112     lua_newtable(L); | 
| jbe@136 | 113     // register internal shadow table: | 
| jbe@123 | 114     lua_pushvalue(L, -2); | 
| jbe@123 | 115     lua_pushvalue(L, -2); | 
| jbe@130 | 116     lua_rawset(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@136 | 117     // increment level: | 
| jbe@121 | 118     level++; | 
| jbe@136 | 119     // expect object key (or end of object) and continue with loop: | 
| jbe@136 | 120     mode = JSON_STATE_OBJECT_KEY; | 
| jbe@121 | 121     goto json_import_loop; | 
| jbe@136 | 122   // new JSON array: | 
| jbe@121 | 123   case '[': | 
| jbe@136 | 124     // if a JSON array is not expected here, then return an error: | 
| jbe@124 | 125     if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE) | 
| jbe@121 | 126       goto json_import_syntax_error; | 
| jbe@136 | 127     // consume input character: | 
| jbe@121 | 128     pos++; | 
| jbe@136 | 129     // create JSON array on stack: | 
| jbe@136 | 130     lua_newtable(L); | 
| jbe@136 | 131     // set metatable of JSON array: | 
| jbe@125 | 132     lua_pushvalue(L, JSON_UPVAL_METATABLE); | 
| jbe@125 | 133     lua_setmetatable(L, -2); | 
| jbe@136 | 134     // mark JSON array as JSON array (as opposed to JSON object): | 
| jbe@127 | 135     lua_pushvalue(L, -1); | 
| jbe@127 | 136     lua_pushliteral(L, "array"); | 
| jbe@127 | 137     lua_rawset(L, JSON_UPVAL_TYPES); | 
| jbe@136 | 138     // create internal shadow table on stack: | 
| jbe@136 | 139     lua_newtable(L); | 
| jbe@136 | 140     // register internal shadow table: | 
| jbe@123 | 141     lua_pushvalue(L, -2); | 
| jbe@123 | 142     lua_pushvalue(L, -2); | 
| jbe@130 | 143     lua_rawset(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@136 | 144     // increment level: | 
| jbe@121 | 145     level++; | 
| jbe@136 | 146     // expect array value (or end of array) and continue with loop: | 
| jbe@136 | 147     mode = JSON_STATE_ARRAY_VALUE; | 
| jbe@121 | 148     goto json_import_loop; | 
| jbe@136 | 149   // end of JSON object: | 
| jbe@121 | 150   case '}': | 
| jbe@136 | 151     // if end of JSON object is not expected here, then return an error: | 
| jbe@124 | 152     if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR) | 
| jbe@121 | 153       goto json_import_syntax_error; | 
| jbe@136 | 154     // jump to common code for end of JSON object and JSON array: | 
| jbe@121 | 155     goto json_import_close; | 
| jbe@136 | 156   // end of JSON array: | 
| jbe@121 | 157   case ']': | 
| jbe@136 | 158     // if end of JSON array is not expected here, then return an error: | 
| jbe@124 | 159     if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR) | 
| jbe@121 | 160       goto json_import_syntax_error; | 
| jbe@136 | 161     // continue with common code for end of JSON object and JSON array: | 
| jbe@136 | 162   // common code for end of JSON object or JSON array: | 
| jbe@121 | 163   json_import_close: | 
| jbe@136 | 164     // consume input character: | 
| jbe@121 | 165     pos++; | 
| jbe@136 | 166     // pop shadow table: | 
| jbe@136 | 167     lua_pop(L, 1); | 
| jbe@136 | 168     // check if nested: | 
| jbe@121 | 169     if (--level) { | 
| jbe@136 | 170       // if nested, then check if outer(!) structure is an array or object: | 
| jbe@136 | 171       lua_pushvalue(L, c == '}' ? -4 : -3); | 
| jbe@136 | 172       lua_rawget(L, JSON_UPVAL_TYPES); | 
| jbe@136 | 173       if (lua_tostring(L, -1)[0] == 'a') { | 
| jbe@136 | 174         // select array value processing: | 
| jbe@124 | 175         mode = JSON_STATE_ARRAY_VALUE; | 
| jbe@121 | 176       } else { | 
| jbe@136 | 177         // select object value processing: | 
| jbe@124 | 178         mode = JSON_STATE_OBJECT_VALUE; | 
| jbe@121 | 179       } | 
| jbe@136 | 180       // pop JSON type from stack (from rawget JSON_UPVAL_TYPES): | 
| jbe@136 | 181       lua_pop(L, 1); | 
| jbe@136 | 182       // store value in outer structure: | 
| jbe@121 | 183       goto json_import_process_value; | 
| jbe@121 | 184     } | 
| jbe@136 | 185     // if not nested, then expect end of JSON document and continue with loop: | 
| jbe@136 | 186     mode = JSON_STATE_END; | 
| jbe@121 | 187     goto json_import_loop; | 
| jbe@136 | 188   // key terminator: | 
| jbe@121 | 189   case ':': | 
| jbe@136 | 190     // if key terminator is not expected here, then return an error: | 
| jbe@124 | 191     if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR) | 
| jbe@121 | 192       goto json_import_syntax_error; | 
| jbe@136 | 193     // consume input character: | 
| jbe@121 | 194     pos++; | 
| jbe@136 | 195     // set state of parser and continue with loop: | 
| jbe@124 | 196     mode = JSON_STATE_OBJECT_VALUE; | 
| jbe@121 | 197     goto json_import_loop; | 
| jbe@136 | 198   // value terminator (NOTE: trailing comma at end of value or key-value list is tolerated by this parser) | 
| jbe@121 | 199   case ',': | 
| jbe@136 | 200     // change parser state accordingly: | 
| jbe@124 | 201     if (mode == JSON_STATE_OBJECT_SEPARATOR) { | 
| jbe@124 | 202       mode = JSON_STATE_OBJECT_KEY; | 
| jbe@124 | 203     } else if (mode == JSON_STATE_ARRAY_SEPARATOR) { | 
| jbe@124 | 204       mode = JSON_STATE_ARRAY_VALUE; | 
| jbe@121 | 205     } else { | 
| jbe@136 | 206        // if value terminator is not expected here, then return an error: | 
| jbe@136 | 207        goto json_import_syntax_error; | 
| jbe@121 | 208     } | 
| jbe@136 | 209     // consume input character: | 
| jbe@121 | 210     pos++; | 
| jbe@136 | 211     // continue with loop: | 
| jbe@121 | 212     goto json_import_loop; | 
| jbe@136 | 213   // string literal: | 
| jbe@121 | 214   case '"': | 
| jbe@136 | 215     // prepare buffer to decode string (with maximum possible length) and set write position to zero: | 
| jbe@121 | 216     cbuf = luaL_buffinitsize(L, &luabuf, total-pos); | 
| jbe@121 | 217     writepos = 0; | 
| jbe@136 | 218     // consume quote character: | 
| jbe@121 | 219     pos++; | 
| jbe@136 | 220     // read next character until encountering end quote: | 
| jbe@121 | 221     while ((c = str[pos++]) != '"') { | 
| jbe@121 | 222       if (c == 0) { | 
| jbe@136 | 223         // handle unexpected end-of-string: | 
| jbe@121 | 224         goto json_import_unexpected_eof; | 
| jbe@121 | 225       } else if (c < 32 || c == 127) { | 
| jbe@136 | 226         // do not allow ASCII control characters: | 
| jbe@136 | 227         // NOTE: illegal UTF-8 sequences and extended control characters are not sanitized | 
| jbe@136 | 228         //       by this parser to allow different encodings than Unicode | 
| jbe@121 | 229         lua_pushnil(L); | 
| jbe@121 | 230         lua_pushliteral(L, "Unexpected control character in JSON string"); | 
| jbe@121 | 231         return 2; | 
| jbe@121 | 232       } else if (c == '\\') { | 
| jbe@136 | 233         // read next char after backslash escape: | 
| jbe@121 | 234         c = str[pos++]; | 
| jbe@121 | 235         switch (c) { | 
| jbe@136 | 236         // unexpected end-of-string: | 
| jbe@121 | 237         case 0: | 
| jbe@121 | 238           goto json_import_unexpected_eof; | 
| jbe@136 | 239         // unescaping of quotation mark, slash, and backslash: | 
| jbe@121 | 240         case '"': | 
| jbe@121 | 241         case '/': | 
| jbe@121 | 242         case '\\': | 
| jbe@121 | 243           cbuf[writepos++] = c; | 
| jbe@121 | 244           break; | 
| jbe@136 | 245         // unescaping of backspace: | 
| jbe@121 | 246         case 'b': | 
| jbe@121 | 247           cbuf[writepos++] = '\b'; | 
| jbe@121 | 248           break; | 
| jbe@136 | 249         // unescaping of form-feed: | 
| jbe@121 | 250         case 'f': | 
| jbe@121 | 251           cbuf[writepos++] = '\f'; | 
| jbe@121 | 252           break; | 
| jbe@136 | 253         // unescaping of new-line: | 
| jbe@121 | 254         case 'n': | 
| jbe@121 | 255           cbuf[writepos++] = '\n'; | 
| jbe@121 | 256           break; | 
| jbe@136 | 257         // unescaping of carriage-return: | 
| jbe@121 | 258         case 'r': | 
| jbe@121 | 259           cbuf[writepos++] = '\r'; | 
| jbe@121 | 260           break; | 
| jbe@136 | 261         // unescaping of tabulator: | 
| jbe@121 | 262         case 't': | 
| jbe@121 | 263           cbuf[writepos++] = '\t'; | 
| jbe@121 | 264           break; | 
| jbe@136 | 265         // unescaping of UTF-16 characters | 
| jbe@121 | 266         case 'u': | 
| jbe@121 | 267           lua_pushnil(L); | 
| jbe@121 | 268           lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet");  // TODO | 
| jbe@121 | 269           return 2; | 
| jbe@136 | 270         // unexpected escape sequence: | 
| jbe@121 | 271         default: | 
| jbe@121 | 272           lua_pushnil(L); | 
| jbe@121 | 273           lua_pushliteral(L, "Unexpected string escape sequence in JSON document"); | 
| jbe@121 | 274           return 2; | 
| jbe@121 | 275         } | 
| jbe@121 | 276       } else { | 
| jbe@136 | 277         // normal character: | 
| jbe@121 | 278         cbuf[writepos++] = c; | 
| jbe@121 | 279       } | 
| jbe@121 | 280     } | 
| jbe@136 | 281     // process buffer to Lua string: | 
| jbe@121 | 282     luaL_pushresultsize(&luabuf, writepos); | 
| jbe@136 | 283     // continue with processing of decoded string: | 
| jbe@121 | 284     goto json_import_process_value; | 
| jbe@121 | 285   } | 
| jbe@136 | 286   // process values whose type is is not deducible from a single character: | 
| jbe@136 | 287   if ((c >= '0' && c <= '9') || c == '-' || c == '+') { | 
| jbe@136 | 288     // numbers: | 
| jbe@122 | 289     char *endptr; | 
| jbe@122 | 290     double numval; | 
| jbe@122 | 291     numval = strtod(str+pos, &endptr); | 
| jbe@122 | 292     if (endptr == str+pos) goto json_import_syntax_error; | 
| jbe@122 | 293     pos += endptr - (str+pos); | 
| jbe@122 | 294     lua_pushnumber(L, numval); | 
| jbe@122 | 295   } else if (!strncmp(str+pos, "true", 4)) { | 
| jbe@136 | 296     // consume 4 input characters for "true": | 
| jbe@121 | 297     pos += 4; | 
| jbe@136 | 298     // put Lua true value on stack: | 
| jbe@136 | 299     lua_pushboolean(L, 1); | 
| jbe@121 | 300   } else if (!strncmp(str+pos, "false", 5)) { | 
| jbe@136 | 301     // consume 5 input characters for "false": | 
| jbe@121 | 302     pos += 5; | 
| jbe@136 | 303     // put Lua false value on stack: | 
| jbe@136 | 304     lua_pushboolean(L, 0); | 
| jbe@121 | 305   } else if (!strncmp(str+pos, "null", 4)) { | 
| jbe@136 | 306     // consume 4 input characters for "null": | 
| jbe@136 | 307     pos += 4; | 
| jbe@136 | 308     // put special null-marker on stack: | 
| jbe@130 | 309     lua_pushvalue(L, JSON_UPVAL_NULLMARK); | 
| jbe@121 | 310   } else { | 
| jbe@136 | 311     // all other cases are a syntax error: | 
| jbe@121 | 312     goto json_import_syntax_error; | 
| jbe@121 | 313   } | 
| jbe@136 | 314   // process a decoded value or key value pair (expected on top of Lua stack): | 
| jbe@136 | 315   json_import_process_value: | 
| jbe@121 | 316   switch (mode) { | 
| jbe@136 | 317   // an object key has been read: | 
| jbe@124 | 318   case JSON_STATE_OBJECT_KEY: | 
| jbe@136 | 319     // if an object key is not a string, then this is a syntax error: | 
| jbe@121 | 320     if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error; | 
| jbe@136 | 321     // expect key terminator and continue with loop: | 
| jbe@124 | 322     mode = JSON_STATE_OBJECT_KEY_TERMINATOR; | 
| jbe@121 | 323     goto json_import_loop; | 
| jbe@136 | 324   // a key value pair has been read: | 
| jbe@124 | 325   case JSON_STATE_OBJECT_VALUE: | 
| jbe@136 | 326     // store key value pair in outer shadow table: | 
| jbe@130 | 327     lua_rawset(L, -3); | 
| jbe@136 | 328     // expect value terminator (or end of object) and continue with loop: | 
| jbe@124 | 329     mode = JSON_STATE_OBJECT_SEPARATOR; | 
| jbe@121 | 330     goto json_import_loop; | 
| jbe@136 | 331   // an array value has been read: | 
| jbe@124 | 332   case JSON_STATE_ARRAY_VALUE: | 
| jbe@136 | 333     // store value in outer shadow table: | 
| jbe@136 | 334     lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); | 
| jbe@136 | 335     // expect value terminator (or end of object) and continue with loop: | 
| jbe@124 | 336     mode = JSON_STATE_ARRAY_SEPARATOR; | 
| jbe@121 | 337     goto json_import_loop; | 
| jbe@136 | 338   // a single value has been read: | 
| jbe@124 | 339   case JSON_STATE_VALUE: | 
| jbe@136 | 340     // leave value on top of stack, expect end of JSON document, and continue with loop: | 
| jbe@124 | 341     mode = JSON_STATE_END; | 
| jbe@121 | 342     goto json_import_loop; | 
| jbe@121 | 343   } | 
| jbe@136 | 344   // syntax error handling (only reachable by goto statement): | 
| jbe@136 | 345   json_import_syntax_error: | 
| jbe@121 | 346   lua_pushnil(L); | 
| jbe@121 | 347   lua_pushliteral(L, "Syntax error in JSON document"); | 
| jbe@121 | 348   return 2; | 
| jbe@121 | 349 } | 
| jbe@121 | 350 | 
| jbe@137 | 351 // gets a value or its type from a JSON document (first argument) | 
| jbe@137 | 352 // optionally using a path (variable number of keys after first argument): | 
| jbe@137 | 353 static int json_path(lua_State *L, int type_mode) { | 
| jbe@130 | 354   int argc; | 
| jbe@130 | 355   int idx = 2; | 
| jbe@137 | 356   // store number of arguments: | 
| jbe@130 | 357   argc = lua_gettop(L); | 
| jbe@137 | 358   // follow path, starting with first argument as "current value": | 
| jbe@126 | 359   lua_pushvalue(L, 1); | 
| jbe@137 | 360   // process each "path key": | 
| jbe@130 | 361   while (idx <= argc) { | 
| jbe@137 | 362     // if "current value" is nil, then the path cannot be walked and nil is returned: | 
| jbe@137 | 363     if (lua_isnil(L, -1)) return 1; | 
| jbe@137 | 364     // try to get shadow table of "current value": | 
| jbe@130 | 365     lua_pushvalue(L, -1); | 
| jbe@130 | 366     lua_rawget(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@126 | 367     if (lua_isnil(L, -1)) { | 
| jbe@137 | 368       // if no shadow table is found, | 
| jbe@137 | 369       // drop nil from stack: | 
| jbe@126 | 370       lua_pop(L, 1); | 
| jbe@130 | 371       if (lua_type(L, -1) == LUA_TTABLE) { | 
| jbe@137 | 372         // if "current value" is a table, | 
| jbe@137 | 373         // get "next value" using the "path key": | 
| jbe@130 | 374         lua_pushvalue(L, idx++); | 
| jbe@130 | 375         lua_gettable(L, -2); | 
| jbe@130 | 376       } else { | 
| jbe@137 | 377         // if "current value" is not a table, | 
| jbe@137 | 378         // then the path cannot be walked and nil is returned: | 
| jbe@130 | 379         lua_pushnil(L); | 
| jbe@137 | 380         return 1; | 
| jbe@130 | 381       } | 
| jbe@130 | 382     } else { | 
| jbe@137 | 383       // if a shadow table is found, | 
| jbe@137 | 384       // set "current value" to its shadow table: | 
| jbe@130 | 385       lua_replace(L, -2); | 
| jbe@137 | 386       // get "next value" using the "path key": | 
| jbe@130 | 387       lua_pushvalue(L, idx++); | 
| jbe@130 | 388       lua_rawget(L, -2); | 
| jbe@126 | 389     } | 
| jbe@137 | 390     // the "next value" replaces the "current value": | 
| jbe@130 | 391     lua_replace(L, -2); | 
| jbe@126 | 392   } | 
| jbe@137 | 393   if (!type_mode) { | 
| jbe@137 | 394     // if a value (and not its type) was requested, | 
| jbe@137 | 395     // check if value is the null-marker, and store nil on top of Lua stack in that case: | 
| jbe@130 | 396     if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L); | 
| jbe@137 | 397   } else { | 
| jbe@137 | 398     // if the type was requested, | 
| jbe@137 | 399     // check if value is the null-marker: | 
| jbe@130 | 400     if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) { | 
| jbe@137 | 401       // if yes, store string "null" on top of Lua stack: | 
| jbe@130 | 402       lua_pushliteral(L, "null"); | 
| jbe@137 | 403     } else { | 
| jbe@137 | 404       // otherwise, | 
| jbe@137 | 405       // try to read type ("object" or "array") from internal type table: | 
| jbe@137 | 406       lua_pushvalue(L, -1); | 
| jbe@137 | 407       lua_rawget(L, JSON_UPVAL_TYPES); | 
| jbe@137 | 408       // if no entry is found in the internal type table, get the Lua type: | 
| jbe@137 | 409       if (lua_isnil(L, -1)) lua_pushstring(L, lua_typename(L, lua_type(L, -2))); | 
| jbe@126 | 410     } | 
| jbe@126 | 411   } | 
| jbe@137 | 412   // return the top most value on the Lua stack: | 
| jbe@137 | 413   return 1; | 
| jbe@130 | 414 } | 
| jbe@130 | 415 | 
| jbe@137 | 416 // gets a value from a JSON document (first argument) | 
| jbe@137 | 417 // optionally using a path (variable number of keys after first argument): | 
| jbe@130 | 418 static int json_get(lua_State *L) { | 
| jbe@137 | 419   return json_path(L, 0); | 
| jbe@130 | 420 } | 
| jbe@130 | 421 | 
| jbe@137 | 422 // gets a value's type from a JSON document (first argument) | 
| jbe@137 | 423 // optionally using a path (variable number of keys after first argument): | 
| jbe@130 | 424 static int json_type(lua_State *L) { | 
| jbe@137 | 425   return json_path(L, 1); | 
| jbe@130 | 426 } | 
| jbe@130 | 427 | 
| jbe@137 | 428 // checks if a value in a JSON document (first argument) is null: | 
| jbe@130 | 429 static int json_isnull(lua_State *L) { | 
| jbe@137 | 430   const char *jsontype; | 
| jbe@137 | 431   lua_getfield(L, JSON_UPVAL_LIBRARY, "type"); | 
| jbe@137 | 432   lua_insert(L, 1); | 
| jbe@137 | 433   lua_call(L, lua_gettop(L) - 1, 1); | 
| jbe@137 | 434   jsontype = lua_tostring(L, -1); | 
| jbe@137 | 435   if (jsontype && !strcmp(jsontype, "null")) lua_pushboolean(L, 1); | 
| jbe@137 | 436   else lua_pushboolean(L, 0); | 
| jbe@137 | 437   return 1; | 
| jbe@130 | 438 } | 
| jbe@130 | 439 | 
| jbe@131 | 440 static int json_setnull(lua_State *L) { | 
| jbe@131 | 441   lua_settop(L, 2); | 
| jbe@131 | 442   lua_pushvalue(L, JSON_UPVAL_METATABLE); | 
| jbe@131 | 443   lua_setmetatable(L, 1); | 
| jbe@131 | 444   lua_pushvalue(L, 1); | 
| jbe@131 | 445   lua_rawget(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@131 | 446   if (lua_isnil(L, -1)) { | 
| jbe@131 | 447     lua_newtable(L); | 
| jbe@131 | 448     lua_pushvalue(L, 1); | 
| jbe@131 | 449     lua_pushvalue(L, -2); | 
| jbe@131 | 450     lua_rawset(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@131 | 451   } | 
| jbe@131 | 452   lua_pushvalue(L, 2); | 
| jbe@131 | 453   lua_pushvalue(L, JSON_UPVAL_NULLMARK); | 
| jbe@131 | 454   lua_rawset(L, -3); | 
| jbe@131 | 455   return 0; | 
| jbe@131 | 456 } | 
| jbe@131 | 457 | 
| jbe@130 | 458 static int json_len(lua_State *L) { | 
| jbe@130 | 459   lua_settop(L, 1); | 
| jbe@130 | 460   lua_pushvalue(L, 1); | 
| jbe@130 | 461   lua_rawget(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@130 | 462   if (lua_isnil(L, -1)) lua_pop(L, 1); | 
| jbe@130 | 463   lua_pushinteger(L, lua_rawlen(L, -1)); | 
| jbe@123 | 464   return 1; | 
| jbe@123 | 465 } | 
| jbe@123 | 466 | 
| jbe@130 | 467 static int json_index(lua_State *L) { | 
| jbe@130 | 468   lua_settop(L, 2); | 
| jbe@130 | 469   lua_pushvalue(L, 1); | 
| jbe@130 | 470   lua_rawget(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@130 | 471   if (lua_isnil(L, -1)) return 1; | 
| jbe@130 | 472   lua_pushvalue(L, 2); | 
| jbe@130 | 473   lua_rawget(L, -2); | 
| jbe@130 | 474   if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L); | 
| jbe@127 | 475   return 1; | 
| jbe@127 | 476 } | 
| jbe@127 | 477 | 
| jbe@130 | 478 static int json_newindex(lua_State *L) { | 
| jbe@130 | 479   lua_settop(L, 3); | 
| jbe@123 | 480   lua_pushvalue(L, 1); | 
| jbe@130 | 481   lua_rawget(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@130 | 482   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); | 
| jbe@130 | 483   lua_replace(L, 1); | 
| jbe@130 | 484   lua_rawset(L, 1); | 
| jbe@121 | 485   return 1; | 
| jbe@121 | 486 } | 
| jbe@121 | 487 | 
| jbe@135 | 488 static int json_pairs_iterfunc(lua_State *L) { | 
| jbe@135 | 489   lua_settop(L, 2); | 
| jbe@135 | 490   lua_pushvalue(L, 1); | 
| jbe@135 | 491   lua_rawget(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@135 | 492   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); | 
| jbe@135 | 493   lua_pushvalue(L, 2); | 
| jbe@135 | 494   if (!lua_next(L, -2)) return 0; | 
| jbe@135 | 495   if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) { | 
| jbe@135 | 496     lua_pop(L, 1); | 
| jbe@135 | 497     lua_pushnil(L); | 
| jbe@135 | 498   } | 
| jbe@135 | 499   return 2; | 
| jbe@135 | 500 } | 
| jbe@135 | 501 | 
| jbe@135 | 502 static int json_pairs(lua_State *L) { | 
| jbe@135 | 503   lua_pushvalue(L, JSON_UPVAL_PAIRS_ITERFUNC); | 
| jbe@135 | 504   lua_pushvalue(L, 1); | 
| jbe@135 | 505   lua_pushnil(L); | 
| jbe@135 | 506   return 3; | 
| jbe@135 | 507 } | 
| jbe@135 | 508 | 
| jbe@134 | 509 static int json_ipairs_iterfunc(lua_State *L) { | 
| jbe@134 | 510   int idx; | 
| jbe@134 | 511   lua_settop(L, 2); | 
| jbe@134 | 512   idx = lua_tointeger(L, 2) + 1; | 
| jbe@134 | 513   lua_pushvalue(L, 1); | 
| jbe@134 | 514   lua_rawget(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@134 | 515   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); | 
| jbe@134 | 516   lua_rawgeti(L, -1, idx); | 
| jbe@134 | 517   if (lua_isnil(L, -1)) return 0; | 
| jbe@134 | 518   lua_pushinteger(L, idx); | 
| jbe@134 | 519   if (lua_rawequal(L, -2, JSON_UPVAL_NULLMARK)) lua_pushnil(L); | 
| jbe@134 | 520   else lua_pushvalue(L, -2); | 
| jbe@134 | 521   return 2; | 
| jbe@134 | 522 } | 
| jbe@134 | 523 | 
| jbe@134 | 524 static int json_ipairs(lua_State *L) { | 
| jbe@134 | 525   lua_pushvalue(L, JSON_UPVAL_IPAIRS_ITERFUNC); | 
| jbe@134 | 526   lua_pushvalue(L, 1); | 
| jbe@134 | 527   lua_pushinteger(L, 0); | 
| jbe@134 | 528   return 3; | 
| jbe@134 | 529 } | 
| jbe@134 | 530 | 
| jbe@121 | 531 static const struct luaL_Reg json_module_functions[] = { | 
| jbe@133 | 532   {"object", json_object}, | 
| jbe@133 | 533   {"array", json_array}, | 
| jbe@121 | 534   {"import", json_import}, | 
| jbe@130 | 535   {"get", json_get}, | 
| jbe@127 | 536   {"type", json_type}, | 
| jbe@123 | 537   {"isnull", json_isnull}, | 
| jbe@131 | 538   {"setnull", json_setnull}, | 
| jbe@121 | 539   {NULL, NULL} | 
| jbe@121 | 540 }; | 
| jbe@121 | 541 | 
| jbe@126 | 542 static const struct luaL_Reg json_metatable_functions[] = { | 
| jbe@130 | 543   {"__len", json_len}, | 
| jbe@130 | 544   {"__index", json_index}, | 
| jbe@130 | 545   {"__newindex", json_newindex}, | 
| jbe@135 | 546   {"__pairs", json_pairs}, | 
| jbe@134 | 547   {"__ipairs", json_ipairs}, | 
| jbe@126 | 548   {NULL, NULL} | 
| jbe@126 | 549 }; | 
| jbe@126 | 550 | 
| jbe@121 | 551 int luaopen_json(lua_State *L) { | 
| jbe@126 | 552   lua_settop(L, 0); | 
| jbe@137 | 553   lua_newtable(L);      // 1: library table on stack position | 
| jbe@137 | 554   lua_pushvalue(L, 1);  // 2: copy of library table | 
| jbe@137 | 555   lua_newtable(L);      // 3: table used as JSON NULL value in internal shadow tables | 
| jbe@137 | 556   lua_newtable(L);      // 4: ephemeron table to store shadow tables for each JSON object/array to allow NULL values returned as nil | 
| jbe@137 | 557   lua_newtable(L);      // 5: ephemeron table to store the type of the JSON object/array | 
| jbe@137 | 558   lua_newtable(L);      // 6: metatable for ephemeron tables | 
| jbe@121 | 559   lua_pushliteral(L, "__mode"); | 
| jbe@121 | 560   lua_pushliteral(L, "k"); | 
| jbe@137 | 561   lua_rawset(L, 6); | 
| jbe@137 | 562   lua_pushvalue(L, 6);  // 7: cloned metatable reference | 
| jbe@130 | 563   lua_setmetatable(L, 4); | 
| jbe@137 | 564   lua_setmetatable(L, 5); | 
| jbe@137 | 565   lua_newtable(L);      // 6: metatable for JSON objects and JSON arrays | 
| jbe@126 | 566   lua_pushvalue(L, 2); | 
| jbe@126 | 567   lua_pushvalue(L, 3); | 
| jbe@127 | 568   lua_pushvalue(L, 4); | 
| jbe@130 | 569   lua_pushvalue(L, 5); | 
| jbe@137 | 570   lua_pushvalue(L, 6); | 
| jbe@137 | 571   lua_pushcclosure(L, json_pairs_iterfunc, 5);  // 7: iteration function for pairs | 
| jbe@135 | 572   lua_pushvalue(L, 2); | 
| jbe@135 | 573   lua_pushvalue(L, 3); | 
| jbe@135 | 574   lua_pushvalue(L, 4); | 
| jbe@135 | 575   lua_pushvalue(L, 5); | 
| jbe@137 | 576   lua_pushvalue(L, 6); | 
| jbe@137 | 577   lua_pushcclosure(L, json_ipairs_iterfunc, 5);  // 8: iteration function for ipairs | 
| jbe@137 | 578   lua_pushvalue(L, 6); | 
| jbe@134 | 579   lua_pushvalue(L, 2); | 
| jbe@134 | 580   lua_pushvalue(L, 3); | 
| jbe@134 | 581   lua_pushvalue(L, 4); | 
| jbe@134 | 582   lua_pushvalue(L, 5); | 
| jbe@134 | 583   lua_pushvalue(L, 6); | 
| jbe@135 | 584   lua_pushvalue(L, 7); | 
| jbe@137 | 585   lua_pushvalue(L, 8); | 
| jbe@137 | 586   luaL_setfuncs(L, json_metatable_functions, 7); | 
| jbe@135 | 587   lua_setfield(L, 1, "metatable"); | 
| jbe@137 | 588   luaL_setfuncs(L, json_module_functions, 7); | 
| jbe@121 | 589   return 1; | 
| jbe@121 | 590 } |