| rev | line source | 
| jbe@121 | 1 #include <lua.h> | 
| jbe@121 | 2 #include <lauxlib.h> | 
| jbe@121 | 3 #include <string.h> | 
| jbe@121 | 4 | 
| jbe@121 | 5 #define JSON_VALUE 0 | 
| jbe@121 | 6 #define JSON_OBJECT_KEY 1 | 
| jbe@121 | 7 #define JSON_OBJECT_KEY_TERMINATOR 2 | 
| jbe@121 | 8 #define JSON_OBJECT_VALUE 3 | 
| jbe@121 | 9 #define JSON_OBJECT_SEPARATOR 4 | 
| jbe@121 | 10 #define JSON_ARRAY_VALUE 5 | 
| jbe@121 | 11 #define JSON_ARRAY_SEPARATOR 6 | 
| jbe@121 | 12 #define JSON_END 7 | 
| jbe@121 | 13 | 
| jbe@121 | 14 static int json_import(lua_State *L) { | 
| jbe@121 | 15   const char *str; | 
| jbe@121 | 16   size_t total; | 
| jbe@121 | 17   size_t pos = 0; | 
| jbe@121 | 18   size_t level = 0; | 
| jbe@121 | 19   int mode = JSON_VALUE; | 
| jbe@121 | 20   char c; | 
| jbe@121 | 21   luaL_Buffer luabuf; | 
| jbe@121 | 22   char *cbuf; | 
| jbe@121 | 23   size_t writepos; | 
| jbe@121 | 24   int aryidx; | 
| jbe@121 | 25   lua_settop(L, 1); | 
| jbe@121 | 26   str = lua_tostring(L, 1); | 
| jbe@121 | 27   total = strlen(str); | 
| jbe@121 | 28 json_import_loop: | 
| jbe@121 | 29   while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++; | 
| jbe@121 | 30   switch (c) { | 
| jbe@121 | 31   case 0: | 
| jbe@121 | 32     if (mode == JSON_END) return 1; | 
| jbe@121 | 33     json_import_unexpected_eof: | 
| jbe@121 | 34     lua_pushnil(L); | 
| jbe@121 | 35     if (level == 0) lua_pushliteral(L, "Empty string"); | 
| jbe@121 | 36     else lua_pushliteral(L, "Unexpected end of JSON document"); | 
| jbe@121 | 37     return 2; | 
| jbe@121 | 38   case '{': | 
| jbe@121 | 39     if (mode != JSON_VALUE && mode != JSON_OBJECT_VALUE && mode != JSON_ARRAY_VALUE) | 
| jbe@121 | 40       goto json_import_syntax_error; | 
| jbe@121 | 41     pos++; | 
| jbe@121 | 42     lua_newtable(L); | 
| jbe@121 | 43     mode = JSON_OBJECT_KEY; | 
| jbe@121 | 44     level++; | 
| jbe@121 | 45     goto json_import_loop; | 
| jbe@121 | 46   case '[': | 
| jbe@121 | 47     if (mode != JSON_VALUE && mode != JSON_OBJECT_VALUE && mode != JSON_ARRAY_VALUE) | 
| jbe@121 | 48       goto json_import_syntax_error; | 
| jbe@121 | 49     pos++; | 
| jbe@121 | 50     lua_newtable(L); | 
| jbe@121 | 51     lua_pushinteger(L, 0);  // length of array (since it may contain nil's) | 
| jbe@121 | 52     mode = JSON_ARRAY_VALUE; | 
| jbe@121 | 53     level++; | 
| jbe@121 | 54     goto json_import_loop; | 
| jbe@121 | 55   case '}': | 
| jbe@121 | 56     if (mode != JSON_OBJECT_KEY && mode != JSON_OBJECT_SEPARATOR) | 
| jbe@121 | 57       goto json_import_syntax_error; | 
| jbe@121 | 58     goto json_import_close; | 
| jbe@121 | 59   case ']': | 
| jbe@121 | 60     if (mode != JSON_ARRAY_VALUE && mode != JSON_ARRAY_SEPARATOR) | 
| jbe@121 | 61       goto json_import_syntax_error; | 
| jbe@121 | 62     lua_pushvalue(L, -2);  // use array table as key | 
| jbe@121 | 63     lua_insert(L, -2);  // use length of array as value | 
| jbe@121 | 64     lua_rawset(L, lua_upvalueindex(1));  // store length in ephemeron table | 
| jbe@121 | 65     // leaves array table on top of stack | 
| jbe@121 | 66   json_import_close: | 
| jbe@121 | 67     pos++; | 
| jbe@121 | 68     if (--level) { | 
| jbe@121 | 69       if (lua_type(L, -2) == LUA_TNUMBER) { | 
| jbe@121 | 70         mode = JSON_ARRAY_VALUE; | 
| jbe@121 | 71       } else { | 
| jbe@121 | 72         mode = JSON_OBJECT_VALUE; | 
| jbe@121 | 73       } | 
| jbe@121 | 74       goto json_import_process_value; | 
| jbe@121 | 75     } else { | 
| jbe@121 | 76       mode = JSON_END; | 
| jbe@121 | 77     } | 
| jbe@121 | 78     goto json_import_loop; | 
| jbe@121 | 79   case ':': | 
| jbe@121 | 80     if (mode != JSON_OBJECT_KEY_TERMINATOR) | 
| jbe@121 | 81       goto json_import_syntax_error; | 
| jbe@121 | 82     pos++; | 
| jbe@121 | 83     mode = JSON_OBJECT_VALUE; | 
| jbe@121 | 84     goto json_import_loop; | 
| jbe@121 | 85   case ',': | 
| jbe@121 | 86     if (mode == JSON_OBJECT_SEPARATOR) { | 
| jbe@121 | 87       mode = JSON_OBJECT_KEY; | 
| jbe@121 | 88     } else if (mode == JSON_ARRAY_SEPARATOR) { | 
| jbe@121 | 89       mode = JSON_ARRAY_VALUE; | 
| jbe@121 | 90     } else { | 
| jbe@121 | 91       goto json_import_syntax_error; | 
| jbe@121 | 92     } | 
| jbe@121 | 93     pos++; | 
| jbe@121 | 94     goto json_import_loop; | 
| jbe@121 | 95   case '"': | 
| jbe@121 | 96     cbuf = luaL_buffinitsize(L, &luabuf, total-pos); | 
| jbe@121 | 97     writepos = 0; | 
| jbe@121 | 98     pos++; | 
| jbe@121 | 99     while ((c = str[pos++]) != '"') { | 
| jbe@121 | 100       if (c == 0) { | 
| jbe@121 | 101         goto json_import_unexpected_eof; | 
| jbe@121 | 102       } else if (c < 32 || c == 127) { | 
| jbe@121 | 103         lua_pushnil(L); | 
| jbe@121 | 104         lua_pushliteral(L, "Unexpected control character in JSON string"); | 
| jbe@121 | 105         return 2; | 
| jbe@121 | 106       } else if (c == '\\') { | 
| jbe@121 | 107         c = str[pos++]; | 
| jbe@121 | 108         switch (c) { | 
| jbe@121 | 109         case 0: | 
| jbe@121 | 110           goto json_import_unexpected_eof; | 
| jbe@121 | 111         case '"': | 
| jbe@121 | 112         case '/': | 
| jbe@121 | 113         case '\\': | 
| jbe@121 | 114           cbuf[writepos++] = c; | 
| jbe@121 | 115           break; | 
| jbe@121 | 116         case 'b': | 
| jbe@121 | 117           cbuf[writepos++] = '\b'; | 
| jbe@121 | 118           break; | 
| jbe@121 | 119         case 'f': | 
| jbe@121 | 120           cbuf[writepos++] = '\f'; | 
| jbe@121 | 121           break; | 
| jbe@121 | 122         case 'n': | 
| jbe@121 | 123           cbuf[writepos++] = '\n'; | 
| jbe@121 | 124           break; | 
| jbe@121 | 125         case 'r': | 
| jbe@121 | 126           cbuf[writepos++] = '\r'; | 
| jbe@121 | 127           break; | 
| jbe@121 | 128         case 't': | 
| jbe@121 | 129           cbuf[writepos++] = '\t'; | 
| jbe@121 | 130           break; | 
| jbe@121 | 131         case 'u': | 
| jbe@121 | 132           lua_pushnil(L); | 
| jbe@121 | 133           lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet");  // TODO | 
| jbe@121 | 134           return 2; | 
| jbe@121 | 135         default: | 
| jbe@121 | 136           lua_pushnil(L); | 
| jbe@121 | 137           lua_pushliteral(L, "Unexpected string escape sequence in JSON document"); | 
| jbe@121 | 138           return 2; | 
| jbe@121 | 139         } | 
| jbe@121 | 140       } else { | 
| jbe@121 | 141         cbuf[writepos++] = c; | 
| jbe@121 | 142       } | 
| jbe@121 | 143     } | 
| jbe@121 | 144     if (!c) goto json_import_unexpected_eof; | 
| jbe@121 | 145     luaL_pushresultsize(&luabuf, writepos); | 
| jbe@121 | 146     goto json_import_process_value; | 
| jbe@121 | 147   } | 
| jbe@121 | 148   if (!strncmp(str+pos, "true", 4)) { | 
| jbe@121 | 149     lua_pushboolean(L, 1); | 
| jbe@121 | 150     pos += 4; | 
| jbe@121 | 151   } else if (!strncmp(str+pos, "false", 5)) { | 
| jbe@121 | 152     lua_pushboolean(L, 0); | 
| jbe@121 | 153     pos += 5; | 
| jbe@121 | 154   } else if (!strncmp(str+pos, "null", 4)) { | 
| jbe@121 | 155     lua_pushnil(L); | 
| jbe@121 | 156     pos += 4; | 
| jbe@121 | 157   } else { | 
| jbe@121 | 158     goto json_import_syntax_error; | 
| jbe@121 | 159   } | 
| jbe@121 | 160 json_import_process_value: | 
| jbe@121 | 161   switch (mode) { | 
| jbe@121 | 162   case JSON_OBJECT_KEY: | 
| jbe@121 | 163     if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error; | 
| jbe@121 | 164     mode = JSON_OBJECT_KEY_TERMINATOR; | 
| jbe@121 | 165     goto json_import_loop; | 
| jbe@121 | 166   case JSON_OBJECT_VALUE: | 
| jbe@121 | 167     lua_rawset(L, -3); | 
| jbe@121 | 168     mode = JSON_OBJECT_SEPARATOR; | 
| jbe@121 | 169     goto json_import_loop; | 
| jbe@121 | 170   case JSON_ARRAY_VALUE: | 
| jbe@121 | 171     aryidx = lua_tointeger(L, -2) + 1; | 
| jbe@121 | 172     lua_rawseti(L, -3, aryidx); | 
| jbe@121 | 173     lua_pop(L, 1); | 
| jbe@121 | 174     lua_pushinteger(L, aryidx); | 
| jbe@121 | 175     mode = JSON_ARRAY_SEPARATOR; | 
| jbe@121 | 176     goto json_import_loop; | 
| jbe@121 | 177   case JSON_VALUE: | 
| jbe@121 | 178     mode = JSON_END; | 
| jbe@121 | 179     goto json_import_loop; | 
| jbe@121 | 180   } | 
| jbe@121 | 181 json_import_syntax_error: | 
| jbe@121 | 182   lua_pushnil(L); | 
| jbe@121 | 183   lua_pushliteral(L, "Syntax error in JSON document"); | 
| jbe@121 | 184   return 2; | 
| jbe@121 | 185 } | 
| jbe@121 | 186 | 
| jbe@121 | 187 static int json_arylen(lua_State *L) { | 
| jbe@121 | 188   lua_settop(L, 1); | 
| jbe@121 | 189   lua_rawget(L, lua_upvalueindex(1)); | 
| jbe@121 | 190   return 1; | 
| jbe@121 | 191 } | 
| jbe@121 | 192 | 
| jbe@121 | 193 static const struct luaL_Reg json_module_functions[] = { | 
| jbe@121 | 194   {"import", json_import}, | 
| jbe@121 | 195   {"arylen", json_arylen}, | 
| jbe@121 | 196   {NULL, NULL} | 
| jbe@121 | 197 }; | 
| jbe@121 | 198 | 
| jbe@121 | 199 int luaopen_json(lua_State *L) { | 
| jbe@121 | 200   lua_newtable(L);  // library table | 
| jbe@121 | 201   lua_newtable(L);  // ephemeron table to store the length of arrays (that may contain nil's) | 
| jbe@121 | 202   lua_newtable(L);  // meta table for ephemeron table | 
| jbe@121 | 203   lua_pushliteral(L, "__mode"); | 
| jbe@121 | 204   lua_pushliteral(L, "k"); | 
| jbe@121 | 205   lua_rawset(L, -3); | 
| jbe@121 | 206   lua_setmetatable(L, -2); | 
| jbe@121 | 207   luaL_setfuncs(L, json_module_functions, 1); | 
| jbe@121 | 208   return 1; | 
| jbe@121 | 209 } |