| 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@130 | 6 #define JSON_UPVAL_NULLMARK  lua_upvalueindex(1) | 
| jbe@130 | 7 #define JSON_UPVAL_SHADOWTBL lua_upvalueindex(2) | 
| jbe@130 | 8 #define JSON_UPVAL_TYPES     lua_upvalueindex(3) | 
| jbe@130 | 9 #define JSON_UPVAL_METATABLE lua_upvalueindex(4) | 
| jbe@123 | 10 | 
| jbe@124 | 11 #define JSON_STATE_VALUE 0 | 
| jbe@124 | 12 #define JSON_STATE_OBJECT_KEY 1 | 
| jbe@124 | 13 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2 | 
| jbe@124 | 14 #define JSON_STATE_OBJECT_VALUE 3 | 
| jbe@124 | 15 #define JSON_STATE_OBJECT_SEPARATOR 4 | 
| jbe@124 | 16 #define JSON_STATE_ARRAY_VALUE 5 | 
| jbe@124 | 17 #define JSON_STATE_ARRAY_SEPARATOR 6 | 
| jbe@124 | 18 #define JSON_STATE_END 7 | 
| jbe@121 | 19 | 
| jbe@121 | 20 static int json_import(lua_State *L) { | 
| jbe@121 | 21   const char *str; | 
| jbe@121 | 22   size_t total; | 
| jbe@121 | 23   size_t pos = 0; | 
| jbe@121 | 24   size_t level = 0; | 
| jbe@124 | 25   int mode = JSON_STATE_VALUE; | 
| jbe@121 | 26   char c; | 
| jbe@121 | 27   luaL_Buffer luabuf; | 
| jbe@121 | 28   char *cbuf; | 
| jbe@121 | 29   size_t writepos; | 
| jbe@121 | 30   lua_settop(L, 1); | 
| jbe@121 | 31   str = lua_tostring(L, 1); | 
| jbe@121 | 32   total = strlen(str); | 
| jbe@121 | 33 json_import_loop: | 
| jbe@121 | 34   while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++; | 
| jbe@121 | 35   switch (c) { | 
| jbe@121 | 36   case 0: | 
| jbe@124 | 37     if (mode == JSON_STATE_END) return 1; | 
| jbe@121 | 38     json_import_unexpected_eof: | 
| jbe@121 | 39     lua_pushnil(L); | 
| jbe@121 | 40     if (level == 0) lua_pushliteral(L, "Empty string"); | 
| jbe@121 | 41     else lua_pushliteral(L, "Unexpected end of JSON document"); | 
| jbe@121 | 42     return 2; | 
| jbe@121 | 43   case '{': | 
| jbe@124 | 44     if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE) | 
| jbe@121 | 45       goto json_import_syntax_error; | 
| jbe@121 | 46     pos++; | 
| jbe@130 | 47     lua_newtable(L);  // the external JSON object representation | 
| jbe@125 | 48     lua_pushvalue(L, JSON_UPVAL_METATABLE); | 
| jbe@125 | 49     lua_setmetatable(L, -2); | 
| jbe@127 | 50     lua_pushvalue(L, -1); | 
| jbe@127 | 51     lua_pushliteral(L, "object"); | 
| jbe@127 | 52     lua_rawset(L, JSON_UPVAL_TYPES); | 
| jbe@130 | 53     lua_newtable(L);  // the internal shadow table | 
| jbe@123 | 54     lua_pushvalue(L, -2); | 
| jbe@123 | 55     lua_pushvalue(L, -2); | 
| jbe@130 | 56     lua_rawset(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@124 | 57     mode = JSON_STATE_OBJECT_KEY; | 
| jbe@121 | 58     level++; | 
| jbe@121 | 59     goto json_import_loop; | 
| jbe@121 | 60   case '[': | 
| jbe@124 | 61     if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE) | 
| jbe@121 | 62       goto json_import_syntax_error; | 
| jbe@121 | 63     pos++; | 
| jbe@130 | 64     lua_newtable(L);  // the external JSON array representation | 
| jbe@125 | 65     lua_pushvalue(L, JSON_UPVAL_METATABLE); | 
| jbe@125 | 66     lua_setmetatable(L, -2); | 
| jbe@127 | 67     lua_pushvalue(L, -1); | 
| jbe@127 | 68     lua_pushliteral(L, "array"); | 
| jbe@127 | 69     lua_rawset(L, JSON_UPVAL_TYPES); | 
| jbe@130 | 70     lua_newtable(L);  // the internal shadow table | 
| jbe@123 | 71     lua_pushvalue(L, -2); | 
| jbe@123 | 72     lua_pushvalue(L, -2); | 
| jbe@130 | 73     lua_rawset(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@130 | 74     lua_pushinteger(L, 0);  // magic integer to indicate an array | 
| jbe@124 | 75     mode = JSON_STATE_ARRAY_VALUE; | 
| jbe@121 | 76     level++; | 
| jbe@121 | 77     goto json_import_loop; | 
| jbe@121 | 78   case '}': | 
| jbe@124 | 79     if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR) | 
| jbe@121 | 80       goto json_import_syntax_error; | 
| jbe@121 | 81     goto json_import_close; | 
| jbe@121 | 82   case ']': | 
| jbe@124 | 83     if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR) | 
| jbe@121 | 84       goto json_import_syntax_error; | 
| jbe@130 | 85     lua_pop(L, 1);  // pop magic integer | 
| jbe@121 | 86   json_import_close: | 
| jbe@121 | 87     pos++; | 
| jbe@130 | 88     lua_pop(L, 1);  // pop shadow table | 
| jbe@121 | 89     if (--level) { | 
| jbe@121 | 90       if (lua_type(L, -2) == LUA_TNUMBER) { | 
| jbe@124 | 91         mode = JSON_STATE_ARRAY_VALUE; | 
| jbe@121 | 92       } else { | 
| jbe@124 | 93         mode = JSON_STATE_OBJECT_VALUE; | 
| jbe@121 | 94       } | 
| jbe@121 | 95       goto json_import_process_value; | 
| jbe@121 | 96     } else { | 
| jbe@124 | 97       mode = JSON_STATE_END; | 
| jbe@121 | 98     } | 
| jbe@121 | 99     goto json_import_loop; | 
| jbe@121 | 100   case ':': | 
| jbe@124 | 101     if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR) | 
| jbe@121 | 102       goto json_import_syntax_error; | 
| jbe@121 | 103     pos++; | 
| jbe@124 | 104     mode = JSON_STATE_OBJECT_VALUE; | 
| jbe@121 | 105     goto json_import_loop; | 
| jbe@121 | 106   case ',': | 
| jbe@124 | 107     if (mode == JSON_STATE_OBJECT_SEPARATOR) { | 
| jbe@124 | 108       mode = JSON_STATE_OBJECT_KEY; | 
| jbe@124 | 109     } else if (mode == JSON_STATE_ARRAY_SEPARATOR) { | 
| jbe@124 | 110       mode = JSON_STATE_ARRAY_VALUE; | 
| jbe@121 | 111     } else { | 
| jbe@121 | 112       goto json_import_syntax_error; | 
| jbe@121 | 113     } | 
| jbe@121 | 114     pos++; | 
| jbe@121 | 115     goto json_import_loop; | 
| jbe@121 | 116   case '"': | 
| jbe@121 | 117     cbuf = luaL_buffinitsize(L, &luabuf, total-pos); | 
| jbe@121 | 118     writepos = 0; | 
| jbe@121 | 119     pos++; | 
| jbe@121 | 120     while ((c = str[pos++]) != '"') { | 
| jbe@121 | 121       if (c == 0) { | 
| jbe@121 | 122         goto json_import_unexpected_eof; | 
| jbe@121 | 123       } else if (c < 32 || c == 127) { | 
| jbe@121 | 124         lua_pushnil(L); | 
| jbe@121 | 125         lua_pushliteral(L, "Unexpected control character in JSON string"); | 
| jbe@121 | 126         return 2; | 
| jbe@121 | 127       } else if (c == '\\') { | 
| jbe@121 | 128         c = str[pos++]; | 
| jbe@121 | 129         switch (c) { | 
| jbe@121 | 130         case 0: | 
| jbe@121 | 131           goto json_import_unexpected_eof; | 
| jbe@121 | 132         case '"': | 
| jbe@121 | 133         case '/': | 
| jbe@121 | 134         case '\\': | 
| jbe@121 | 135           cbuf[writepos++] = c; | 
| jbe@121 | 136           break; | 
| jbe@121 | 137         case 'b': | 
| jbe@121 | 138           cbuf[writepos++] = '\b'; | 
| jbe@121 | 139           break; | 
| jbe@121 | 140         case 'f': | 
| jbe@121 | 141           cbuf[writepos++] = '\f'; | 
| jbe@121 | 142           break; | 
| jbe@121 | 143         case 'n': | 
| jbe@121 | 144           cbuf[writepos++] = '\n'; | 
| jbe@121 | 145           break; | 
| jbe@121 | 146         case 'r': | 
| jbe@121 | 147           cbuf[writepos++] = '\r'; | 
| jbe@121 | 148           break; | 
| jbe@121 | 149         case 't': | 
| jbe@121 | 150           cbuf[writepos++] = '\t'; | 
| jbe@121 | 151           break; | 
| jbe@121 | 152         case 'u': | 
| jbe@121 | 153           lua_pushnil(L); | 
| jbe@121 | 154           lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet");  // TODO | 
| jbe@121 | 155           return 2; | 
| jbe@121 | 156         default: | 
| jbe@121 | 157           lua_pushnil(L); | 
| jbe@121 | 158           lua_pushliteral(L, "Unexpected string escape sequence in JSON document"); | 
| jbe@121 | 159           return 2; | 
| jbe@121 | 160         } | 
| jbe@121 | 161       } else { | 
| jbe@121 | 162         cbuf[writepos++] = c; | 
| jbe@121 | 163       } | 
| jbe@121 | 164     } | 
| jbe@121 | 165     if (!c) goto json_import_unexpected_eof; | 
| jbe@121 | 166     luaL_pushresultsize(&luabuf, writepos); | 
| jbe@121 | 167     goto json_import_process_value; | 
| jbe@121 | 168   } | 
| jbe@122 | 169   if (c == '-' || (c >= '0' && c <= '9')) { | 
| jbe@122 | 170     char *endptr; | 
| jbe@122 | 171     double numval; | 
| jbe@122 | 172     numval = strtod(str+pos, &endptr); | 
| jbe@122 | 173     if (endptr == str+pos) goto json_import_syntax_error; | 
| jbe@122 | 174     pos += endptr - (str+pos); | 
| jbe@122 | 175     lua_pushnumber(L, numval); | 
| jbe@122 | 176   } else if (!strncmp(str+pos, "true", 4)) { | 
| jbe@121 | 177     lua_pushboolean(L, 1); | 
| jbe@121 | 178     pos += 4; | 
| jbe@121 | 179   } else if (!strncmp(str+pos, "false", 5)) { | 
| jbe@121 | 180     lua_pushboolean(L, 0); | 
| jbe@121 | 181     pos += 5; | 
| jbe@121 | 182   } else if (!strncmp(str+pos, "null", 4)) { | 
| jbe@130 | 183     lua_pushvalue(L, JSON_UPVAL_NULLMARK); | 
| jbe@121 | 184     pos += 4; | 
| jbe@121 | 185   } else { | 
| jbe@121 | 186     goto json_import_syntax_error; | 
| jbe@121 | 187   } | 
| jbe@121 | 188 json_import_process_value: | 
| jbe@121 | 189   switch (mode) { | 
| jbe@124 | 190   case JSON_STATE_OBJECT_KEY: | 
| jbe@121 | 191     if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error; | 
| jbe@124 | 192     mode = JSON_STATE_OBJECT_KEY_TERMINATOR; | 
| jbe@121 | 193     goto json_import_loop; | 
| jbe@124 | 194   case JSON_STATE_OBJECT_VALUE: | 
| jbe@130 | 195     lua_rawset(L, -3); | 
| jbe@124 | 196     mode = JSON_STATE_OBJECT_SEPARATOR; | 
| jbe@121 | 197     goto json_import_loop; | 
| jbe@124 | 198   case JSON_STATE_ARRAY_VALUE: | 
| jbe@130 | 199     lua_rawseti(L, -3, lua_rawlen(L, -3) + 1); | 
| jbe@124 | 200     mode = JSON_STATE_ARRAY_SEPARATOR; | 
| jbe@121 | 201     goto json_import_loop; | 
| jbe@124 | 202   case JSON_STATE_VALUE: | 
| jbe@124 | 203     mode = JSON_STATE_END; | 
| jbe@121 | 204     goto json_import_loop; | 
| jbe@121 | 205   } | 
| jbe@121 | 206 json_import_syntax_error: | 
| jbe@121 | 207   lua_pushnil(L); | 
| jbe@121 | 208   lua_pushliteral(L, "Syntax error in JSON document"); | 
| jbe@121 | 209   return 2; | 
| jbe@121 | 210 } | 
| jbe@121 | 211 | 
| jbe@130 | 212 #define JSON_PATH_GET 1 | 
| jbe@130 | 213 #define JSON_PATH_TYPE 2 | 
| jbe@130 | 214 #define JSON_PATH_ISNULL 3 | 
| jbe@130 | 215 | 
| jbe@130 | 216 static int json_path(lua_State *L, int mode) { | 
| jbe@130 | 217   int argc; | 
| jbe@130 | 218   int idx = 2; | 
| jbe@130 | 219   argc = lua_gettop(L); | 
| jbe@126 | 220   lua_pushvalue(L, 1); | 
| jbe@130 | 221   while (idx <= argc) { | 
| jbe@132 | 222     if (lua_isnil(L, -1)) { | 
| jbe@132 | 223       if (mode == JSON_PATH_ISNULL) lua_pushboolean(L, 0); | 
| jbe@132 | 224       return 1; | 
| jbe@132 | 225     } | 
| jbe@130 | 226     lua_pushvalue(L, -1); | 
| jbe@130 | 227     lua_rawget(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@126 | 228     if (lua_isnil(L, -1)) { | 
| jbe@126 | 229       lua_pop(L, 1); | 
| jbe@130 | 230       if (lua_type(L, -1) == LUA_TTABLE) { | 
| jbe@130 | 231         lua_pushvalue(L, idx++); | 
| jbe@130 | 232         lua_gettable(L, -2); | 
| jbe@130 | 233       } else { | 
| jbe@130 | 234         lua_pushnil(L); | 
| jbe@130 | 235       } | 
| jbe@130 | 236     } else { | 
| jbe@130 | 237       lua_replace(L, -2); | 
| jbe@130 | 238       lua_pushvalue(L, idx++); | 
| jbe@130 | 239       lua_rawget(L, -2); | 
| jbe@126 | 240     } | 
| jbe@130 | 241     lua_replace(L, -2); | 
| jbe@126 | 242   } | 
| jbe@130 | 243   switch (mode) { | 
| jbe@130 | 244   case JSON_PATH_GET: | 
| jbe@130 | 245     if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L); | 
| jbe@130 | 246     return 1; | 
| jbe@130 | 247   case JSON_PATH_TYPE: | 
| jbe@130 | 248     if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) { | 
| jbe@130 | 249       lua_pushliteral(L, "null"); | 
| jbe@130 | 250       return 1; | 
| jbe@126 | 251     } | 
| jbe@130 | 252     lua_pushvalue(L, -1); | 
| jbe@130 | 253     lua_rawget(L, JSON_UPVAL_TYPES); | 
| jbe@130 | 254     if (lua_isnil(L, -1)) lua_pushstring(L, lua_typename(L, lua_type(L, -2))); | 
| jbe@130 | 255     return 1; | 
| jbe@130 | 256   case JSON_PATH_ISNULL: | 
| jbe@130 | 257     lua_pushboolean(L, lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)); | 
| jbe@130 | 258     return 1; | 
| jbe@126 | 259   } | 
| jbe@130 | 260   return 0; | 
| jbe@130 | 261 } | 
| jbe@130 | 262 | 
| jbe@130 | 263 static int json_get(lua_State *L) { | 
| jbe@130 | 264   return json_path(L, JSON_PATH_GET); | 
| jbe@130 | 265 } | 
| jbe@130 | 266 | 
| jbe@130 | 267 static int json_type(lua_State *L) { | 
| jbe@130 | 268   return json_path(L, JSON_PATH_TYPE); | 
| jbe@130 | 269 } | 
| jbe@130 | 270 | 
| jbe@130 | 271 static int json_isnull(lua_State *L) { | 
| jbe@130 | 272   return json_path(L, JSON_PATH_ISNULL); | 
| jbe@130 | 273 } | 
| jbe@130 | 274 | 
| jbe@131 | 275 static int json_setnull(lua_State *L) { | 
| jbe@131 | 276   lua_settop(L, 2); | 
| jbe@131 | 277   lua_pushvalue(L, JSON_UPVAL_METATABLE); | 
| jbe@131 | 278   lua_setmetatable(L, 1); | 
| jbe@131 | 279   lua_pushvalue(L, 1); | 
| jbe@131 | 280   lua_rawget(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@131 | 281   if (lua_isnil(L, -1)) { | 
| jbe@131 | 282     lua_newtable(L); | 
| jbe@131 | 283     lua_pushvalue(L, 1); | 
| jbe@131 | 284     lua_pushvalue(L, -2); | 
| jbe@131 | 285     lua_rawset(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@131 | 286   } | 
| jbe@131 | 287   lua_pushvalue(L, 2); | 
| jbe@131 | 288   lua_pushvalue(L, JSON_UPVAL_NULLMARK); | 
| jbe@131 | 289   lua_rawset(L, -3); | 
| jbe@131 | 290   return 0; | 
| jbe@131 | 291 } | 
| jbe@131 | 292 | 
| jbe@130 | 293 static int json_len(lua_State *L) { | 
| jbe@130 | 294   lua_settop(L, 1); | 
| jbe@130 | 295   lua_pushvalue(L, 1); | 
| jbe@130 | 296   lua_rawget(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@130 | 297   if (lua_isnil(L, -1)) lua_pop(L, 1); | 
| jbe@130 | 298   lua_pushinteger(L, lua_rawlen(L, -1)); | 
| jbe@123 | 299   return 1; | 
| jbe@123 | 300 } | 
| jbe@123 | 301 | 
| jbe@130 | 302 static int json_index(lua_State *L) { | 
| jbe@130 | 303   lua_settop(L, 2); | 
| jbe@130 | 304   lua_pushvalue(L, 1); | 
| jbe@130 | 305   lua_rawget(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@130 | 306   if (lua_isnil(L, -1)) return 1; | 
| jbe@130 | 307   lua_pushvalue(L, 2); | 
| jbe@130 | 308   lua_rawget(L, -2); | 
| jbe@130 | 309   if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L); | 
| jbe@127 | 310   return 1; | 
| jbe@127 | 311 } | 
| jbe@127 | 312 | 
| jbe@130 | 313 static int json_newindex(lua_State *L) { | 
| jbe@130 | 314   lua_settop(L, 3); | 
| jbe@123 | 315   lua_pushvalue(L, 1); | 
| jbe@130 | 316   lua_rawget(L, JSON_UPVAL_SHADOWTBL); | 
| jbe@130 | 317   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); | 
| jbe@130 | 318   lua_replace(L, 1); | 
| jbe@130 | 319   lua_rawset(L, 1); | 
| jbe@121 | 320   return 1; | 
| jbe@121 | 321 } | 
| jbe@121 | 322 | 
| jbe@121 | 323 static const struct luaL_Reg json_module_functions[] = { | 
| jbe@121 | 324   {"import", json_import}, | 
| jbe@130 | 325   {"get", json_get}, | 
| jbe@127 | 326   {"type", json_type}, | 
| jbe@123 | 327   {"isnull", json_isnull}, | 
| jbe@131 | 328   {"setnull", json_setnull}, | 
| jbe@121 | 329   {NULL, NULL} | 
| jbe@121 | 330 }; | 
| jbe@121 | 331 | 
| jbe@126 | 332 static const struct luaL_Reg json_metatable_functions[] = { | 
| jbe@130 | 333   {"__len", json_len}, | 
| jbe@130 | 334   {"__index", json_index}, | 
| jbe@130 | 335   {"__newindex", json_newindex}, | 
| jbe@126 | 336   {NULL, NULL} | 
| jbe@126 | 337 }; | 
| jbe@126 | 338 | 
| jbe@121 | 339 int luaopen_json(lua_State *L) { | 
| jbe@126 | 340   lua_settop(L, 0); | 
| jbe@126 | 341   lua_newtable(L);  // 1: library table on stack position | 
| jbe@130 | 342   lua_newtable(L);  // 2: table used as JSON NULL value in internal shadow tables | 
| jbe@130 | 343   lua_newtable(L);  // 3: ephemeron table to store shadow tables for each JSON object/array to allow NULL values returned as nil | 
| jbe@130 | 344   lua_newtable(L);  // 4: ephemeron table to store the type of the JSON object/array | 
| jbe@130 | 345   lua_newtable(L);  // 5: metatable for ephemeron tables | 
| jbe@121 | 346   lua_pushliteral(L, "__mode"); | 
| jbe@121 | 347   lua_pushliteral(L, "k"); | 
| jbe@130 | 348   lua_rawset(L, 5); | 
| jbe@130 | 349   lua_pushvalue(L, 5);  // 6: cloned metatable reference | 
| jbe@127 | 350   lua_setmetatable(L, 3); | 
| jbe@130 | 351   lua_setmetatable(L, 4); | 
| jbe@130 | 352   lua_newtable(L);  // 5: metatable for JSON objects and JSON arrays | 
| jbe@130 | 353   lua_pushvalue(L, 5); | 
| jbe@126 | 354   lua_setfield(L, 1, "metatable"); | 
| jbe@126 | 355   lua_pushvalue(L, 2); | 
| jbe@126 | 356   lua_pushvalue(L, 3); | 
| jbe@127 | 357   lua_pushvalue(L, 4); | 
| jbe@130 | 358   lua_pushvalue(L, 5); | 
| jbe@130 | 359   luaL_setfuncs(L, json_metatable_functions, 4); | 
| jbe@130 | 360   luaL_setfuncs(L, json_module_functions, 4); | 
| jbe@121 | 361   return 1; | 
| jbe@121 | 362 } |