# HG changeset patch # User jbe # Date 1406319953 -7200 # Node ID 9ad1165cf3a14bb8efa994af59548ebd493267de # Parent b07438523a9d50b14baafecb0a95d4977556378f Started work on a JSON library diff -r b07438523a9d -r 9ad1165cf3a1 libraries/json/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/json/Makefile Fri Jul 25 22:25:53 2014 +0200 @@ -0,0 +1,10 @@ +include ../../Makefile.options + +json.so: json.o + $(LD) $(LDFLAGS) -o json.$(SLIB_EXT) json.o + +json.o: json.c + $(CC) -c $(CFLAGS) -o json.o json.c + +clean:: + rm -f json.so json.o diff -r b07438523a9d -r 9ad1165cf3a1 libraries/json/json.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/json/json.c Fri Jul 25 22:25:53 2014 +0200 @@ -0,0 +1,209 @@ +#include +#include +#include + +#define JSON_VALUE 0 +#define JSON_OBJECT_KEY 1 +#define JSON_OBJECT_KEY_TERMINATOR 2 +#define JSON_OBJECT_VALUE 3 +#define JSON_OBJECT_SEPARATOR 4 +#define JSON_ARRAY_VALUE 5 +#define JSON_ARRAY_SEPARATOR 6 +#define JSON_END 7 + +static int json_import(lua_State *L) { + const char *str; + size_t total; + size_t pos = 0; + size_t level = 0; + int mode = JSON_VALUE; + char c; + luaL_Buffer luabuf; + char *cbuf; + size_t writepos; + int aryidx; + lua_settop(L, 1); + str = lua_tostring(L, 1); + total = strlen(str); +json_import_loop: + while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++; + switch (c) { + case 0: + if (mode == JSON_END) return 1; + json_import_unexpected_eof: + lua_pushnil(L); + if (level == 0) lua_pushliteral(L, "Empty string"); + else lua_pushliteral(L, "Unexpected end of JSON document"); + return 2; + case '{': + if (mode != JSON_VALUE && mode != JSON_OBJECT_VALUE && mode != JSON_ARRAY_VALUE) + goto json_import_syntax_error; + pos++; + lua_newtable(L); + mode = JSON_OBJECT_KEY; + level++; + goto json_import_loop; + case '[': + if (mode != JSON_VALUE && mode != JSON_OBJECT_VALUE && mode != JSON_ARRAY_VALUE) + goto json_import_syntax_error; + pos++; + lua_newtable(L); + lua_pushinteger(L, 0); // length of array (since it may contain nil's) + mode = JSON_ARRAY_VALUE; + level++; + goto json_import_loop; + case '}': + if (mode != JSON_OBJECT_KEY && mode != JSON_OBJECT_SEPARATOR) + goto json_import_syntax_error; + goto json_import_close; + case ']': + if (mode != JSON_ARRAY_VALUE && mode != JSON_ARRAY_SEPARATOR) + goto json_import_syntax_error; + lua_pushvalue(L, -2); // use array table as key + lua_insert(L, -2); // use length of array as value + lua_rawset(L, lua_upvalueindex(1)); // store length in ephemeron table + // leaves array table on top of stack + json_import_close: + pos++; + if (--level) { + if (lua_type(L, -2) == LUA_TNUMBER) { + mode = JSON_ARRAY_VALUE; + } else { + mode = JSON_OBJECT_VALUE; + } + goto json_import_process_value; + } else { + mode = JSON_END; + } + goto json_import_loop; + case ':': + if (mode != JSON_OBJECT_KEY_TERMINATOR) + goto json_import_syntax_error; + pos++; + mode = JSON_OBJECT_VALUE; + goto json_import_loop; + case ',': + if (mode == JSON_OBJECT_SEPARATOR) { + mode = JSON_OBJECT_KEY; + } else if (mode == JSON_ARRAY_SEPARATOR) { + mode = JSON_ARRAY_VALUE; + } else { + goto json_import_syntax_error; + } + pos++; + goto json_import_loop; + case '"': + cbuf = luaL_buffinitsize(L, &luabuf, total-pos); + writepos = 0; + pos++; + while ((c = str[pos++]) != '"') { + if (c == 0) { + goto json_import_unexpected_eof; + } else if (c < 32 || c == 127) { + lua_pushnil(L); + lua_pushliteral(L, "Unexpected control character in JSON string"); + return 2; + } else if (c == '\\') { + c = str[pos++]; + switch (c) { + case 0: + goto json_import_unexpected_eof; + case '"': + case '/': + case '\\': + cbuf[writepos++] = c; + break; + case 'b': + cbuf[writepos++] = '\b'; + break; + case 'f': + cbuf[writepos++] = '\f'; + break; + case 'n': + cbuf[writepos++] = '\n'; + break; + case 'r': + cbuf[writepos++] = '\r'; + break; + case 't': + cbuf[writepos++] = '\t'; + break; + case 'u': + lua_pushnil(L); + lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet"); // TODO + return 2; + default: + lua_pushnil(L); + lua_pushliteral(L, "Unexpected string escape sequence in JSON document"); + return 2; + } + } else { + cbuf[writepos++] = c; + } + } + if (!c) goto json_import_unexpected_eof; + luaL_pushresultsize(&luabuf, writepos); + goto json_import_process_value; + } + if (!strncmp(str+pos, "true", 4)) { + lua_pushboolean(L, 1); + pos += 4; + } else if (!strncmp(str+pos, "false", 5)) { + lua_pushboolean(L, 0); + pos += 5; + } else if (!strncmp(str+pos, "null", 4)) { + lua_pushnil(L); + pos += 4; + } else { + goto json_import_syntax_error; + } +json_import_process_value: + switch (mode) { + case JSON_OBJECT_KEY: + if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error; + mode = JSON_OBJECT_KEY_TERMINATOR; + goto json_import_loop; + case JSON_OBJECT_VALUE: + lua_rawset(L, -3); + mode = JSON_OBJECT_SEPARATOR; + goto json_import_loop; + case JSON_ARRAY_VALUE: + aryidx = lua_tointeger(L, -2) + 1; + lua_rawseti(L, -3, aryidx); + lua_pop(L, 1); + lua_pushinteger(L, aryidx); + mode = JSON_ARRAY_SEPARATOR; + goto json_import_loop; + case JSON_VALUE: + mode = JSON_END; + goto json_import_loop; + } +json_import_syntax_error: + lua_pushnil(L); + lua_pushliteral(L, "Syntax error in JSON document"); + return 2; +} + +static int json_arylen(lua_State *L) { + lua_settop(L, 1); + lua_rawget(L, lua_upvalueindex(1)); + return 1; +} + +static const struct luaL_Reg json_module_functions[] = { + {"import", json_import}, + {"arylen", json_arylen}, + {NULL, NULL} +}; + +int luaopen_json(lua_State *L) { + lua_newtable(L); // library table + lua_newtable(L); // ephemeron table to store the length of arrays (that may contain nil's) + lua_newtable(L); // meta table for ephemeron table + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "k"); + lua_rawset(L, -3); + lua_setmetatable(L, -2); + luaL_setfuncs(L, json_module_functions, 1); + return 1; +}