Tarantool development patches archive
 help / color / mirror / Atom feed
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: Fri,  8 May 2020 14:47:13 +0300	[thread overview]
Message-ID: <20200508114714.426908-7-gorcunov@gmail.com> (raw)
In-Reply-To: <20200508114714.426908-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           | 71 ++++++++++++++--------------
 test/app-tap/console_lua.test.lua | 18 ++++----
 3 files changed, 120 insertions(+), 46 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..e6e471a7d 100644
--- a/src/box/lua/console.lua
+++ b/src/box/lua/console.lua
@@ -69,47 +69,35 @@ 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, ...)
@@ -121,8 +109,17 @@ output_handlers["lua"] = function(status, opts, ...)
     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 +207,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

  parent reply	other threads:[~2020-05-08 11:48 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-08 11:47 [Tarantool-patches] [PATCH 0/7] box/console: add support for internal types Cyrill Gorcunov
2020-05-08 11:47 ` [Tarantool-patches] [PATCH 1/7] box/console: console_session_vtab -- use designated initialization Cyrill Gorcunov
2020-05-08 11:47 ` [Tarantool-patches] [PATCH 2/7] box/console: use tabs instead of spaces in consolelib Cyrill Gorcunov
2020-05-08 11:47 ` [Tarantool-patches] [PATCH 3/7] box/console: rename format to format_yaml Cyrill Gorcunov
2020-05-08 11:47 ` [Tarantool-patches] [PATCH 4/7] box/console: rename luaL_yaml_default to serializer_yaml Cyrill Gorcunov
2020-05-08 11:47 ` [Tarantool-patches] [PATCH 5/7] box/console: implement lua serializer Cyrill Gorcunov
2020-05-08 11:47 ` Cyrill Gorcunov [this message]
2020-05-08 11:47 ` [Tarantool-patches] [PATCH 7/7] test: extend console lua test Cyrill Gorcunov
2020-05-08 16:04 ` [Tarantool-patches] [PATCH 0/7] box/console: add support for internal types Oleg Babin
2020-05-08 16:19   ` Cyrill Gorcunov
2020-05-12 17:06     ` Mons Anderson
2020-05-12 17:31       ` Cyrill Gorcunov
2020-05-12 13:53 ` Cyrill Gorcunov
2020-05-12 13:50 Cyrill Gorcunov
2020-05-12 13:50 ` [Tarantool-patches] [PATCH 6/7] box/console: switch to new lua serializer Cyrill Gorcunov
2020-05-18 12:21   ` Oleg Babin

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=20200508114714.426908-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