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