From 516601cb8e909d4c32ef50b10d34e4dd0b467b46 Mon Sep 17 00:00:00 2001 From: MikalaiR Date: Tue, 2 Feb 2016 17:19:46 +0000 Subject: MirLua: libjson-based json parser git-svn-id: http://svn.miranda-ng.org/main/trunk@16215 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/MirLua/Modules/JSON/src/json.c | 590 -------------------------- plugins/MirLua/Modules/JSON/src/json.cpp | 28 ++ plugins/MirLua/Modules/JSON/src/metatable.cpp | 160 +++++++ plugins/MirLua/Modules/JSON/src/stdafx.h | 68 +-- 4 files changed, 203 insertions(+), 643 deletions(-) delete mode 100644 plugins/MirLua/Modules/JSON/src/json.c create mode 100644 plugins/MirLua/Modules/JSON/src/json.cpp create mode 100644 plugins/MirLua/Modules/JSON/src/metatable.cpp (limited to 'plugins/MirLua') diff --git a/plugins/MirLua/Modules/JSON/src/json.c b/plugins/MirLua/Modules/JSON/src/json.c deleted file mode 100644 index dd7c864f70..0000000000 --- a/plugins/MirLua/Modules/JSON/src/json.c +++ /dev/null @@ -1,590 +0,0 @@ -#include "stdafx.h" - -int vasprintf(char **strp, const char *fmt, va_list ap) -{ - int r = -1, size = _vscprintf(fmt, ap); - - if ((size >= 0) && (size < INT_MAX)) - { - *strp = (char *)malloc(size + 1); //+1 for null - if (*strp) - { - r = vsnprintf(*strp, size + 1, fmt, ap); //+1 for null - if ((r < 0) || (r > size)) - { - free(*strp); - *strp = 0; - r = -1; - } - } - } - else { *strp = 0; } - - return(r); -} - -static int -json_error(lua_State *L, const char *fmt, ...) -{ - va_list ap; - int len; - char *msg; - - va_start(ap, fmt); - len = vasprintf(&msg, fmt, ap); - va_end(ap); - - lua_pushnil(L); - if (len != -1) { - lua_pushstring(L, msg); - free(msg); - } - else - lua_pushstring(L, "internal error: vasprintf failed"); - longjmp(env, 0); -} - -static unsigned int -digit2int(lua_State *L, const unsigned char digit) -{ - unsigned int val = 0; - - if (digit >= '0' && digit <= '9') - val = digit - '0'; - else if (digit >= 'a' || digit <= 'f') - val = digit - 'a' + 10; - else if (digit >= 'A' || digit <= 'F') - val = digit - 'A' + 10; - else - json_error(L, "Invalid hex digit"); - return val; -} - -static unsigned int -fourhex2int(lua_State *L, const unsigned char *code) -{ - unsigned int utf = 0; - - utf += digit2int(L, code[0]) * 4096; - utf += digit2int(L, code[1]) * 256; - utf += digit2int(L, code[2]) * 16; - utf += digit2int(L, code[3]); - return utf; -} - -static const char * -code2utf8(lua_State *L, const unsigned char *code, char buf[4]) -{ - unsigned int utf = 0; - - utf = fourhex2int(L, code); - if (utf < 128) { - buf[0] = utf & 0x7F; - buf[1] = buf[2] = buf[3] = 0; - } - else if (utf < 2048) { - buf[0] = ((utf >> 6) & 0x1F) | 0xC0; - buf[1] = (utf & 0x3F) | 0x80; - buf[2] = buf[3] = 0; - } - else { - buf[0] = ((utf >> 12) & 0x0F) | 0xE0; - buf[1] = ((utf >> 6) & 0x3F) | 0x80; - buf[2] = (utf & 0x3F) | 0x80; - buf[3] = 0; - } - return buf; -} - -static void -skip_ws(char **s) -{ - while (isspace(**s)) - (*s)++; -} - -static void -decode_array(lua_State *L, char **s, int null) -{ - int i = 1; - - (*s)++; - lua_newtable(L); - for (i = 1; 1; i++) { - skip_ws(s); - lua_pushinteger(L, i); - decode_value(L, s, null); - lua_settable(L, -3); - skip_ws(s); - if (**s == ',') { - (*s)++; - skip_ws(s); - } - else - break; - } - skip_ws(s); - if (**s == ']') - (*s)++; - else - json_error(L, "array does not end with ']'"); -} - -static void -decode_object(lua_State *L, char **s, int null) -{ - (*s)++; - lua_newtable(L); - skip_ws(s); - - if (**s != '}') { - while (1) { - skip_ws(s); - decode_string(L, s); - skip_ws(s); - if (**s != ':') - json_error(L, "object lacks separator ':'"); - (*s)++; - skip_ws(s); - decode_value(L, s, null); - lua_settable(L, -3); - skip_ws(s); - if (**s == ',') { - (*s)++; - skip_ws(s); - } - else - break; - } - skip_ws(s); - } - if (**s == '}') - (*s)++; - else - json_error(L, "objects does not end with '}'"); -} - -static void -decode_string(lua_State *L, char **s) -{ - size_t len; - char *newstr = NULL, *newc, *beginning, *end, *nextEscape = NULL; - char utfbuf[4] = ""; - - (*s)++; - beginning = *s; - for (end = NULL; **s != '\0' && end == NULL; (*s)++) { - if (**s == '"' && (*((*s) - 1) != '\\')) - end = *s; - } - *s = beginning; - len = strlen(*s); - newstr = (char*)malloc(len + 1); - memset(newstr, 0, len + 1); - newc = newstr; - while (*s != end) { - nextEscape = strchr(*s, '\\'); - if (nextEscape > end) - nextEscape = NULL; - if (nextEscape == *s) { - switch (*((*s) + 1)) { - case '"': - *newc = '"'; - newc++; - (*s) += 2; - break; - case '\\': - *newc = '\\'; - newc++; - (*s) += 2; - break; - case '/': - *newc = '/'; - newc++; - (*s) += 2; - break; - case 'b': - *newc = '\b'; - newc++; - (*s) += 2; - break; - case 'f': - *newc = '\f'; - newc++; - (*s) += 2; - break; - case 'n': - *newc = '\n'; - newc++; - (*s) += 2; - break; - case 'r': - *newc = '\r'; - newc++; - (*s) += 2; - break; - case 't': - *newc = '\t'; - newc++; - (*s) += 2; - break; - case 'u': - code2utf8(L, (unsigned char *)(*s) + 2, utfbuf); - strcpy(newc, utfbuf); - newc += strlen(utfbuf); - (*s) += 6; - break; - default: - json_error(L, "invalid escape character"); - break; - } - } - else if (nextEscape != NULL) { - size_t len = nextEscape - *s; - strncpy(newc, *s, len); - newc += len; - (*s) += len; - } - else { - size_t len = end - *s; - strncpy(newc, *s, len); - newc += len; - (*s) += len; - } - } - *newc = 0; - lua_pushstring(L, newstr); - (*s)++; - free(newstr); -} - -static void -decode_value(lua_State *L, char **s, int null) -{ - skip_ws(s); - - if (!strncmp(*s, "false", 5)) { - lua_pushboolean(L, 0); - *s += 5; - } - else if (!strncmp(*s, "true", 4)) { - lua_pushboolean(L, 1); - *s += 4; - } - else if (!strncmp(*s, "null", 4)) { - switch (null) { - case 0: - lua_pushstring(L, ""); - break; - case 1: - lua_newtable(L); - luaL_getmetatable(L, JSON_NULL_METATABLE); - lua_setmetatable(L, -2); - break; - case 2: - lua_pushnil(L); - break; - } - *s += 4; - } - else if (isdigit(**s) || **s == '+' || **s == '-') { - lua_pushnumber(L, atof(*s)); - /* advance pointer past the number */ - while (isdigit(**s) || **s == '+' || **s == '-' - || **s == 'e' || **s == 'E' || **s == '.') - (*s)++; - } - else { - switch (**s) { - case '[': - decode_array(L, s, null); - break; - case '{': - decode_object(L, s, null); - break; - case '"': - decode_string(L, s); - break; - case ']': /* ignore end of empty array */ - lua_pushnil(L); - break; - default: - json_error(L, "syntax error"); - break; - } - } -} - -static int -json_decode(lua_State *L) -{ - char *s; - int null; - const char *const options[] = { - "empty-string", - "json-null", - "nil", - NULL - }; - - s = (char *)luaL_checkstring(L, 1); - - null = luaL_checkoption(L, 2, "json-null", options); - - if (!setjmp(env)) { - decode_value(L, &s, null); - return 1; - } - else - return 2; -} - -/* encode JSON */ - -/* encode_string assumes an UTF-8 string */ -static void -encode_string(lua_State *L, luaL_Buffer *b, unsigned char *s) -{ - char hexbuf[6]; - - luaL_addchar(b, '"'); - for (; *s; s++) { - switch (*s) { - case '\\': - luaL_addstring(b, "\\\\"); - break; - case '"': - luaL_addstring(b, "\\\""); - break; - case '\b': - luaL_addstring(b, "\\b"); - break; - case '\f': - luaL_addstring(b, "\\f"); - break; - case '\n': - luaL_addstring(b, "\\n"); - break; - case '\r': - luaL_addstring(b, "\\r"); - break; - case '\t': - luaL_addstring(b, "\\t"); - break; - default: - /* Convert UTF-8 to unicode - * 00000000 - 0000007F: 0xxxxxxx - * 00000080 - 000007FF: 110xxxxx 10xxxxxx - * 00000800 - 0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx - * 00010000 - 001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - */ - if ((*s & 0x80) == 0) - luaL_addchar(b, *s); - else if (((*s >> 5) & 0x07) == 0x06) { - luaL_addstring(b, "\\u"); - _snprintf(hexbuf, sizeof hexbuf, "%04x", - ((*s & 0x1f) << 6) | (*(s + 1) & 0x3f)); - luaL_addstring(b, hexbuf); - s++; - } - else if (((*s >> 4) & 0x0f) == 0x0e) { - luaL_addstring(b, "\\u"); - _snprintf(hexbuf, sizeof hexbuf, "%04x", - ((*s & 0x0f) << 12) | - ((*(s + 1) & 0x3f) << 6) | - (*(s + 2) & 0x3f)); - s += 2; - } - else if (((*s >> 3) & 0x1f) == 0x1e) { - /* deliberately ignored */ - s += 3; - } - break; - } - } - luaL_addchar(b, '"'); -} - -static void -encode(lua_State *L, luaL_Buffer *b) -{ - int e, t, n, m; - - switch (lua_type(L, -1)) { - case LUA_TBOOLEAN: - luaL_addstring(b, lua_toboolean(L, -1) ? "true" : "false"); - lua_pop(L, 1); - break; - case LUA_TNUMBER: - luaL_addvalue(b); - break; - case LUA_TSTRING: - encode_string(L, b, (unsigned char *)lua_tostring(L, -1)); - lua_pop(L, 1); - break; - case LUA_TTABLE: - /* check if this is the null value */ - lua_checkstack(L, 2); - if (lua_getmetatable(L, -1)) { - luaL_getmetatable(L, JSON_NULL_METATABLE); -#if LUA_VERSION_NUM >= 502 - if (lua_compare(L, -2, -1, LUA_OPEQ)) { -#else - if (lua_equal(L, -2, -1)) { -#endif - lua_pop(L, 2); - luaL_addstring(b, "null"); - lua_pop(L, 1); - break; - } - lua_pop(L, 2); - } - /* if there are t[1] .. t[n], output them as array */ - n = 0; - e = 1; - for (m = 1;; m++) { - lua_pushnumber(L, m); - lua_gettable(L, -2); - if (lua_isnil(L, -1)) { - lua_pop(L, 1); - break; - } - luaL_addchar(b, n ? ',' : '['); - encode(L, b); - n++; - } - if (n) { - luaL_addchar(b, ']'); - lua_pop(L, 1); - e = 0; - break; - } - - /* output non-numerical indices as object */ - t = lua_gettop(L); - lua_pushnil(L); - n = 0; - while (lua_next(L, t) != 0) { - if (lua_type(L, -2) == LUA_TNUMBER) { - lua_pop(L, 1); - continue; - } - luaL_addstring(b, n ? ",\"" : "{\""); - luaL_addstring(b, lua_tostring(L, -2)); - luaL_addstring(b, "\":"); - encode(L, b); - n++; - } - if (n) { - luaL_addchar(b, '}'); - e = 0; - } - - if (e) - luaL_addstring(b, "[]"); - lua_pop(L, 1); - break; - case LUA_TNIL: - luaL_addstring(b, "null"); - lua_pop(L, 1); - break; - default: - json_error(L, "Lua type %s is incompatible with JSON", - luaL_typename(L, -1)); - lua_pop(L, 1); - } -} - -static int -json_encode(lua_State *L) -{ - luaL_Buffer b; - - luaL_buffinit(L, &b); - encode(L, &b); - luaL_pushresult(&b); - return 1; -} - -static int -json_isnull(lua_State *L) -{ - if (lua_getmetatable(L, -1)) { - luaL_getmetatable(L, JSON_NULL_METATABLE); -#if LUA_VERSION_NUM >= 502 - if (lua_compare(L, -2, -1, LUA_OPEQ)) { -#else - if (lua_equal(L, -2, -1)) { -#endif - lua_pop(L, 2); - lua_pushboolean(L, 1); - goto done; - } - lua_pop(L, 2); - } - lua_pushboolean(L, 0); -done: - return 1; -} - -static void -json_set_info(lua_State *L) -{ - lua_pushliteral(L, "_COPYRIGHT"); - lua_pushliteral(L, "Copyright (C) 2011 - 2015 " - "micro systems marc balmer"); - lua_settable(L, -3); - lua_pushliteral(L, "_DESCRIPTION"); - lua_pushliteral(L, "JSON encoder/decoder for Lua"); - lua_settable(L, -3); - lua_pushliteral(L, "_VERSION"); - lua_pushliteral(L, "json 1.2.3"); - lua_settable(L, -3); -} - -static int -json_null(lua_State *L) -{ - lua_pushstring(L, "null"); - return 1; -} - -LUA_LIBRARY_EXPORT -luaopen_json(lua_State* L) -{ - static const struct luaL_Reg methods[] = { - { "decode", json_decode }, - { "encode", json_encode }, - { "isnull", json_isnull }, - { NULL, NULL } - }; - static const struct luaL_Reg null_methods[] = { - { "__tostring", json_null }, - { "__call", json_null }, - { NULL, NULL } - }; - -#if LUA_VERSION_NUM >= 502 - luaL_newlib(L, methods); -#else - luaL_register(L, "json", methods); -#endif - json_set_info(L); - - lua_newtable(L); - /* The null metatable */ - if (luaL_newmetatable(L, JSON_NULL_METATABLE)) { -#if LUA_VERSION_NUM >= 502 - luaL_setfuncs(L, null_methods, 0); -#else - luaL_register(L, NULL, null_methods); -#endif - lua_pushliteral(L, "__metatable"); - lua_pushliteral(L, "must not access this metatable"); - lua_settable(L, -3); - - } - lua_setmetatable(L, -2); - lua_setfield(L, -2, "null"); - return 1; -} \ No newline at end of file diff --git a/plugins/MirLua/Modules/JSON/src/json.cpp b/plugins/MirLua/Modules/JSON/src/json.cpp new file mode 100644 index 0000000000..edfa04fb83 --- /dev/null +++ b/plugins/MirLua/Modules/JSON/src/json.cpp @@ -0,0 +1,28 @@ +#include "stdafx.h" + +static int lua_decode(lua_State *L) +{ + const char *string = luaL_checkstring(L, 1); + MT *udata = (MT*)lua_newuserdata(L, sizeof(MT)); + udata->node = json_parse(string); + udata->bDelete = true; + luaL_setmetatable(L, MT_JSON); + return 1; +} + +static const luaL_Reg methods[] = +{ + { "decode", lua_decode }, + { NULL, NULL } +}; + +LUA_LIBRARY_EXPORT(json) +{ + luaL_newlib(L, methods); + + luaL_newmetatable(L, MT_JSON); + luaL_setfuncs(L, jsonApi, 0); + lua_pop(L, 1); + + return 1; +} \ No newline at end of file diff --git a/plugins/MirLua/Modules/JSON/src/metatable.cpp b/plugins/MirLua/Modules/JSON/src/metatable.cpp new file mode 100644 index 0000000000..2c6ce9b1d6 --- /dev/null +++ b/plugins/MirLua/Modules/JSON/src/metatable.cpp @@ -0,0 +1,160 @@ +#include "stdafx.h" + +void table2json(lua_State *L, int idx, JSONNode &node) +{ + lua_pushnil(L); + while (lua_next(L, idx) != 0) + { + JSONNode &nnode = (lua_type(L, -2) == LUA_TNUMBER ? (node)[(size_t)lua_tonumber(L, -2) - 1] : (node)[lua_tostring(L, -2)]); + + switch (lua_type(L, -1)) + { + case LUA_TSTRING: + nnode = lua_tostring(L, -1); + break; + case LUA_TBOOLEAN: + nnode = lua_toboolean(L, -1) != 0; + break; + case LUA_TNUMBER: + nnode = lua_tonumber(L, -1); + break; + case LUA_TNIL: + nnode.nullify(); + break; + case LUA_TTABLE: + nnode = JSONNode(nnode.name(), NULL); + table2json(L, -1, nnode); + } + + lua_pop(L, 1); + } +} + +int json_pushvalue(lua_State *L, JSONNode &node) +{ + switch (node.type()) + { + case JSON_NULL: + lua_pushnil(L); + break; + case JSON_STRING: + lua_pushstring(L, node.as_string().c_str()); + break; + case JSON_NUMBER: + lua_pushnumber(L, node.as_int()); + break; + case JSON_BOOL: + lua_pushboolean(L, node.as_bool()); + break; + case JSON_ARRAY: + case JSON_NODE: + MT *udata = (MT*)lua_newuserdata(L, sizeof(MT)); + udata->node = &node; + udata->bDelete = false; + luaL_setmetatable(L, MT_JSON); + } + return 1; +} + +int json_setvalue(lua_State *L, JSONNode &node) +{ + switch (lua_type(L, 3)) + { + case LUA_TSTRING: + node = lua_tostring(L, 3); + break; + case LUA_TBOOLEAN: + node = lua_toboolean(L, 3) != 0; + break; + case LUA_TNUMBER: + node = lua_tonumber(L, 3); + break; + case LUA_TNIL: + node.nullify(); + break; + //case LUA_TTABLE: + //node = JSONNode(node.name(), JSONNode()); + //table2json(L, 3, node); + } + + return 0; +} + +static int json__call(lua_State *L) +{ + if (lua_istable(L, 1)) + { +/* MT *udata = (MT*)lua_newuserdata(L, sizeof(MT)); + udata->node = json_new(JSON_NODE); + table2json(L, 1, *udata->node); + udata->bDelete = true; + luaL_setmetatable(L, MT_JSON); + return 1; +*/ + } + return 0; +} + +static int json__index(lua_State *L) +{ + JSONNode *node = ((MT*)luaL_checkudata(L, 1, MT_JSON))->node; + switch (lua_type(L, 2)) + { + case LUA_TNUMBER: + return json_pushvalue(L, (*node)[(size_t)lua_tonumber(L, 2) - 1]); + case LUA_TSTRING: + return json_pushvalue(L, (*node)[lua_tostring(L, 2)]); + } + return 0; +} + +static int json__newindex(lua_State *L) +{ + JSONNode *node = ((MT*)luaL_checkudata(L, 1, MT_JSON))->node; + switch (lua_type(L, 2)) + { + case LUA_TNUMBER: + json_setvalue(L, (*node)[(size_t)lua_tonumber(L, 2) - 1]); + break; + case LUA_TSTRING: + json_setvalue(L, (*node)[lua_tostring(L, 2)]); + break; + } + return 0; +} + +static int json__len(lua_State *L) +{ + JSONNode *node = ((MT*)luaL_checkudata(L, 1, MT_JSON))->node; + lua_pushnumber(L, (*node).size()); + return 1; +} + +static int json__tostring(lua_State *L) +{ + JSONNode *node = ((MT*)luaL_checkudata(L, 1, MT_JSON))->node; + lua_pushstring(L, (*node).write().c_str()); + return 1; +} + +static int json__gc(lua_State *L) +{ + MT *mt = (MT*)luaL_checkudata(L, 1, MT_JSON); + if (mt->bDelete) + { + json_delete(mt->node); + mt->node = nullptr; + } + return 0; +} + +static const struct luaL_Reg jsonApi[] = +{ + { "__call", json__call }, + { "__index", json__index }, + { "__newindex", json__newindex }, + { "__len", json__len }, + { "__tostring", json__tostring }, + { "__gc", json__gc }, + { NULL, NULL } +}; diff --git a/plugins/MirLua/Modules/JSON/src/stdafx.h b/plugins/MirLua/Modules/JSON/src/stdafx.h index 7853175a06..104b898845 100644 --- a/plugins/MirLua/Modules/JSON/src/stdafx.h +++ b/plugins/MirLua/Modules/JSON/src/stdafx.h @@ -1,59 +1,21 @@ -/* -* Copyright (c) 2011 - 2015, Micro Systems Marc Balmer, CH-5073 Gipf-Oberfrick -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Micro Systems Marc Balmer nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -* ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ +#pragma once -/* JSON interface for Lua */ +#define LUA_LIBRARY_EXPORT(x) EXTERN_C int __declspec(dllexport) luaopen_##x(lua_State* L) -/* -* This code has been derived from the public domain LuaJSON Library 1.1 -* written by Nathaniel Musgrove (proton.zero@gmail.com), for the original -* code see http://luaforge.net/projects/luajsonlib/ -*/ -#ifdef __cplusplus -# define LUA_LIBRARY_EXPORT extern "C" int __declspec(dllexport) -#else -# define LUA_LIBRARY_EXPORT int __declspec(dllexport) -#endif +#include +#include +#include +//#include "..\..\..\src\mlua_metatable.h" -#include +#include +#include -#include +struct MT +{ + JSONNode *node; + bool bDelete; +}; -#include -#include -#include -#include -#include -#include - -#define JSON_NULL_METATABLE "JSON null object methods" - -static void decode_value(lua_State *, char **, int); -static void decode_string(lua_State *, char **); -static void encode(lua_State *, luaL_Buffer *); - -static jmp_buf env; +extern const luaL_Reg jsonApi[]; +#define MT_JSON "JSON_METATABLE" \ No newline at end of file -- cgit v1.2.3