Skip to content

Instantly share code, notes, and snippets.

@knazarov
Created November 29, 2018 10:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save knazarov/20a6e29d49b968a26ad99abcd91ba81a to your computer and use it in GitHub Desktop.
Save knazarov/20a6e29d49b968a26ad99abcd91ba81a to your computer and use it in GitHub Desktop.
Tarantool output in Lua instead of YAML
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index d1276472b..2ab48db84 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -12,6 +12,7 @@ lua_source(lua_sources lua/feedback_daemon.lua)
lua_source(lua_sources lua/net_box.lua)
lua_source(lua_sources lua/upgrade.lua)
lua_source(lua_sources lua/console.lua)
+lua_source(lua_sources lua/serialize.lua)
lua_source(lua_sources lua/xlog.lua)
set(bin_sources)
bin_source(bin_sources bootstrap.snap bootstrap.h)
diff --git a/src/box/lua/console.lua b/src/box/lua/console.lua
index 028001127..ae6ca0cfc 100644
--- a/src/box/lua/console.lua
+++ b/src/box/lua/console.lua
@@ -9,6 +9,7 @@ local errno = require('errno')
local urilib = require('uri')
local yaml = require('yaml')
local net_box = require('net.box')
+local serialize = require('serialize')
local YAML_TERM = '\n...\n'
local PUSH_TAG_HANDLE = '!push!'
@@ -17,7 +18,15 @@ local function format(status, ...)
local err
if status then
-- serializer can raise an exception
- status, err = pcall(internal.format, ...)
+ local args = {...}
+ --status, err = pcall(internal.format, ...)
+ if #args == 0 then
+ status, err = true, "nil"
+ elseif #args == 1 then
+ status, err = pcall(serialize.serialize, args[1])
+ else
+ status, err = pcall(serialize.serialize, args)
+ end
if status then
return err
else
@@ -30,7 +39,8 @@ local function format(status, ...)
err = box.NULL
end
end
- return internal.format({ error = err })
+ --return internal.format({ error = err })
+ return tostring(err)
end
--
@@ -272,19 +282,25 @@ local function remote_eval(self, line)
end
local function local_check_lua(buf)
- local fn, err = loadstring(buf)
- if fn ~= nil or not string.find(err, " near '<eof>'$") then
- -- valid Lua code or a syntax error not due to
- -- an incomplete input
- return true
- end
- if loadstring('return '..buf) ~= nil then
- -- certain obscure inputs like '(42\n)' yield the
- -- same error as incomplete statement
+ -- certain inputs like '(42\n)' or "{}" yield the
+ -- same error as incomplete statement. They only
+ -- evaluate when prefixed with "return"
+ local prefixes = {"return ", ""}
+
+ for _, prefix in ipairs(prefixes) do
+ local fn, err = loadstring(prefix..buf)
+ if fn ~= nil then
+ -- valid Lua code
return true
end
+ if string.find(err, " near '<eof>'$") then
+ -- definitely an incomplete code
return false
end
+ end
+
+ return true
+end
--
-- Read command from stdin
diff --git a/src/box/lua/init.c b/src/box/lua/init.c
index ccb4c6a46..d2cf7a5e6 100644
--- a/src/box/lua/init.c
+++ b/src/box/lua/init.c
@@ -69,7 +69,8 @@ extern char session_lua[],
feedback_daemon_lua[],
net_box_lua[],
upgrade_lua[],
- console_lua[];
+ console_lua[],
+ serialize_lua[];
static const char *lua_sources[] = {
"box/session", session_lua,
@@ -79,6 +80,7 @@ static const char *lua_sources[] = {
"box/feedback_daemon", feedback_daemon_lua,
"box/upgrade", upgrade_lua,
"box/net_box", net_box_lua,
+ "box/serialize", serialize_lua,
"box/console", console_lua,
"box/load_cfg", load_cfg_lua,
"box/xlog", xlog_lua,
diff --git a/src/box/lua/serialize.lua b/src/box/lua/serialize.lua
new file mode 100644
index 000000000..816eff130
--- /dev/null
+++ b/src/box/lua/serialize.lua
@@ -0,0 +1,205 @@
+local lua_keyword = {
+ ["and"] = true, ["break"] = true, ["do"] = true,
+ ["else"] = true, ["elseif"] = true, ["end"] = true,
+ ["false"] = true, ["for"] = true, ["function"] = true,
+ ["if"] = true, ["in"] = true, ["local"] = true, ["nil"] = true,
+ ["not"] = true, ["or"] = true, ["repeat"] = true,
+ ["return"] = true, ["then"] = true, ["true"] = true,
+ ["until"] = true, ["while"] = true
+}
+
+
+local function has_lquote(s)
+ local lstring_pat = '([%[%]])(=*)%1'
+ local equals, new_equals, _
+ local finish = 1
+ repeat
+ _, finish, _, new_equals = s:find(lstring_pat, finish)
+ if new_equals then
+ equals = math.max(equals or 0, #new_equals)
+ end
+ until not new_equals
+
+ return equals
+end
+
+local function is_identifier (s)
+ return type(s) == 'string' and s:find('^[%a_][%w_]*$') and not lua_keyword[s]
+end
+
+local function quote_string(s)
+ if type(s) ~= 'string' then
+ error("quote_string: s should be a string")
+ end
+
+ -- Find out if there are any embedded long-quote sequences that may cause issues.
+ -- This is important when strings are embedded within strings, like when serializing.
+ -- Append a closing bracket to catch unfinished long-quote sequences at the end of the string.
+ local equal_signs = has_lquote(s .. "]")
+
+ -- Note that strings containing "\r" can't be quoted using long brackets
+ -- as Lua lexer converts all newlines to "\n" within long strings.
+ if (s:find("\n") or equal_signs) and not s:find("\r") then
+ -- If there is an embedded sequence that matches a long quote, then
+ -- find the one with the maximum number of = signs and add one to that number.
+ equal_signs = ("="):rep((equal_signs or -1) + 1)
+ -- Long strings strip out leading newline. We want to retain that, when quoting.
+ if s:find("^\n") then s = "\n" .. s end
+ local lbracket, rbracket =
+ "[" .. equal_signs .. "[",
+ "]" .. equal_signs .. "]"
+ s = lbracket .. s .. rbracket
+ else
+ -- Escape funny stuff. Lua 5.1 does not handle "\r" correctly.
+ s = ("%q"):format(s):gsub("\r", "\\r")
+ end
+ return s
+end
+
+local function quote_if_necessary (v)
+ if not v then return ''
+ else
+ --AAS
+ if v:find ' ' then v = quote_string(v) end
+ end
+ return v
+end
+
+local serialize = nil
+
+local function quote (s)
+ if type(s) == 'table' then
+ return pretty.write(s,'')
+ else
+ --AAS
+ return quote_string(s)-- ('%q'):format(tostring(s))
+ end
+end
+
+local function index (numkey,key)
+ --AAS
+ if not numkey then
+ key = quote(key)
+ key = key:find("^%[") and (" " .. key .. " ") or key
+ end
+ return '['..key..']'
+end
+
+--- Create a string representation of a Lua table.
+-- This function never fails, but may complain by returning an
+-- extra value. Normally puts out one item per line, using
+-- the provided indent; set the second parameter to an empty string
+-- if you want output on one line.
+-- @tab tbl Table to serialize to a string.
+-- @string[opt] space The indent to use.
+-- Defaults to two spaces; pass an empty string for no indentation.
+-- @bool[opt] not_clever Pass `true` for plain output, e.g `{['key']=1}`.
+-- Defaults to `false`.
+-- @return a string
+-- @return an optional error message
+serialize = function(tbl,space,not_clever)
+ if type(tbl) ~= 'table' then
+ local res = tostring(tbl)
+ if type(tbl) == 'string' then return quote(tbl) end
+ return res, 'not a table'
+ end
+
+ local set = ' = '
+ if space == '' then set = '=' end
+ space = space or ' '
+ local lines = {}
+ local line = ''
+ local tables = {}
+
+
+ local function put(s)
+ if #s > 0 then
+ line = line..s
+ end
+ end
+
+ local function putln (s)
+ if #line > 0 then
+ line = line..s
+ table.insert(lines,line)
+ line = ''
+ else
+ table.insert(lines,s)
+ end
+ end
+
+ local function eat_last_comma ()
+ local n = #lines
+ local lastch = lines[n]:sub(-1,-1)
+ if lastch == ',' then
+ lines[n] = lines[n]:sub(1,-2)
+ end
+ end
+
+
+ local writeit
+ writeit = function (t,oldindent,indent)
+ local tp = type(t)
+ if t == box.NULL then
+ putln('box.NULL,')
+ elseif tp ~= 'string' and tp ~= 'table' then
+ putln(quote_if_necessary(tostring(t))..',')
+ elseif tp == 'string' then
+ -- if t:find('\n') then
+ -- putln('[[\n'..t..']],')
+ -- else
+ -- putln(quote(t)..',')
+ -- end
+ --AAS
+ putln(quote_string(t) ..",")
+ elseif tp == 'table' then
+ if tables[t] then
+ putln('<cycle>,')
+ return
+ end
+ tables[t] = true
+ local newindent = indent..space
+ putln('{')
+ local used = {}
+ if not not_clever then
+ for i,val in ipairs(t) do
+ put(indent)
+ writeit(val,indent,newindent)
+ used[i] = true
+ end
+ end
+ for key,val in pairs(t) do
+ local tkey = type(key)
+ local numkey = tkey == 'number'
+ if not_clever then
+ key = tostring(key)
+ put(indent..index(numkey,key)..set)
+ writeit(val,indent,newindent)
+ else
+ if not numkey or not used[key] then -- non-array indices
+ if tkey ~= 'string' then
+ key = tostring(key)
+ end
+ if numkey or not is_identifier(key) then
+ key = index(numkey,key)
+ end
+ put(indent..key..set)
+ writeit(val,indent,newindent)
+ end
+ end
+ end
+ tables[t] = nil
+ eat_last_comma()
+ putln(oldindent..'},')
+ else
+ putln(tostring(t)..',')
+ end
+ end
+ writeit(tbl,'',space)
+ eat_last_comma()
+ return table.concat(lines,#space > 0 and '\n' or '')
+end
+
+package.loaded['serialize'] = {
+ serialize = serialize
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment