diff options
| author | ruki <[email protected]> | 2023-01-19 16:54:59 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-01-19 16:54:59 +0800 |
| commit | 6aa98ec8055538a5ec3742d0faafa47938fefd80 (patch) | |
| tree | d9280d1e9e3e2f171559607a9458a7c9ace17a73 /xmake | |
| parent | 17978855ebb374e21593129b52d3266db7715eb5 (diff) | |
| parent | ed38818a56f40b2fbd51ea10957c1cd6ed0ced41 (diff) | |
Merge pull request #3305 from xmake-io/json
Improve json
Diffstat (limited to 'xmake')
| -rw-r--r-- | xmake/core/base/json.lua | 213 |
1 files changed, 209 insertions, 4 deletions
diff --git a/xmake/core/base/json.lua b/xmake/core/base/json.lua index e83f6b320..4c619e69a 100644 --- a/xmake/core/base/json.lua +++ b/xmake/core/base/json.lua @@ -20,14 +20,219 @@ -- define module: json local json = json or {} -local cjson = cjson or {} -- load modules local io = require("base/io") +local os = require("base/os") local utils = require("base/utils") -- export null -json.null = cjson.null +if cjson then + json.null = cjson.null +else + json.null = {} + setmetatable(json.null, { + __is_json_null = true, + __eq = function (obj) + if type(obj) == "table" then + local mt = getmetatable(obj) + if mt and mt.__is_json_null then + return true + end + end + return false + end, + __tostring = function() + return "null" + end}) +end + +function json._pure_kind_of(obj) + if type(obj) ~= "table" then + return type(obj) + end + if json.is_marked_as_array(obj) then + return "array" + end + if obj == json.null then + return "nil" + end + local i = 1 + for _ in pairs(obj) do + if obj[i] ~= nil then + i = i + 1 + else + return "table" + end + end + if i == 1 then + return "table" + else + return "array" + end +end + +function json._pure_escape_str(s) + local in_char = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'} + local out_char = {'\\', '"', '/', 'b', 'f', 'n', 'r', 't'} + for i, c in ipairs(in_char) do + s = s:gsub(c, '\\' .. out_char[i]) + end + return s +end + +function json._pure_skip_delim(str, pos, delim, err_if_missing) + pos = pos + #str:match('^%s*', pos) + if str:sub(pos, pos) ~= delim then + if err_if_missing then + os.raise("expected %s near position %d", delim, pos) + end + return pos, false + end + return pos + 1, true +end + +function json._pure_parse_str_val(str, pos, val) + val = val or '' + local early_end_error = "end of input found while parsing string." + if pos > #str then + os.raise(early_end_error) + end + local c = str:sub(pos, pos) + if c == '"' then + return val, pos + 1 + end + if c ~= '\\' then + return json._pure_parse_str_val(str, pos + 1, val .. c) + end + + -- we must have a \ character. + local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'} + local nextc = str:sub(pos + 1, pos + 1) + if not nextc then + os.raise(early_end_error) + end + return json._pure_parse_str_val(str, pos + 2, val .. (esc_map[nextc] or nextc)) +end + +function json._pure_parse_num_val(str, pos) + local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos) + local val = tonumber(num_str) + if not val then + os.raise("error parsing number at position %d", pos) + end + return val, pos + #num_str +end + +function json._pure_stringify(obj, as_key) + local s = {} + local kind = json._pure_kind_of(obj) + if kind == "array" then + if as_key then + os.raise("can\'t encode array as key.") + end + s[#s + 1] = '[' + for i, val in ipairs(obj) do + if i > 1 then s[#s + 1] = ',' end + s[#s + 1] = json._pure_stringify(val) + end + s[#s + 1] = ']' + elseif kind == "table" then + if as_key then + os.raise("can\'t encode table as key.") + end + s[#s + 1] = '{' + for k, v in pairs(obj) do + if #s > 1 then s[#s + 1] = ',' end + s[#s + 1] = json._pure_stringify(k, true) + s[#s + 1] = ':' + s[#s + 1] = json._pure_stringify(v) + end + s[#s + 1] = '}' + elseif kind == "string" then + return '"' .. json._pure_escape_str(obj) .. '"' + elseif kind == "number" then + if as_key then + return '"' .. tostring(obj) .. '"' + end + return tostring(obj) + elseif kind == "boolean" then + return tostring(obj) + elseif kind == "nil" then + return "null" + else + os.raise("unknown type: %s", kind) + end + return table.concat(s) +end + +function json._pure_parse(str, pos, end_delim) + pos = pos or 1 + if pos > #str then + os.raise("reached unexpected end of input.") + end + -- skip whitespace. + local pos = pos + #str:match('^%s*', pos) + local first = str:sub(pos, pos) + if first == '{' then + local obj, key, delim_found = {}, true, true + pos = pos + 1 + while true do + key, pos = json._pure_parse(str, pos, '}') + if key == nil then + return obj, pos + end + if not delim_found then + os.raise("comma missing between object items.") + end + pos = json._pure_skip_delim(str, pos, ':', true) -- true -> error if missing. + obj[key], pos = json._pure_parse(str, pos) + pos, delim_found = json._pure_skip_delim(str, pos, ',') + end + elseif first == '[' then + local arr, val, delim_found = {}, true, true + json.mark_as_array(arr) + pos = pos + 1 + while true do + val, pos = json._pure_parse(str, pos, ']') + if val == nil then + return arr, pos + end + if not delim_found then + os.raise("comma missing between array items.") + end + arr[#arr + 1] = val + pos, delim_found = json._pure_skip_delim(str, pos, ',') + end + elseif first == '"' then + return json._pure_parse_str_val(str, pos + 1) + elseif first == '-' or first:match("%d") then + return json._pure_parse_num_val(str, pos) + elseif first == end_delim then + -- end of an object or array. + return nil, pos + 1 + else + local literals = {["true"] = true, ["false"] = false, ["null"] = json.null} + for lit_str, lit_val in pairs(literals) do + local lit_end = pos + #lit_str - 1 + if str:sub(pos, lit_end) == lit_str then + return lit_val, lit_end + 1 + end + end + local pos_info_str = "position " .. pos .. ": " .. str:sub(pos, pos + 10) + os.raise("invalid json syntax starting at " .. pos_info_str) + end +end + +-- decode json string using pure lua +function json._pure_decode(jsonstr, opt) + return json._pure_parse(jsonstr) +end + +-- encode json string using pua lua +function json._pure_encode(luatable, opt) + return json._pure_stringify(luatable) +end -- support empty array -- @see https://github.com/mpx/lua-cjson/issues/11 @@ -51,7 +256,7 @@ end -- @return the lua table -- function json.decode(jsonstr, opt) - local ok, luatable_or_errors = utils.trycall(cjson.decode, nil, jsonstr) + local ok, luatable_or_errors = utils.trycall(cjson and cjson.decode or json._pure_decode, nil, jsonstr) if not ok then return nil, string.format("decode json failed, %s", luatable_or_errors) end @@ -66,7 +271,7 @@ end -- @return the json string -- function json.encode(luatable, opt) - local ok, jsonstr_or_errors = utils.trycall(cjson.encode, nil, luatable) + local ok, jsonstr_or_errors = utils.trycall(cjson and cjson.encode or json._pure_encode, nil, luatable) if not ok then return nil, string.format("encode json failed, %s", jsonstr_or_errors) end |
