Created
November 29, 2018 10:05
-
-
Save knazarov/6681c5055c1f6bf06eee8a6a09489f8b to your computer and use it in GitHub Desktop.
Tarantool output in Lua instead of YAML
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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