2 Licensed according to the included 'LICENSE' document
3 Author: Thomas Harning Jr <harningt@gmail.com>
5 local lpeg
= require("lpeg")
6 local tostring = tostring
7 local pairs
, ipairs
= pairs
, ipairs
8 local next, type = next, type
11 local util
= require("json.decode.util")
13 local buildCall
= require("json.util").buildCall
15 local getmetatable
= getmetatable
17 module("json.decode.calls")
19 local defaultOptions
= {
21 -- By default, do not allow undefined calls to be de-serialized as call objects
22 allowUndefined
= false
25 -- No real default-option handling needed...
31 function isPattern(value
)
32 return lpeg
.type(value
) == 'pattern'
35 local metaAdd
= getmetatable(lpeg
.P("")).__add
36 function isPattern(value
)
37 return getmetatable(value
).__add
== metaAdd
41 local function buildDefinedCaptures(argumentCapture
, defs
)
43 if not defs
then return end
44 for name
, func
in pairs(defs
) do
45 if type(name
) ~= 'string' and not isPattern(name
) then
46 error("Invalid functionCalls name: " .. tostring(name
) .. " not a string or LPEG pattern")
48 -- Allow boolean or function to match up w/ encoding permissions
49 if type(func
) ~= 'boolean' and type(func
) ~= 'function' then
50 error("Invalid functionCalls item: " .. name
.. " not a function")
53 if type(name
) == 'string' then
54 nameCallCapture
= lpeg
.P(name
.. "(") * lpeg
.Cc(name
)
56 -- Name matcher expected to produce a capture
57 nameCallCapture
= name
* "("
59 -- Call func over nameCallCapture and value to permit function receiving name
61 -- Process 'func' if it is not a function
62 if type(func
) == 'boolean' then
64 func
= function(name
, ...)
66 error("Function call on '" .. name
.. "' not permitted")
68 return buildCall(name
, ...)
71 local newCapture
= (nameCallCapture
* argumentCapture
) / func
* ")"
72 if not callCapture
then
73 callCapture
= newCapture
75 callCapture
= callCapture
+ newCapture
81 local function buildCapture(options
, global_options
, state
)
82 if not options
-- No ops, don't bother to parse
83 or not (options
.defs
and (nil ~= next(options
.defs
)) or options
.allowUndefined
) then
86 -- Allow zero or more arguments separated by commas
87 local value
= lpeg
.V(util
.types
.VALUE
)
89 value
= lpeg
.Cmt(lpeg
.Cp(), function(str
, i
)
90 -- Decode one value then return
93 -- Found empty segment
94 #lpeg
.P(')' * lpeg
.Cc(END_MARKER
) * lpeg
.Cp())
95 -- Found a value + captured, check for required , or ) + capture next pos
96 + state
.VALUE_MATCH
* #(lpeg
.P(',') + lpeg
.P(')')) * lpeg
.Cp()
97 local capture
, i
= pattern
:match(str
, i
)
98 if END_MARKER
== capture
then
100 elseif (i
== nil and capture
== nil) then
107 local argumentCapture
= (value
* (lpeg
.P(",") * value
)^
0) + 0
108 local callCapture
= buildDefinedCaptures(argumentCapture
, options
.defs
)
109 if options
.allowUndefined
then
110 local function func(name
, ...)
111 return buildCall(name
, ...)
113 -- Identifier-type-match
114 local nameCallCapture
= lpeg
.C(util
.identifier
) * "("
115 local newCapture
= (nameCallCapture
* argumentCapture
) / func
* ")"
116 if not callCapture
then
117 callCapture
= newCapture
119 callCapture
= callCapture
+ newCapture
125 function load_types(options
, global_options
, grammar
, state
)
126 local capture
= buildCapture(options
, global_options
, state
)
128 util
.append_grammar_item(grammar
, "VALUE", capture
)