85 lines
2.3 KiB
Lua
85 lines
2.3 KiB
Lua
--[[
|
|
Licensed according to the included 'LICENSE' document
|
|
Author: Thomas Harning Jr <harningt@gmail.com>
|
|
]]
|
|
local lpeg = require("lpeg")
|
|
|
|
local util = require("json.decode.util")
|
|
local jsonutil = require("json.util")
|
|
|
|
local table_maxn = require("table").maxn
|
|
|
|
local unpack = unpack
|
|
|
|
module("json.decode.array")
|
|
|
|
-- Utility function to help manage slighly sparse arrays
|
|
local function processArray(array)
|
|
local max_n = table_maxn(array)
|
|
-- Only populate 'n' if it is necessary
|
|
if #array ~= max_n then
|
|
array.n = max_n
|
|
end
|
|
if jsonutil.InitArray then
|
|
array = jsonutil.InitArray(array) or array
|
|
end
|
|
return array
|
|
end
|
|
|
|
local defaultOptions = {
|
|
trailingComma = true
|
|
}
|
|
|
|
default = nil -- Let the buildCapture optimization take place
|
|
strict = {
|
|
trailingComma = false
|
|
}
|
|
|
|
local function buildCapture(options, global_options, state)
|
|
local ignored = global_options.ignored
|
|
-- arrayItem == element
|
|
local arrayItem = lpeg.V(util.types.VALUE)
|
|
-- If match-time capture supported, use it to remove stack limit for JSON
|
|
if lpeg.Cmt then
|
|
arrayItem = lpeg.Cmt(lpeg.Cp(), function(str, i)
|
|
-- Decode one value then return
|
|
local END_MARKER = {}
|
|
local pattern =
|
|
-- Found empty segment
|
|
#lpeg.P(']' * lpeg.Cc(END_MARKER) * lpeg.Cp())
|
|
-- Found a value + captured, check for required , or ] + capture next pos
|
|
+ state.VALUE_MATCH * #(lpeg.P(',') + lpeg.P(']')) * lpeg.Cp()
|
|
local capture, i = pattern:match(str, i)
|
|
if END_MARKER == capture then
|
|
return i
|
|
elseif (i == nil and capture == nil) then
|
|
return false
|
|
else
|
|
return i, capture
|
|
end
|
|
end)
|
|
end
|
|
local arrayElements = lpeg.Ct(arrayItem * (ignored * lpeg.P(',') * ignored * arrayItem)^0 + 0) / processArray
|
|
|
|
options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions
|
|
local capture = lpeg.P("[")
|
|
capture = capture * ignored
|
|
* arrayElements * ignored
|
|
if options.trailingComma then
|
|
capture = capture * (lpeg.P(",") + 0) * ignored
|
|
end
|
|
capture = capture * lpeg.P("]")
|
|
return capture
|
|
end
|
|
|
|
function register_types()
|
|
util.register_type("ARRAY")
|
|
end
|
|
|
|
function load_types(options, global_options, grammar, state)
|
|
local capture = buildCapture(options, global_options, state)
|
|
local array_id = util.types.ARRAY
|
|
grammar[array_id] = capture
|
|
util.append_grammar_item(grammar, "VALUE", lpeg.V(array_id))
|
|
end
|