summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--script/json-beautify.lua17
-rw-r--r--script/json.lua178
2 files changed, 115 insertions, 80 deletions
diff --git a/script/json-beautify.lua b/script/json-beautify.lua
index 1d2a6cc0..ede0e75b 100644
--- a/script/json-beautify.lua
+++ b/script/json-beautify.lua
@@ -25,17 +25,22 @@ local function encode_newline()
end
local encode_map = {}
-for k ,v in next, json.encode_map do
+local encode_string = json._encode_string
+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.string(v)
+ statusQue[#statusQue+1] = '"'
+ statusQue[#statusQue+1] = encode_string(v)
+ return '"'
+end
+
function encode_map.table(t)
local first_val = next(t)
if first_val == nil then
@@ -62,15 +67,17 @@ function encode_map.table(t)
statusDep = statusDep + 1
encode_newline()
local k = key[1]
+ statusQue[#statusQue+1] = '"'
statusQue[#statusQue+1] = encode_string(k)
- statusQue[#statusQue+1] = ": "
+ statusQue[#statusQue+1] = '": '
encode(t[k])
for i = 2, #key do
local k = key[i]
statusQue[#statusQue+1] = ","
encode_newline()
+ statusQue[#statusQue+1] = '"'
statusQue[#statusQue+1] = encode_string(k)
- statusQue[#statusQue+1] = ": "
+ statusQue[#statusQue+1] = '": '
encode(t[k])
end
statusDep = statusDep - 1
diff --git a/script/json.lua b/script/json.lua
index 46261d7d..39d06ab9 100644
--- a/script/json.lua
+++ b/script/json.lua
@@ -16,14 +16,17 @@ local string_format = string.format
local math_type = math.type
local setmetatable = setmetatable
local getmetatable = getmetatable
-local Inf = math.huge
+local huge = math.huge
+local tiny = -huge
local json = {}
json.object = {}
+json.supportSparseArray = true
+
-- json.encode --
-local statusMark
-local statusQue
+local statusVisited
+local statusBuilder
local encode_map = {}
@@ -54,17 +57,22 @@ end
local function encode(v)
local res = encode_map[type(v)](v)
- statusQue[#statusQue+1] = res
+ statusBuilder[#statusBuilder+1] = res
end
encode_map["nil"] = function ()
return "null"
end
+local function encode_string(v)
+ return string_gsub(v, '[\0-\31\\"]', encode_escape_map)
+end
+
function encode_map.string(v)
- return '"' .. string_gsub(v, '[\0-\31\\"]', encode_escape_map) .. '"'
+ statusBuilder[#statusBuilder+1] = '"'
+ statusBuilder[#statusBuilder+1] = encode_string(v)
+ return '"'
end
-local encode_string = encode_map.string
local function convertreal(v)
local g = string_format('%.16g', v)
@@ -74,11 +82,18 @@ local function convertreal(v)
return string_format('%.17g', v)
end
+if string_match(tostring(1/2), "%p") == "," then
+ local _convertreal = convertreal
+ function convertreal(v)
+ return string_gsub(_convertreal(v), ',', '.')
+ end
+end
+
function encode_map.number(v)
- if v ~= v or v <= -Inf or v >= Inf then
+ if v ~= v or v <= tiny or v >= huge then
error("unexpected number value '" .. tostring(v) .. "'")
end
- return string_gsub(convertreal(v), ',', '.')
+ return convertreal(v)
end
function encode_map.boolean(v)
@@ -98,50 +113,55 @@ function encode_map.table(t)
return "[]"
end
end
- if statusMark[t] then
+ if statusVisited[t] then
error("circular reference")
end
- statusMark[t] = true
+ statusVisited[t] = true
if type(first_val) == 'string' then
- local key = {}
+ local keys = {}
for k in next, t do
if type(k) ~= "string" then
error("invalid table: mixed or invalid key types")
end
- key[#key+1] = k
+ keys[#keys+1] = k
end
- table_sort(key)
- statusQue[#statusQue+1] = "{"
- local k = key[1]
- statusQue[#statusQue+1] = encode_string(k)
- statusQue[#statusQue+1] = ":"
+ table_sort(keys)
+ local k = keys[1]
+ statusBuilder[#statusBuilder+1] = '{"'
+ statusBuilder[#statusBuilder+1] = encode_string(k)
+ statusBuilder[#statusBuilder+1] = '":'
encode(t[k])
- for i = 2, #key do
- local k = key[i]
- statusQue[#statusQue+1] = ","
- statusQue[#statusQue+1] = encode_string(k)
- statusQue[#statusQue+1] = ":"
+ for i = 2, #keys do
+ local k = keys[i]
+ statusBuilder[#statusBuilder+1] = ',"'
+ statusBuilder[#statusBuilder+1] = encode_string(k)
+ statusBuilder[#statusBuilder+1] = '":'
encode(t[k])
end
- statusMark[t] = nil
+ statusVisited[t] = nil
return "}"
else
+ local count = 0
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
+ count = count + 1
if max < k then
max = k
end
end
- statusQue[#statusQue+1] = "["
+ if not json.supportSparseArray and count ~= max then
+ error("sparse array are not supported")
+ end
+ statusBuilder[#statusBuilder+1] = "["
encode(t[1])
for i = 2, max do
- statusQue[#statusQue+1] = ","
+ statusBuilder[#statusBuilder+1] = ","
encode(t[i])
end
- statusMark[t] = nil
+ statusVisited[t] = nil
return "]"
end
end
@@ -158,13 +178,14 @@ encode_map[ "userdata" ] = encode_unexpected
encode_map[ "thread" ] = encode_unexpected
function json.encode(v)
- statusMark = {}
- statusQue = {}
+ statusVisited = {}
+ statusBuilder = {}
encode(v)
- return table_concat(statusQue)
+ return table_concat(statusBuilder)
end
-json.encode_map = encode_map
+json._encode_map = encode_map
+json._encode_string = encode_string
-- json.decode --
@@ -200,12 +221,20 @@ local function get_word()
end
local function next_byte()
- statusPos = string_find(statusBuf, "[^ \t\r\n]", statusPos)
- if statusPos then
- return string_byte(statusBuf, statusPos)
+ local pos = string_find(statusBuf, "[^ \t\r\n]", statusPos)
+ if pos then
+ statusPos = pos
+ return string_byte(statusBuf, pos)
+ end
+ return -1
+end
+
+local function consume_byte(c)
+ local _, pos = string_find(statusBuf, c, statusPos)
+ if pos then
+ statusPos = pos + 1
+ return true
end
- statusPos = #statusBuf + 1
- decode_error("unexpected character '<eol>'")
end
local function expect_byte(c)
@@ -272,49 +301,46 @@ local function decode_string()
end
local function decode_number()
- local word = get_word()
- if not (
- string_match(word, '^.[0-9]*$')
- or string_match(word, '^.[0-9]*%.[0-9]+$')
- or string_match(word, '^.[0-9]*[Ee][+-]?[0-9]+$')
- or string_match(word, '^.[0-9]*%.[0-9]+[Ee][+-]?[0-9]+$')
- ) then
- decode_error("invalid number '" .. word .. "'")
+ local num, c = string_match(statusBuf, '^([0-9]+%.?[0-9]*)([eE]?)', statusPos)
+ if not num or string_byte(num, -1) == 0x2E --[[ "." ]] then
+ decode_error("invalid number '" .. get_word() .. "'")
+ end
+ if c ~= '' then
+ num = string_match(statusBuf, '^([^eE]*[eE][-+]?[0-9]+)[ \t\r\n%]},]', statusPos)
+ if not num then
+ decode_error("invalid number '" .. get_word() .. "'")
+ end
end
- statusPos = statusPos + #word
- return tonumber(word)
+ statusPos = statusPos + #num
+ return tonumber(num)
end
-local function decode_number_negative()
- local word = get_word()
- if not (
- string_match(word, '^.[1-9][0-9]*$')
- or string_match(word, '^.[1-9][0-9]*%.[0-9]+$')
- or string_match(word, '^.[1-9][0-9]*[Ee][+-]?[0-9]+$')
- or string_match(word, '^.[1-9][0-9]*%.[0-9]+[Ee][+-]?[0-9]+$')
- or word == "-0"
- or string_match(word, '^.0%.[0-9]+$')
- or string_match(word, '^.0[Ee][+-]?[0-9]+$')
- or string_match(word, '^.0%.[0-9]+[Ee][+-]?[0-9]+$')
- ) then
- decode_error("invalid number '" .. word .. "'")
+local function decode_number_zero()
+ local num, c = string_match(statusBuf, '^(.%.?[0-9]*)([eE]?)', statusPos)
+ if not num or string_byte(num, -1) == 0x2E --[[ "." ]] or string_match(statusBuf, '^.[0-9]+', statusPos) then
+ decode_error("invalid number '" .. get_word() .. "'")
end
- statusPos = statusPos + #word
- return tonumber(word)
+ if c ~= '' then
+ num = string_match(statusBuf, '^([^eE]*[eE][-+]?[0-9]+)[ \t\r\n%]},]', statusPos)
+ if not num then
+ decode_error("invalid number '" .. get_word() .. "'")
+ end
+ end
+ statusPos = statusPos + #num
+ return tonumber(num)
end
-local function decode_number_zero()
- local word = get_word()
- if not (
- #word == 1
- or string_match(word, '^.%.[0-9]+$')
- or string_match(word, '^.[Ee][+-]?[0-9]+$')
- or string_match(word, '^.%.[0-9]+[Ee][+-]?[0-9]+$')
- ) then
- decode_error("invalid number '" .. word .. "'")
+local function decode_number_negative()
+ statusPos = statusPos + 1
+ local c = string_byte(statusBuf, statusPos)
+ if c then
+ if c == 0x30 then
+ return -decode_number_zero()
+ elseif c > 0x30 and c < 0x3A then
+ return -decode_number()
+ end
end
- statusPos = statusPos + #word
- return tonumber(word)
+ decode_error("invalid number '" .. get_word() .. "'")
end
local function decode_true()
@@ -344,8 +370,7 @@ end
local function decode_array()
statusPos = statusPos + 1
local res = {}
- if next_byte() == 93 --[[ "]" ]] then
- statusPos = statusPos + 1
+ if consume_byte "^[ \t\r\n]*%]" then
return res
end
statusTop = statusTop + 1
@@ -357,8 +382,7 @@ end
local function decode_object()
statusPos = statusPos + 1
local res = {}
- if next_byte() == 125 --[[ "}" ]] then
- statusPos = statusPos + 1
+ if consume_byte "^[ \t\r\n]*}" then
return setmetatable(res, json.object)
end
statusTop = statusTop + 1
@@ -389,11 +413,15 @@ local decode_uncompleted_map = {
local function unexpected_character()
decode_error("unexpected character '" .. string_sub(statusBuf, statusPos, statusPos) .. "'")
end
+local function unexpected_eol()
+ decode_error("unexpected character '<eol>'")
+end
local decode_map = {}
for i = 0, 255 do
decode_map[i] = decode_uncompleted_map[i] or unexpected_character
end
+decode_map[-1] = unexpected_eol
local function decode()
return decode_map[next_byte()]()