| 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@125 | 6 #define JSON_UPVAL_METATABLE lua_upvalueindex(1) | 
| jbe@123 | 7 #define JSON_UPVAL_NULLS lua_upvalueindex(2) | 
| jbe@125 | 8 #define JSON_UPVAL_ARYLEN lua_upvalueindex(3) | 
| jbe@123 | 9 | 
| jbe@124 | 10 #define JSON_STATE_VALUE 0 | 
| jbe@124 | 11 #define JSON_STATE_OBJECT_KEY 1 | 
| jbe@124 | 12 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2 | 
| jbe@124 | 13 #define JSON_STATE_OBJECT_VALUE 3 | 
| jbe@124 | 14 #define JSON_STATE_OBJECT_SEPARATOR 4 | 
| jbe@124 | 15 #define JSON_STATE_ARRAY_VALUE 5 | 
| jbe@124 | 16 #define JSON_STATE_ARRAY_SEPARATOR 6 | 
| jbe@124 | 17 #define JSON_STATE_END 7 | 
| jbe@121 | 18 | 
| jbe@121 | 19 static int json_import(lua_State *L) { | 
| jbe@121 | 20   const char *str; | 
| jbe@121 | 21   size_t total; | 
| jbe@121 | 22   size_t pos = 0; | 
| jbe@121 | 23   size_t level = 0; | 
| jbe@124 | 24   int mode = JSON_STATE_VALUE; | 
| jbe@121 | 25   char c; | 
| jbe@121 | 26   luaL_Buffer luabuf; | 
| jbe@121 | 27   char *cbuf; | 
| jbe@121 | 28   size_t writepos; | 
| jbe@121 | 29   int aryidx; | 
| 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@125 | 47     lua_newtable(L);  // the actual JSON object | 
| jbe@125 | 48     lua_pushvalue(L, JSON_UPVAL_METATABLE); | 
| jbe@125 | 49     lua_setmetatable(L, -2); | 
| jbe@123 | 50     lua_newtable(L);  // stores set of NULL values | 
| jbe@123 | 51     lua_pushvalue(L, -2); | 
| jbe@123 | 52     lua_pushvalue(L, -2); | 
| jbe@123 | 53     lua_rawset(L, JSON_UPVAL_NULLS); | 
| jbe@124 | 54     mode = JSON_STATE_OBJECT_KEY; | 
| jbe@121 | 55     level++; | 
| jbe@121 | 56     goto json_import_loop; | 
| jbe@121 | 57   case '[': | 
| jbe@124 | 58     if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE) | 
| jbe@121 | 59       goto json_import_syntax_error; | 
| jbe@121 | 60     pos++; | 
| jbe@125 | 61     lua_newtable(L);  // the actual JSON array | 
| jbe@125 | 62     lua_pushvalue(L, JSON_UPVAL_METATABLE); | 
| jbe@125 | 63     lua_setmetatable(L, -2); | 
| jbe@123 | 64     lua_newtable(L);  // stores set of NULL values | 
| jbe@123 | 65     lua_pushvalue(L, -2); | 
| jbe@123 | 66     lua_pushvalue(L, -2); | 
| jbe@123 | 67     lua_rawset(L, JSON_UPVAL_NULLS); | 
| jbe@121 | 68     lua_pushinteger(L, 0);  // length of array (since it may contain nil's) | 
| jbe@124 | 69     mode = JSON_STATE_ARRAY_VALUE; | 
| jbe@121 | 70     level++; | 
| jbe@121 | 71     goto json_import_loop; | 
| jbe@121 | 72   case '}': | 
| jbe@124 | 73     if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR) | 
| jbe@121 | 74       goto json_import_syntax_error; | 
| jbe@121 | 75     goto json_import_close; | 
| jbe@121 | 76   case ']': | 
| jbe@124 | 77     if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR) | 
| jbe@121 | 78       goto json_import_syntax_error; | 
| jbe@121 | 79     lua_pushvalue(L, -2);  // use array table as key | 
| jbe@121 | 80     lua_insert(L, -2);  // use length of array as value | 
| jbe@123 | 81     lua_rawset(L, JSON_UPVAL_ARYLEN);  // store length in ephemeron table | 
| jbe@121 | 82     // leaves array table on top of stack | 
| jbe@121 | 83   json_import_close: | 
| jbe@121 | 84     pos++; | 
| jbe@123 | 85     lua_pop(L, 1);  // pop table that stores set of NULL values | 
| jbe@121 | 86     if (--level) { | 
| jbe@121 | 87       if (lua_type(L, -2) == LUA_TNUMBER) { | 
| jbe@124 | 88         mode = JSON_STATE_ARRAY_VALUE; | 
| jbe@121 | 89       } else { | 
| jbe@124 | 90         mode = JSON_STATE_OBJECT_VALUE; | 
| jbe@121 | 91       } | 
| jbe@121 | 92       goto json_import_process_value; | 
| jbe@121 | 93     } else { | 
| jbe@124 | 94       mode = JSON_STATE_END; | 
| jbe@121 | 95     } | 
| jbe@121 | 96     goto json_import_loop; | 
| jbe@121 | 97   case ':': | 
| jbe@124 | 98     if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR) | 
| jbe@121 | 99       goto json_import_syntax_error; | 
| jbe@121 | 100     pos++; | 
| jbe@124 | 101     mode = JSON_STATE_OBJECT_VALUE; | 
| jbe@121 | 102     goto json_import_loop; | 
| jbe@121 | 103   case ',': | 
| jbe@124 | 104     if (mode == JSON_STATE_OBJECT_SEPARATOR) { | 
| jbe@124 | 105       mode = JSON_STATE_OBJECT_KEY; | 
| jbe@124 | 106     } else if (mode == JSON_STATE_ARRAY_SEPARATOR) { | 
| jbe@124 | 107       mode = JSON_STATE_ARRAY_VALUE; | 
| jbe@121 | 108     } else { | 
| jbe@121 | 109       goto json_import_syntax_error; | 
| jbe@121 | 110     } | 
| jbe@121 | 111     pos++; | 
| jbe@121 | 112     goto json_import_loop; | 
| jbe@121 | 113   case '"': | 
| jbe@121 | 114     cbuf = luaL_buffinitsize(L, &luabuf, total-pos); | 
| jbe@121 | 115     writepos = 0; | 
| jbe@121 | 116     pos++; | 
| jbe@121 | 117     while ((c = str[pos++]) != '"') { | 
| jbe@121 | 118       if (c == 0) { | 
| jbe@121 | 119         goto json_import_unexpected_eof; | 
| jbe@121 | 120       } else if (c < 32 || c == 127) { | 
| jbe@121 | 121         lua_pushnil(L); | 
| jbe@121 | 122         lua_pushliteral(L, "Unexpected control character in JSON string"); | 
| jbe@121 | 123         return 2; | 
| jbe@121 | 124       } else if (c == '\\') { | 
| jbe@121 | 125         c = str[pos++]; | 
| jbe@121 | 126         switch (c) { | 
| jbe@121 | 127         case 0: | 
| jbe@121 | 128           goto json_import_unexpected_eof; | 
| jbe@121 | 129         case '"': | 
| jbe@121 | 130         case '/': | 
| jbe@121 | 131         case '\\': | 
| jbe@121 | 132           cbuf[writepos++] = c; | 
| jbe@121 | 133           break; | 
| jbe@121 | 134         case 'b': | 
| jbe@121 | 135           cbuf[writepos++] = '\b'; | 
| jbe@121 | 136           break; | 
| jbe@121 | 137         case 'f': | 
| jbe@121 | 138           cbuf[writepos++] = '\f'; | 
| jbe@121 | 139           break; | 
| jbe@121 | 140         case 'n': | 
| jbe@121 | 141           cbuf[writepos++] = '\n'; | 
| jbe@121 | 142           break; | 
| jbe@121 | 143         case 'r': | 
| jbe@121 | 144           cbuf[writepos++] = '\r'; | 
| jbe@121 | 145           break; | 
| jbe@121 | 146         case 't': | 
| jbe@121 | 147           cbuf[writepos++] = '\t'; | 
| jbe@121 | 148           break; | 
| jbe@121 | 149         case 'u': | 
| jbe@121 | 150           lua_pushnil(L); | 
| jbe@121 | 151           lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet");  // TODO | 
| jbe@121 | 152           return 2; | 
| jbe@121 | 153         default: | 
| jbe@121 | 154           lua_pushnil(L); | 
| jbe@121 | 155           lua_pushliteral(L, "Unexpected string escape sequence in JSON document"); | 
| jbe@121 | 156           return 2; | 
| jbe@121 | 157         } | 
| jbe@121 | 158       } else { | 
| jbe@121 | 159         cbuf[writepos++] = c; | 
| jbe@121 | 160       } | 
| jbe@121 | 161     } | 
| jbe@121 | 162     if (!c) goto json_import_unexpected_eof; | 
| jbe@121 | 163     luaL_pushresultsize(&luabuf, writepos); | 
| jbe@121 | 164     goto json_import_process_value; | 
| jbe@121 | 165   } | 
| jbe@122 | 166   if (c == '-' || (c >= '0' && c <= '9')) { | 
| jbe@122 | 167     char *endptr; | 
| jbe@122 | 168     double numval; | 
| jbe@122 | 169     numval = strtod(str+pos, &endptr); | 
| jbe@122 | 170     if (endptr == str+pos) goto json_import_syntax_error; | 
| jbe@122 | 171     pos += endptr - (str+pos); | 
| jbe@122 | 172     lua_pushnumber(L, numval); | 
| jbe@122 | 173   } else if (!strncmp(str+pos, "true", 4)) { | 
| jbe@121 | 174     lua_pushboolean(L, 1); | 
| jbe@121 | 175     pos += 4; | 
| jbe@121 | 176   } else if (!strncmp(str+pos, "false", 5)) { | 
| jbe@121 | 177     lua_pushboolean(L, 0); | 
| jbe@121 | 178     pos += 5; | 
| jbe@121 | 179   } else if (!strncmp(str+pos, "null", 4)) { | 
| jbe@121 | 180     lua_pushnil(L); | 
| jbe@121 | 181     pos += 4; | 
| jbe@121 | 182   } else { | 
| jbe@121 | 183     goto json_import_syntax_error; | 
| jbe@121 | 184   } | 
| jbe@121 | 185 json_import_process_value: | 
| jbe@121 | 186   switch (mode) { | 
| jbe@124 | 187   case JSON_STATE_OBJECT_KEY: | 
| jbe@121 | 188     if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error; | 
| jbe@124 | 189     mode = JSON_STATE_OBJECT_KEY_TERMINATOR; | 
| jbe@121 | 190     goto json_import_loop; | 
| jbe@124 | 191   case JSON_STATE_OBJECT_VALUE: | 
| jbe@123 | 192     if (lua_isnil(L, -1)) { | 
| jbe@123 | 193       lua_pushvalue(L, -2); | 
| jbe@123 | 194       lua_pushboolean(L, 1); | 
| jbe@123 | 195       lua_rawset(L, -5); | 
| jbe@123 | 196     } | 
| jbe@123 | 197     lua_rawset(L, -4); | 
| jbe@124 | 198     mode = JSON_STATE_OBJECT_SEPARATOR; | 
| jbe@121 | 199     goto json_import_loop; | 
| jbe@124 | 200   case JSON_STATE_ARRAY_VALUE: | 
| jbe@121 | 201     aryidx = lua_tointeger(L, -2) + 1; | 
| jbe@123 | 202     if (lua_isnil(L, -1)) { | 
| jbe@123 | 203       lua_pushinteger(L, aryidx); | 
| jbe@123 | 204       lua_pushboolean(L, 1); | 
| jbe@123 | 205       lua_rawset(L, -5); | 
| jbe@123 | 206     } | 
| jbe@123 | 207     lua_rawseti(L, -4, aryidx); | 
| jbe@121 | 208     lua_pop(L, 1); | 
| jbe@121 | 209     lua_pushinteger(L, aryidx); | 
| jbe@124 | 210     mode = JSON_STATE_ARRAY_SEPARATOR; | 
| jbe@121 | 211     goto json_import_loop; | 
| jbe@124 | 212   case JSON_STATE_VALUE: | 
| jbe@124 | 213     mode = JSON_STATE_END; | 
| jbe@121 | 214     goto json_import_loop; | 
| jbe@121 | 215   } | 
| jbe@121 | 216 json_import_syntax_error: | 
| jbe@121 | 217   lua_pushnil(L); | 
| jbe@121 | 218   lua_pushliteral(L, "Syntax error in JSON document"); | 
| jbe@121 | 219   return 2; | 
| jbe@121 | 220 } | 
| jbe@121 | 221 | 
| jbe@121 | 222 static int json_arylen(lua_State *L) { | 
| jbe@121 | 223   lua_settop(L, 1); | 
| jbe@123 | 224   lua_rawget(L, JSON_UPVAL_ARYLEN); | 
| jbe@123 | 225   return 1; | 
| jbe@123 | 226 } | 
| jbe@123 | 227 | 
| jbe@123 | 228 static int json_isnull(lua_State *L) { | 
| jbe@123 | 229   lua_pushvalue(L, 1); | 
| jbe@123 | 230   lua_rawget(L, JSON_UPVAL_NULLS); | 
| jbe@123 | 231   if (lua_isnil(L, -1)) goto json_isnull_false; | 
| jbe@123 | 232   lua_pushvalue(L, 2); | 
| jbe@123 | 233   lua_rawget(L, -2); | 
| jbe@123 | 234   if (!lua_isnil(L, -1)) return 1; | 
| jbe@123 | 235 json_isnull_false: | 
| jbe@123 | 236   lua_pushboolean(L, 0); | 
| jbe@121 | 237   return 1; | 
| jbe@121 | 238 } | 
| jbe@121 | 239 | 
| jbe@121 | 240 static const struct luaL_Reg json_module_functions[] = { | 
| jbe@121 | 241   {"import", json_import}, | 
| jbe@121 | 242   {"arylen", json_arylen}, | 
| jbe@123 | 243   {"isnull", json_isnull}, | 
| jbe@121 | 244   {NULL, NULL} | 
| jbe@121 | 245 }; | 
| jbe@121 | 246 | 
| jbe@121 | 247 int luaopen_json(lua_State *L) { | 
| jbe@121 | 248   lua_newtable(L);  // library table | 
| jbe@125 | 249   lua_newtable(L);  // metatable for JSON objects and JSON arrays | 
| jbe@125 | 250   lua_pushvalue(L, -1); | 
| jbe@125 | 251   lua_setfield(L, -3, "metatable"); | 
| jbe@125 | 252   lua_newtable(L);  // ephemeron table to store a set of keys associated with JSON-null values | 
| jbe@121 | 253   lua_newtable(L);  // ephemeron table to store the length of arrays (that may contain nil's) | 
| jbe@121 | 254   lua_newtable(L);  // meta table for ephemeron table | 
| jbe@121 | 255   lua_pushliteral(L, "__mode"); | 
| jbe@121 | 256   lua_pushliteral(L, "k"); | 
| jbe@121 | 257   lua_rawset(L, -3); | 
| jbe@123 | 258   lua_pushvalue(L, -1); | 
| jbe@123 | 259   lua_setmetatable(L, -3); | 
| jbe@121 | 260   lua_setmetatable(L, -2); | 
| jbe@125 | 261   luaL_setfuncs(L, json_module_functions, 3); | 
| jbe@121 | 262   return 1; | 
| jbe@121 | 263 } |