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