summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOpportunityLiu <[email protected]>2019-07-25 17:43:55 +0800
committerOpportunityLiu <[email protected]>2019-07-25 17:43:55 +0800
commit980fe707e37687ceb5a2653298001845377136ee (patch)
treed21cf5ef881d543d6256b4ae388bcb8178073134
parent95169f0ec740f0466dfab97424e3c72460764f17 (diff)
refactor serialize functions
-rw-r--r--xmake/core/base/math.lua11
-rw-r--r--xmake/core/base/serialize.lua272
-rw-r--r--xmake/core/base/string.lua146
3 files changed, 290 insertions, 139 deletions
diff --git a/xmake/core/base/math.lua b/xmake/core/base/math.lua
index a2dad7a6e..d49e8a8b9 100644
--- a/xmake/core/base/math.lua
+++ b/xmake/core/base/math.lua
@@ -25,6 +25,17 @@ local math = math or {}
math.nan = math.log(-1)
math.e = math.exp(1)
+-- check a number is int
+--
+-- @returns true for int, otherwise false
+function math:isint()
+
+ -- check
+ assert(type(self) == "number", "number expacted")
+
+ return self == math.floor(self) and self ~= math.huge and self ~= -math.huge
+end
+
-- check a number is inf or -inf
--
-- @returns 1 for inf, -1 for -inf, otherwise false
diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua
new file mode 100644
index 000000000..0af3a27a0
--- /dev/null
+++ b/xmake/core/base/serialize.lua
@@ -0,0 +1,272 @@
+
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015 - 2019, TBOOX Open Source Group.
+--
+-- @author OpportunityLiu
+-- @file serialize.lua
+--
+
+-- define module: serialize
+local serialize = serialize or {}
+
+-- load modules
+local math = require("base/math")
+
+-- save original interfaces
+serialize._dump = serialize._dump or string._dump or string.dump
+
+serialize._FUNCTAG = "\27FUNCTION"
+
+function serialize._makenumber(num, opt, level)
+ if math.isnan(num) then
+ return "math.nan"
+ end
+ local inf = math.isinf(num)
+ if inf == 1 then
+ return "math.huge"
+ elseif inf == -1 then
+ return "-math.huge"
+ end
+ return tostring(num)
+end
+
+function serialize._makestring(str, opt, level)
+ return string.format("%q", str)
+end
+
+function serialize._makekeyword(val, opt, level)
+ return tostring(val)
+end
+
+function serialize._maketable(object, opt, level)
+
+ local childlevel = level + 1
+
+ -- serialize child items
+ local serialized = {}
+ local numidxcount = 0
+ local isarr = true
+ local maxn = 0
+ for k, v in pairs(object) do
+ if type(k) == "number" then
+ numidxcount = numidxcount + 1
+ if k < 1 or not math.isint(k) then
+ isarr = false
+ elseif k > maxn then
+ maxn = k
+ end
+ elseif type(k) == "string" then
+ isarr = false
+ else
+ return nil, string.format("cannot serialize table with key of %s: %s", type(k), tostring(k))
+ end
+ serialized[k] = serialize._make(v, opt, childlevel)
+ end
+
+ -- too sparse
+ if numidxcount * 2 < maxn then
+ isarr = false
+ end
+
+ -- make indent
+ local indent = ""
+ if opt.indent then
+ indent = string.rep(" ", level)
+ end
+
+ -- make head
+ local headstr
+ if opt.indent then
+ headstr = (level > 0 and "\n" or "") .. indent .. "{\n"
+ else
+ headstr = "{"
+ end
+ -- make tail
+ local tailstr
+ if opt.indent then
+ tailstr = "\n" .. indent .. "}"
+ else
+ tailstr = "}"
+ end
+
+ -- make body
+ local s = {}
+ if opt.indent then
+ indent = indent .. " "
+ end
+
+ if isarr then
+ local nilval
+ if maxn ~= numidxcount then
+ nilval = indent .. "nil"
+ end
+ for i = 1, maxn do
+ local val = serialized[i]
+ if val == nil then
+ s[i] = nilval
+ else
+ s[i] = indent .. val
+ end
+ end
+ else
+ local con = opt.indent and " = " or "="
+ for k, v in pairs(serialized) do
+ if type(k) == "string" and not k:match("^[%a_][%w_]*$") then
+ k = string.format("[%q]", k)
+ elseif type(k) == "number" then
+ k = string.format("[%s]", serialize._makenumber(k, opt, childlevel))
+ end
+ table.insert(s, indent .. k .. con .. v)
+ end
+ end
+ return headstr .. table.concat(s, opt.indent and ",\n" or ",") .. tailstr
+end
+
+function serialize._makefunction(func, opt, level)
+ local ok, funccode = pcall(serialize._dump, func, opt.strip)
+ if not ok then
+ return nil, funccode
+ end
+ local closure = {}
+ -- local nups = debug.getinfo(func, 'u').nups
+ -- for i = 1, nups do
+ -- local k, v = debug.getupvalue(func, i)
+ -- if _G[k] == v then
+ -- v = nil
+ -- else
+ -- v = "haha"
+ -- end
+ -- closure[i] = { k, v }
+ -- end
+ -- closure.nups = nups
+ closure.func = funccode
+ closure.tag = serialize._FUNCTAG
+ return serialize._maketable(closure, opt, level + 1)
+end
+
+-- make string with the level
+function serialize._make(object, opt, level)
+
+ -- call make* by type
+ if type(object) == "string" then
+ return serialize._makestring(object, opt, level)
+ elseif type(object) == "boolean" or type(object) == "nil" then
+ return serialize._makekeyword(object, opt, level)
+ elseif type(object) == "number" then
+ return serialize._makenumber(object, opt, level)
+ elseif type(object) == "table" then
+ return serialize._maketable(object, opt, level)
+ elseif type(object) == "function" then
+ return serialize._makefunction(object, opt, level)
+ else
+ return nil, string.format("cannot serialize %s: '%s'", type(object), tostring(object))
+ end
+end
+
+-- serialize to string from the given object
+--
+-- @param opt serialize options
+--
+-- @return string, errors
+--
+function serialize.save(object, opt)
+
+ -- init options
+ opt = opt or {}
+
+ -- make string
+ local result, errors = serialize._make(object, opt, 0)
+
+ -- ok?
+ if errors ~= nil then
+ return nil, errors
+ end
+ return result
+end
+
+function serialize._loadfunction(closure)
+ local func, err = loadstring(closure.func, "=(deserialized code)")
+ if func == nil then
+ return nil, err
+ end
+ -- for i = 1, closure.nups do
+ -- local upval = closure[i]
+ -- if upval[2] == nil then
+ -- upval[2] = _G[upval[1]]
+ -- end
+ -- debug.setupvalue(func, i, upval[2])
+ -- end
+ return func
+end
+
+-- load table from string in table
+function serialize._load(object)
+ if type(object) == "table" then
+ if object.tag == serialize._FUNCTAG then
+ return serialize._loadfunction(object)
+ end
+ for k, v in pairs(object) do
+ local value, errors = serialize._load(v)
+ if value ~= nil then
+ object[k] = value
+ else
+ return nil, errors
+ end
+ end
+ end
+ return object
+end
+
+-- deserialize string to object
+--
+-- @param str the serialized string
+--
+-- @return object, errors
+--
+function serialize.load(str)
+
+ -- load table as script
+ local result = nil
+ local script, errors = loadstring("return " .. str)
+ if script then
+
+ -- load object
+ local ok, object = pcall(script)
+ if ok then
+ result = object
+ elseif object then
+ -- error
+ errors = object
+ else
+ -- error
+ errors = string.format("cannot deserialize string: %s", str)
+ end
+ end
+
+ -- load function from string in table
+ if result then
+ result, errors = serialize._load(result)
+ end
+
+ -- ok?
+ if errors ~= nil then
+ return nil, errors
+ end
+ return result
+end
+
+-- return module: serialize
+return serialize
diff --git a/xmake/core/base/string.lua b/xmake/core/base/string.lua
index d3a584f3e..30c374def 100644
--- a/xmake/core/base/string.lua
+++ b/xmake/core/base/string.lua
@@ -23,120 +23,12 @@ local string = string or {}
-- load modules
local deprecated = require("base/deprecated")
+local serialize = require("base/serialize")
-- save original interfaces
string._dump = string._dump or string.dump
string._trim = string._trim or string.trim
--- make string with the level
-function string._makestr(object, deflate, serialize, level)
- if type(object) == "string" then
- return serialize and string.format("%q", object) or object
- elseif type(object) == "boolean" or type(object) == "nil" then
- return tostring(object)
- elseif type(object) == "number" then
- if serialize then
- if math.isnan(object) then
- return "math.nan"
- end
- local inf = math.isinf(object)
- if inf == 1 then
- return "math.huge"
- elseif inf == -1 then
- return "-math.huge"
- end
- end
- return tostring(object)
- elseif not serialize and type(object) == "table" and (getmetatable(object) or {}).__tostring then
- return tostring(object)
- elseif type(object) == "table" then
-
- local indent = ""
- if not deflate then
- indent = string.rep(" ", level)
- end
-
- -- make head
- local s = ""
- if deflate then
- s = s .. "{"
- else
- if level > 0 then
- s = s .. "\n"
- end
- s = s .. indent .. "{\n"
- end
-
- -- make body
- local i = 0
- for k, v in pairs(object) do
-
- if deflate then
- s = s .. (i ~= 0 and "," or "")
- else
- s = s .. indent
- if i == 0 then
- s = s .. " "
- else
- s = s .. ", "
- end
- end
-
- -- make key = value
- if type(k) == "string" then
- if serialize and not k:match("^[%a_][%w_]+$") then
- k = string.format("[%q]", k)
- end
- if deflate then
- s = s .. k .. "="
- else
- s = s .. k .. " = "
- end
- end
- local substr, errors = string._makestr(v, deflate, serialize, level + 1)
- if substr == nil then
- return nil, errors
- end
- s = s .. substr
-
- if not deflate then
- s = s .. "\n"
- end
- i = i + 1
- end
-
- -- make tail
- s = s .. indent .. "}"
- return s
- elseif serialize and type(object) == "function" then
- return string.format("%q", string._dump(object))
- elseif serialize then
- return nil, "cannot serialize object: " .. type(object)
- elseif object ~= nil then
- return "<" .. tostring(object) .. ">"
- else
- return "nil"
- end
-end
-
--- load table from string in table
-function string._loadstr(object)
- -- only load luajit function data: e.g. "\27LJ\2\0\6=stdin"
- if type(object) == "string" and object:startswith("\27LJ") then
- return loadstring(object)
- elseif type(object) == "table" then
- for k, v in pairs(object) do
- local value, errors = string._loadstr(v)
- if value ~= nil then
- object[k] = value
- else
- return nil, errors
- end
- end
- end
- return object
-end
-
-- find the last substring with the given pattern
function string:find_last(pattern, plain)
@@ -306,7 +198,7 @@ end
--
function string.dump(object, deflate)
deprecated.add("utils.dump() or string.serialize()", "string.dump()")
- return string._makestr(object, deflate, false, 0)
+ return string.serialize(object, deflate)
end
-- serialize to string from the given object
@@ -314,9 +206,10 @@ end
-- @param deflate deflate empty characters
--
-- @return string, errors
---
+--
function string.serialize(object, deflate)
- return string._makestr(object, deflate, true, 0)
+ deflate = not not deflate
+ return serialize.save(object, { strip = deflate, indent = not deflate })
end
-- deserialize string to object
@@ -324,34 +217,9 @@ end
-- @param str the serialized string
--
-- @return object, errors
---
+--
function string:deserialize()
-
- -- load table as script
- local result = nil
- local script, errors = loadstring("return " .. self)
- if script then
-
- -- load object
- local ok, object = pcall(script)
- if ok then
- result = object
- elseif object then
- -- error
- errors = object
- else
- -- error
- errors = string.format("cannot deserialize string: %s", self)
- end
- end
-
- -- load function from string in table
- if result then
- result, errors = string._loadstr(result)
- end
-
- -- ok?
- return result, errors
+ return serialize.load(self)
end
-- return module: string