blob: 4f9c8a02a66de24c8082970ef5cda9ee01098ed1 [file] [log] [blame]
--[[
Licensed according to the included 'LICENSE' document
Author: Thomas Harning Jr <harningt@gmail.com>
]]
local lpeg = require("lpeg")
local tostring = tostring
local pairs, ipairs = pairs, ipairs
local next, type = next, type
local error = error
local util = require("json.decode.util")
local buildCall = require("json.util").buildCall
local getmetatable = getmetatable
module("json.decode.calls")
local defaultOptions = {
defs = nil,
-- By default, do not allow undefined calls to be de-serialized as call objects
allowUndefined = false
}
-- No real default-option handling needed...
default = nil
strict = nil
local isPattern
if lpeg.type then
function isPattern(value)
return lpeg.type(value) == 'pattern'
end
else
local metaAdd = getmetatable(lpeg.P("")).__add
function isPattern(value)
return getmetatable(value).__add == metaAdd
end
end
local function buildDefinedCaptures(argumentCapture, defs)
local callCapture
if not defs then return end
for name, func in pairs(defs) do
if type(name) ~= 'string' and not isPattern(name) then
error("Invalid functionCalls name: " .. tostring(name) .. " not a string or LPEG pattern")
end
-- Allow boolean or function to match up w/ encoding permissions
if type(func) ~= 'boolean' and type(func) ~= 'function' then
error("Invalid functionCalls item: " .. name .. " not a function")
end
local nameCallCapture
if type(name) == 'string' then
nameCallCapture = lpeg.P(name .. "(") * lpeg.Cc(name)
else
-- Name matcher expected to produce a capture
nameCallCapture = name * "("
end
-- Call func over nameCallCapture and value to permit function receiving name
-- Process 'func' if it is not a function
if type(func) == 'boolean' then
local allowed = func
func = function(name, ...)
if not allowed then
error("Function call on '" .. name .. "' not permitted")
end
return buildCall(name, ...)
end
else
local inner_func = func
func = function(...)
return (inner_func(...))
end
end
local newCapture = (nameCallCapture * argumentCapture) / func * ")"
if not callCapture then
callCapture = newCapture
else
callCapture = callCapture + newCapture
end
end
return callCapture
end
local function buildCapture(options)
if not options -- No ops, don't bother to parse
or not (options.defs and (nil ~= next(options.defs)) or options.allowUndefined) then
return nil
end
-- Allow zero or more arguments separated by commas
local value = lpeg.V(util.types.VALUE)
local argumentCapture = (value * (lpeg.P(",") * value)^0) + 0
local callCapture = buildDefinedCaptures(argumentCapture, options.defs)
if options.allowUndefined then
local function func(name, ...)
return buildCall(name, ...)
end
-- Identifier-type-match
local nameCallCapture = lpeg.C(util.identifier) * "("
local newCapture = (nameCallCapture * argumentCapture) / func * ")"
if not callCapture then
callCapture = newCapture
else
callCapture = callCapture + newCapture
end
end
return callCapture
end
function load_types(options, global_options, grammar)
local capture = buildCapture(options, global_options)
if capture then
util.append_grammar_item(grammar, "VALUE", capture)
end
end