summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--script/json-beautify.lua120
-rw-r--r--script/json.lua141
3 files changed, 206 insertions, 57 deletions
diff --git a/README.md b/README.md
index c41505b2..08ebf87e 100644
--- a/README.md
+++ b/README.md
@@ -103,9 +103,9 @@ Please [help me][en-US] improve the quality of `en-US`.
* [lua.tmbundle](https://github.com/textmate/lua.tmbundle)
* [EmmyLua](https://emmylua.github.io)
* [lua-glob](https://github.com/sumneko/lua-glob)
-* [JSON4Lua](http://github.com/craigmj/json4lua/)
* [utility](https://github.com/sumneko/utility)
* [vscode-lua-doc](https://github.com/actboy168/vscode-lua-doc)
+* [json.lua](https://github.com/actboy168/json.lua)
## Acknowledgement
diff --git a/script/json-beautify.lua b/script/json-beautify.lua
new file mode 100644
index 00000000..1d2a6cc0
--- /dev/null
+++ b/script/json-beautify.lua
@@ -0,0 +1,120 @@
+local json = require "json"
+local type = type
+local next = next
+local error = error
+local table_concat = table.concat
+local table_sort = table.sort
+local string_rep = string.rep
+local math_type = math.type
+local setmetatable = setmetatable
+local getmetatable = getmetatable
+
+local statusMark
+local statusQue
+local statusDep
+local statusOpt
+
+local defaultOpt = {
+ newline = "\n",
+ indent = " ",
+}
+defaultOpt.__index = defaultOpt
+
+local function encode_newline()
+ statusQue[#statusQue+1] = statusOpt.newline..string_rep(statusOpt.indent, statusDep)
+end
+
+local encode_map = {}
+for k ,v in next, json.encode_map do
+ encode_map[k] = v
+end
+
+local encode_string = json.encode_map.string
+
+local function encode(v)
+ local res = encode_map[type(v)](v)
+ statusQue[#statusQue+1] = res
+end
+
+function encode_map.table(t)
+ local first_val = next(t)
+ if first_val == nil then
+ if getmetatable(t) == json.object then
+ return "{}"
+ else
+ return "[]"
+ end
+ end
+ if statusMark[t] then
+ error("circular reference")
+ end
+ statusMark[t] = true
+ if type(first_val) == 'string' then
+ local key = {}
+ for k in next, t do
+ if type(k) ~= "string" then
+ error("invalid table: mixed or invalid key types")
+ end
+ key[#key+1] = k
+ end
+ table_sort(key)
+ statusQue[#statusQue+1] = "{"
+ statusDep = statusDep + 1
+ encode_newline()
+ local k = key[1]
+ statusQue[#statusQue+1] = encode_string(k)
+ statusQue[#statusQue+1] = ": "
+ encode(t[k])
+ for i = 2, #key do
+ local k = key[i]
+ statusQue[#statusQue+1] = ","
+ encode_newline()
+ statusQue[#statusQue+1] = encode_string(k)
+ statusQue[#statusQue+1] = ": "
+ encode(t[k])
+ end
+ statusDep = statusDep - 1
+ encode_newline()
+ statusMark[t] = nil
+ return "}"
+ else
+ local max = 0
+ for k in next, t do
+ if math_type(k) ~= "integer" or k <= 0 then
+ error("invalid table: mixed or invalid key types")
+ end
+ if max < k then
+ max = k
+ end
+ end
+ statusQue[#statusQue+1] = "["
+ statusDep = statusDep + 1
+ encode_newline()
+ encode(t[1])
+ for i = 2, max do
+ statusQue[#statusQue+1] = ","
+ encode_newline()
+ encode(t[i])
+ end
+ statusDep = statusDep - 1
+ encode_newline()
+ statusMark[t] = nil
+ return "]"
+ end
+end
+
+local function beautify(v, option)
+ if type(v) == "string" then
+ v = json.decode(v)
+ end
+ statusMark = {}
+ statusQue = {}
+ statusDep = 0
+ statusOpt = option and setmetatable(option, defaultOpt) or defaultOpt
+ encode(v)
+ return table_concat(statusQue)
+end
+
+json.beautify = beautify
+
+return json
diff --git a/script/json.lua b/script/json.lua
index 6390e5f0..0d3e6b48 100644
--- a/script/json.lua
+++ b/script/json.lua
@@ -1,4 +1,3 @@
-local pairs = pairs
local type = type
local next = next
local error = error
@@ -16,15 +15,17 @@ local string_sub = string.sub
local string_format = string.format
local math_type = math.type
local setmetatable = setmetatable
+local getmetatable = getmetatable
local Inf = math.huge
local json = {}
-json.null = function() end
json.object = {}
-- json.encode --
+local statusMark
+local statusQue
-local encode
+local encode_map = {}
local encode_escape_map = {
[ "\"" ] = "\\\"",
@@ -39,7 +40,7 @@ local encode_escape_map = {
local decode_escape_set = {}
local decode_escape_map = {}
-for k, v in pairs(encode_escape_map) do
+for k, v in next, encode_escape_map do
decode_escape_map[v] = k
decode_escape_set[string_byte(v, 2)] = true
end
@@ -51,20 +52,19 @@ for i = 0, 31 do
end
end
-local function encode_nil()
- return "null"
+local function encode(v)
+ local res = encode_map[type(v)](v)
+ statusQue[#statusQue+1] = res
end
-local function encode_null(val)
- if val == json.null then
- return "null"
- end
- error "cannot serialise function: type not supported"
+encode_map["nil"] = function ()
+ return "null"
end
-local function encode_string(val)
- return '"' .. string_gsub(val, '[\0-\31\\"/]', encode_escape_map) .. '"'
+function encode_map.string(v)
+ return '"' .. string_gsub(v, '[\0-\31\\"/]', encode_escape_map) .. '"'
end
+local encode_string = encode_map.string
local function convertreal(v)
local g = string_format('%.16g', v)
@@ -74,75 +74,97 @@ local function convertreal(v)
return string_format('%.17g', v)
end
-local function encode_number(val)
- if val ~= val or val <= -Inf or val >= Inf then
- error("unexpected number value '" .. tostring(val) .. "'")
+function encode_map.number(v)
+ if v ~= v or v <= -Inf or v >= Inf then
+ error("unexpected number value '" .. tostring(v) .. "'")
end
- return string_gsub(convertreal(val), ',', '.')
+ return string_gsub(convertreal(v), ',', '.')
end
-local function encode_table(val, mark)
- local first_val = next(val)
+function encode_map.boolean(v)
+ if v then
+ return "true"
+ else
+ return "false"
+ end
+end
+
+function encode_map.table(t)
+ local first_val = next(t)
if first_val == nil then
- if getmetatable(val) == json.object then
+ if getmetatable(t) == json.object then
return "{}"
else
return "[]"
end
end
- mark = mark or {}
- if mark[val] then
+ if statusMark[t] then
error("circular reference")
end
- mark[val] = true
- local res = {}
+ statusMark[t] = true
if type(first_val) == 'string' then
local key = {}
- for k in pairs(val) do
+ for k in next, t do
if type(k) ~= "string" then
error("invalid table: mixed or invalid key types")
end
key[#key+1] = k
end
table_sort(key)
- for i = 1, #key do
+ statusQue[#statusQue+1] = "{"
+ local k = key[1]
+ statusQue[#statusQue+1] = encode_string(k)
+ statusQue[#statusQue+1] = ":"
+ encode(t[k])
+ for i = 2, #key do
local k = key[i]
- res[i] = encode_string(k) .. ":" .. encode(val[k], mark)
+ statusQue[#statusQue+1] = ","
+ statusQue[#statusQue+1] = encode_string(k)
+ statusQue[#statusQue+1] = ":"
+ encode(t[k])
end
- mark[val] = nil
- return "{" .. table_concat(res, ",") .. "}"
+ statusMark[t] = nil
+ return "}"
else
local max = 0
- for k in pairs(val) do
- if math_type(k) ~= "integer" then
+ for k in next, t do
+ if math_type(k) ~= "integer" or k <= 0 then
error("invalid table: mixed or invalid key types")
end
- max = max > k and max or k
+ if max < k then
+ max = k
+ end
end
- for i = 1, max do
- res[i] = encode(val[i], mark)
+ statusQue[#statusQue+1] = "["
+ encode(t[1])
+ for i = 2, max do
+ statusQue[#statusQue+1] = ","
+ encode(t[i])
end
- mark[val] = nil
- return "[" .. table_concat(res, ",") .. "]"
+ statusMark[t] = nil
+ return "]"
end
end
-local encode_map = {
- [ "nil" ] = encode_nil,
- [ "table" ] = encode_table,
- [ "string" ] = encode_string,
- [ "number" ] = encode_number,
- [ "boolean" ] = tostring,
- [ "function" ] = encode_null,
- [ "userdata" ] = function () error("unexpected type 'userdata'") end,
- [ "thread" ] = function () error("unexpected type 'thread'") end,
-}
-
-encode = function(val, mark)
- return encode_map[type(val)](val, mark)
+local function encode_unexpected(v)
+ if v == json.null then
+ return "null"
+ else
+ error("unexpected type '"..type(v).."'")
+ end
+end
+encode_map[ "function" ] = encode_unexpected
+encode_map[ "userdata" ] = encode_unexpected
+encode_map[ "thread" ] = encode_unexpected
+
+function json.encode(v)
+ statusMark = {}
+ statusQue = {}
+ encode(v)
+ return table_concat(statusQue)
end
-json.encode = encode
+json.encode_map = encode_map
-- json.decode --
@@ -186,6 +208,14 @@ local function next_byte()
decode_error("unexpected character '<eol>'")
end
+local function expect_byte(c)
+ local _, pos = string_find(statusBuf, c, statusPos)
+ if not pos then
+ decode_error(string_format("expected '%s'", string_sub(c, #c)))
+ end
+ statusPos = pos
+end
+
local function decode_unicode_surrogate(s1, s2)
return utf8_char(0x10000 + (tonumber(s1, 16) - 0xd800) * 0x400 + (tonumber(s2, 16) - 0xdc00))
end
@@ -375,13 +405,9 @@ local function decode_item()
if statusAry[top] then
ref[#ref+1] = decode()
else
- if next_byte() ~= 34 --[[ '"' ]] then
- decode_error "expected string for key"
- end
+ expect_byte '^[ \t\r\n]*"'
local key = decode_string()
- if next_byte() ~= 58 --[[ ":" ]] then
- decode_error "expected ':' after key"
- end
+ expect_byte '^[ \t\r\n]*:'
statusPos = statusPos + 1
ref[key] = decode()
end
@@ -418,4 +444,7 @@ function json.decode(str)
return res
end
+-- Generate a lightuserdata
+json.null = debug.upvalueid(decode, 1)
+
return json