diff options
-rw-r--r-- | script/json-beautify.lua | 17 | ||||
-rw-r--r-- | script/json.lua | 178 |
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()]() |