From: Cyrill Gorcunov <gorcunov@gmail.com> To: tml <tarantool-patches@dev.tarantool.org> Subject: [Tarantool-patches] [PATCH 6/7] box/console: switch to new lua serializer Date: Tue, 12 May 2020 16:50:51 +0300 [thread overview] Message-ID: <20200512135052.221379-7-gorcunov@gmail.com> (raw) In-Reply-To: <20200512135052.221379-1-gorcunov@gmail.com> I do not drop the serpent module for a while since I need to make sure that everything work as expected as we've not break backward compatibility significantly (though we didn't claim the lua mode output is stable enough). Fixes #4682 Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com> --- src/box/lua/console.c | 77 +++++++++++++++++++++++++++++++ src/box/lua/console.lua | 76 +++++++++++++----------------- test/app-tap/console_lua.test.lua | 18 ++++---- 3 files changed, 119 insertions(+), 52 deletions(-) diff --git a/src/box/lua/console.c b/src/box/lua/console.c index c8e0dea58..d1bf7949c 100644 --- a/src/box/lua/console.c +++ b/src/box/lua/console.c @@ -39,6 +39,7 @@ #include "coio.h" #include "lua/msgpack.h" #include "lua-yaml/lyaml.h" +#include "serialize_lua.h" #include <lua.h> #include <lauxlib.h> #include <lualib.h> @@ -50,6 +51,7 @@ extern char serpent_lua[]; static struct luaL_serializer *serializer_yaml; +static struct luaL_serializer *serializer_lua; /* * Completion engine (Mike Paul's). @@ -68,6 +70,54 @@ lua_rl_complete(lua_State *L, const char *text, int start, int end); */ static struct lua_State *readline_L; +/** + * Encode Lua object into Lua form. + */ +static int +lbox_console_format_lua(struct lua_State *L) +{ + lua_dumper_opts_t opts; + int arg_count; + + /* Parse options and remove them */ + lua_parse_opts(L, &opts); + lua_remove(L, 1); + + arg_count = lua_gettop(L); + + /* If nothing to process just exit early */ + if (arg_count == 0) + return 0; + + /* + * When processing arguments we might + * need to modify reference (for example + * when __index references to object itself) + * thus make a copy of incoming data. + * + * Note that in yaml there is no map for + * Lua's "nil" value so for yaml encoder + * we do + * + * if (lua_isnil(L, i + 1)) + * luaL_pushnull(L); + * else + * lua_pushvalue(L, i + 1); + * + * + * For lua mode we have to preserve "nil". + */ + lua_createtable(L, arg_count, 0); + for (int i = 0; i < arg_count; ++i) { + lua_pushvalue(L, i + 1); + lua_rawseti(L, -2, i + 1); + } + + lua_replace(L, 1); + lua_settop(L, 1); + return lua_encode(L, serializer_lua, &opts); +} + /* * console_completion_handler() * Called by readline to collect plausible completions; @@ -557,6 +607,7 @@ tarantool_lua_console_init(struct lua_State *L) {"add_history", lbox_console_add_history}, {"completion_handler", lbox_console_completion_handler}, {"format_yaml", lbox_console_format_yaml}, + {"format_lua", lbox_console_format_lua}, {NULL, NULL} }; luaL_register_module(L, "console", consolelib); @@ -581,6 +632,32 @@ tarantool_lua_console_init(struct lua_State *L) * load_history work the same way. */ lua_setfield(L, -2, "formatter"); + + /* + * We don't export it as a module + * for a while, so the library + * is kept empty. + */ + static const luaL_Reg lualib[] = { + { }, + }; + + serializer_lua = luaL_newserializer(L, NULL, lualib); + serializer_lua->has_compact = 1; + serializer_lua->encode_invalid_numbers = 1; + serializer_lua->encode_load_metatables = 1; + serializer_lua->encode_use_tostring = 1; + serializer_lua->encode_invalid_as_nil = 1; + + /* + * Keep a reference to this module so it + * won't be unloaded. + */ + lua_setfield(L, -2, "formatter_lua"); + + /* Output formatter in Lua mode */ + lua_serializer_init(L); + struct session_vtab console_session_vtab = { .push = console_session_push, .fd = console_session_fd, diff --git a/src/box/lua/console.lua b/src/box/lua/console.lua index 5642ca956..6ea27a393 100644 --- a/src/box/lua/console.lua +++ b/src/box/lua/console.lua @@ -69,60 +69,50 @@ output_handlers["yaml"] = function(status, opts, ...) return internal.format_yaml({ error = err }) end --- A map for internal symbols in case if they --- are not inside tables and serpent won't be --- able to handle them properly. -local lua_map_direct_symbols = { - [box.NULL] = 'box.NULL', -} - --- A map for internal symbols in case if they --- are coming from tables and we need to depict --- them into user known values. -local lua_map_table_symbols = { - ['"cdata<void %*>: NULL"'] = 'box.NULL' -} - -- --- Map internal symbols which serpent doesn't --- know about to a known representation. -local serpent_map_symbols = function(tag, head, body, tail, level) - for k,v in pairs(lua_map_table_symbols) do - body = body:gsub(k, v) +-- Format a Lua value. +local function format_lua_value(status, internal_opts, value) + local err + if status then + status, err = pcall(internal.format_lua, internal_opts, value) + if status then + return err + else + local m = 'console: exception while formatting output: "%s"' + err = m:format(tostring(err)) + end + else + err = value + if err == nil then + err = box.NULL + end end - return tag..head..body..tail + return internal.format_lua(internal_opts, { error = err }) end -- --- Format a Lua value. -local function format_lua_value(value, opts) - for k,v in pairs(lua_map_direct_symbols) do - if k == value then - return v - end - end - local serpent_opts = { - custom = serpent_map_symbols, - comment = false, - nocode = true, - } +-- Convert options from user form to the internal format. +local function gen_lua_opts(opts) if opts == "block" then - return serpent.block(value, serpent_opts) + return {block=true, indent=2} + else + return {block=false, indent=2} end - return serpent.line(value, serpent_opts) end output_handlers["lua"] = function(status, opts, ...) local collect = {} -- - -- If no data present at least EOS should be put, - -- otherwise wire readers won't be able to find - -- where the end of string is. - if not ... then - return output_eos["lua"] - end + -- Convert options to internal dictionary. + local internal_opts = gen_lua_opts(opts) for i = 1, select('#', ...) do - collect[i] = format_lua_value(select(i, ...), opts) + collect[i] = format_lua_value(status, internal_opts, select(i, ...)) + if collect[i] == nil then + -- + -- table.concat doesn't work for nil + -- so convert it explicitly. + collect[i] = "nil" + end end return table.concat(collect, ', ') .. output_eos["lua"] end @@ -210,8 +200,8 @@ end -- Used by console_session_push. box_internal.format_lua_push = function(value) - local opts = current_output()["opts"] - value = format_lua_value(value, opts) + local internal_opts = gen_lua_opts(current_output()["opts"]) + value = format_lua_value(true, internal_opts, value) return '-- Push\n' .. value .. ';' end diff --git a/test/app-tap/console_lua.test.lua b/test/app-tap/console_lua.test.lua index 3ed6aad97..8bb6eb0b3 100755 --- a/test/app-tap/console_lua.test.lua +++ b/test/app-tap/console_lua.test.lua @@ -102,29 +102,29 @@ local cases = { prepare = 'a = {1, 2, 3}', opts = {block = false}, input = '1, 2, nil, a', - expected = '1, 2, box.NULL, {1, 2, 3}', + expected = '1, 2, nil, {1, 2, 3}', cleanup = 'a = nil', }, { name = 'multireturn block mode', prepare = 'a = {1, 2, 3}', opts = {block = true}, input = '1, 2, nil, a', - expected = '1, 2, box.NULL, {\n 1,\n 2,\n 3\n}', + expected = '1, 2, nil, {\n 1,\n 2,\n 3\n}', cleanup = 'a = nil', }, { - name = 'trailing nils, line mode', + name = 'trailing nils, box.NULL, line mode', opts = {block = false}, - input = '1, nil, nil, nil', - expected = '1, box.NULL, box.NULL, box.NULL', + input = '1, nil, box.NULL, nil', + expected = '1, nil, box.NULL, nil', }, { - name = 'trailing nils, block mode', + name = 'trailing nils, box.NULL, block mode', opts = {block = true}, - input = '1, nil, nil, nil', - expected = '1, box.NULL, box.NULL, box.NULL', + input = '1, nil, box.NULL, nil', + expected = '1, nil, box.NULL, nil', }, { name = 'empty output', input = '\\set output', - expected = '"Specify output format: lua or yaml."', + expected = '{error = \"Specify output format: lua or yaml.\"}', } } -- 2.26.2
next prev parent reply other threads:[~2020-05-12 13:52 UTC|newest] Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top 2020-05-12 13:50 [Tarantool-patches] [PATCH 0/7] box/console: add support for internal types Cyrill Gorcunov 2020-05-12 13:50 ` [Tarantool-patches] [PATCH 1/7] box/console: console_session_vtab -- use designated initialization Cyrill Gorcunov 2020-05-18 12:03 ` Oleg Babin 2020-05-12 13:50 ` [Tarantool-patches] [PATCH 2/7] box/console: use tabs instead of spaces in consolelib Cyrill Gorcunov 2020-05-18 12:04 ` Oleg Babin 2020-05-12 13:50 ` [Tarantool-patches] [PATCH 3/7] box/console: rename format to format_yaml Cyrill Gorcunov 2020-05-18 12:04 ` Oleg Babin 2020-05-12 13:50 ` [Tarantool-patches] [PATCH 4/7] box/console: rename luaL_yaml_default to serializer_yaml Cyrill Gorcunov 2020-05-18 12:11 ` Oleg Babin 2020-05-18 21:17 ` Igor Munkin 2020-05-19 6:47 ` Oleg Babin 2020-05-12 13:50 ` [Tarantool-patches] [PATCH 5/7] box/console: implement lua serializer Cyrill Gorcunov 2020-05-18 12:18 ` Oleg Babin 2020-05-12 13:50 ` Cyrill Gorcunov [this message] 2020-05-18 12:21 ` [Tarantool-patches] [PATCH 6/7] box/console: switch to new " Oleg Babin 2020-05-12 13:50 ` [Tarantool-patches] [PATCH 7/7] test: extend console lua test Cyrill Gorcunov 2020-05-18 12:22 ` Oleg Babin 2020-05-18 12:25 ` Cyrill Gorcunov 2020-05-18 12:46 ` [Tarantool-patches] [PATCH 0/7] box/console: add support for internal types Kirill Yukhin -- strict thread matches above, loose matches on Subject: below -- 2020-05-08 11:47 Cyrill Gorcunov 2020-05-08 11:47 ` [Tarantool-patches] [PATCH 6/7] box/console: switch to new lua serializer Cyrill Gorcunov
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20200512135052.221379-7-gorcunov@gmail.com \ --to=gorcunov@gmail.com \ --cc=tarantool-patches@dev.tarantool.org \ --subject='Re: [Tarantool-patches] [PATCH 6/7] box/console: switch to new lua serializer' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox