jbe@121: #include jbe@121: #include jbe@121: #include jbe@121: jbe@121: #define JSON_VALUE 0 jbe@121: #define JSON_OBJECT_KEY 1 jbe@121: #define JSON_OBJECT_KEY_TERMINATOR 2 jbe@121: #define JSON_OBJECT_VALUE 3 jbe@121: #define JSON_OBJECT_SEPARATOR 4 jbe@121: #define JSON_ARRAY_VALUE 5 jbe@121: #define JSON_ARRAY_SEPARATOR 6 jbe@121: #define JSON_END 7 jbe@121: jbe@121: static int json_import(lua_State *L) { jbe@121: const char *str; jbe@121: size_t total; jbe@121: size_t pos = 0; jbe@121: size_t level = 0; jbe@121: int mode = JSON_VALUE; jbe@121: char c; jbe@121: luaL_Buffer luabuf; jbe@121: char *cbuf; jbe@121: size_t writepos; jbe@121: int aryidx; jbe@121: lua_settop(L, 1); jbe@121: str = lua_tostring(L, 1); jbe@121: total = strlen(str); jbe@121: json_import_loop: jbe@121: while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++; jbe@121: switch (c) { jbe@121: case 0: jbe@121: if (mode == JSON_END) return 1; jbe@121: json_import_unexpected_eof: jbe@121: lua_pushnil(L); jbe@121: if (level == 0) lua_pushliteral(L, "Empty string"); jbe@121: else lua_pushliteral(L, "Unexpected end of JSON document"); jbe@121: return 2; jbe@121: case '{': jbe@121: if (mode != JSON_VALUE && mode != JSON_OBJECT_VALUE && mode != JSON_ARRAY_VALUE) jbe@121: goto json_import_syntax_error; jbe@121: pos++; jbe@121: lua_newtable(L); jbe@121: mode = JSON_OBJECT_KEY; jbe@121: level++; jbe@121: goto json_import_loop; jbe@121: case '[': jbe@121: if (mode != JSON_VALUE && mode != JSON_OBJECT_VALUE && mode != JSON_ARRAY_VALUE) jbe@121: goto json_import_syntax_error; jbe@121: pos++; jbe@121: lua_newtable(L); jbe@121: lua_pushinteger(L, 0); // length of array (since it may contain nil's) jbe@121: mode = JSON_ARRAY_VALUE; jbe@121: level++; jbe@121: goto json_import_loop; jbe@121: case '}': jbe@121: if (mode != JSON_OBJECT_KEY && mode != JSON_OBJECT_SEPARATOR) jbe@121: goto json_import_syntax_error; jbe@121: goto json_import_close; jbe@121: case ']': jbe@121: if (mode != JSON_ARRAY_VALUE && mode != JSON_ARRAY_SEPARATOR) jbe@121: goto json_import_syntax_error; jbe@121: lua_pushvalue(L, -2); // use array table as key jbe@121: lua_insert(L, -2); // use length of array as value jbe@121: lua_rawset(L, lua_upvalueindex(1)); // store length in ephemeron table jbe@121: // leaves array table on top of stack jbe@121: json_import_close: jbe@121: pos++; jbe@121: if (--level) { jbe@121: if (lua_type(L, -2) == LUA_TNUMBER) { jbe@121: mode = JSON_ARRAY_VALUE; jbe@121: } else { jbe@121: mode = JSON_OBJECT_VALUE; jbe@121: } jbe@121: goto json_import_process_value; jbe@121: } else { jbe@121: mode = JSON_END; jbe@121: } jbe@121: goto json_import_loop; jbe@121: case ':': jbe@121: if (mode != JSON_OBJECT_KEY_TERMINATOR) jbe@121: goto json_import_syntax_error; jbe@121: pos++; jbe@121: mode = JSON_OBJECT_VALUE; jbe@121: goto json_import_loop; jbe@121: case ',': jbe@121: if (mode == JSON_OBJECT_SEPARATOR) { jbe@121: mode = JSON_OBJECT_KEY; jbe@121: } else if (mode == JSON_ARRAY_SEPARATOR) { jbe@121: mode = JSON_ARRAY_VALUE; jbe@121: } else { jbe@121: goto json_import_syntax_error; jbe@121: } jbe@121: pos++; jbe@121: goto json_import_loop; jbe@121: case '"': jbe@121: cbuf = luaL_buffinitsize(L, &luabuf, total-pos); jbe@121: writepos = 0; jbe@121: pos++; jbe@121: while ((c = str[pos++]) != '"') { jbe@121: if (c == 0) { jbe@121: goto json_import_unexpected_eof; jbe@121: } else if (c < 32 || c == 127) { jbe@121: lua_pushnil(L); jbe@121: lua_pushliteral(L, "Unexpected control character in JSON string"); jbe@121: return 2; jbe@121: } else if (c == '\\') { jbe@121: c = str[pos++]; jbe@121: switch (c) { jbe@121: case 0: jbe@121: goto json_import_unexpected_eof; jbe@121: case '"': jbe@121: case '/': jbe@121: case '\\': jbe@121: cbuf[writepos++] = c; jbe@121: break; jbe@121: case 'b': jbe@121: cbuf[writepos++] = '\b'; jbe@121: break; jbe@121: case 'f': jbe@121: cbuf[writepos++] = '\f'; jbe@121: break; jbe@121: case 'n': jbe@121: cbuf[writepos++] = '\n'; jbe@121: break; jbe@121: case 'r': jbe@121: cbuf[writepos++] = '\r'; jbe@121: break; jbe@121: case 't': jbe@121: cbuf[writepos++] = '\t'; jbe@121: break; jbe@121: case 'u': jbe@121: lua_pushnil(L); jbe@121: lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet"); // TODO jbe@121: return 2; jbe@121: default: jbe@121: lua_pushnil(L); jbe@121: lua_pushliteral(L, "Unexpected string escape sequence in JSON document"); jbe@121: return 2; jbe@121: } jbe@121: } else { jbe@121: cbuf[writepos++] = c; jbe@121: } jbe@121: } jbe@121: if (!c) goto json_import_unexpected_eof; jbe@121: luaL_pushresultsize(&luabuf, writepos); jbe@121: goto json_import_process_value; jbe@121: } jbe@121: if (!strncmp(str+pos, "true", 4)) { jbe@121: lua_pushboolean(L, 1); jbe@121: pos += 4; jbe@121: } else if (!strncmp(str+pos, "false", 5)) { jbe@121: lua_pushboolean(L, 0); jbe@121: pos += 5; jbe@121: } else if (!strncmp(str+pos, "null", 4)) { jbe@121: lua_pushnil(L); jbe@121: pos += 4; jbe@121: } else { jbe@121: goto json_import_syntax_error; jbe@121: } jbe@121: json_import_process_value: jbe@121: switch (mode) { jbe@121: case JSON_OBJECT_KEY: jbe@121: if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error; jbe@121: mode = JSON_OBJECT_KEY_TERMINATOR; jbe@121: goto json_import_loop; jbe@121: case JSON_OBJECT_VALUE: jbe@121: lua_rawset(L, -3); jbe@121: mode = JSON_OBJECT_SEPARATOR; jbe@121: goto json_import_loop; jbe@121: case JSON_ARRAY_VALUE: jbe@121: aryidx = lua_tointeger(L, -2) + 1; jbe@121: lua_rawseti(L, -3, aryidx); jbe@121: lua_pop(L, 1); jbe@121: lua_pushinteger(L, aryidx); jbe@121: mode = JSON_ARRAY_SEPARATOR; jbe@121: goto json_import_loop; jbe@121: case JSON_VALUE: jbe@121: mode = JSON_END; jbe@121: goto json_import_loop; jbe@121: } jbe@121: json_import_syntax_error: jbe@121: lua_pushnil(L); jbe@121: lua_pushliteral(L, "Syntax error in JSON document"); jbe@121: return 2; jbe@121: } jbe@121: jbe@121: static int json_arylen(lua_State *L) { jbe@121: lua_settop(L, 1); jbe@121: lua_rawget(L, lua_upvalueindex(1)); jbe@121: return 1; jbe@121: } jbe@121: jbe@121: static const struct luaL_Reg json_module_functions[] = { jbe@121: {"import", json_import}, jbe@121: {"arylen", json_arylen}, jbe@121: {NULL, NULL} jbe@121: }; jbe@121: jbe@121: int luaopen_json(lua_State *L) { jbe@121: lua_newtable(L); // library table jbe@121: lua_newtable(L); // ephemeron table to store the length of arrays (that may contain nil's) jbe@121: lua_newtable(L); // meta table for ephemeron table jbe@121: lua_pushliteral(L, "__mode"); jbe@121: lua_pushliteral(L, "k"); jbe@121: lua_rawset(L, -3); jbe@121: lua_setmetatable(L, -2); jbe@121: luaL_setfuncs(L, json_module_functions, 1); jbe@121: return 1; jbe@121: }