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 local is_52
= _VERSION
== "Lua 5.2"
24 local defaultOptions
= {
26 -- By default, do not allow undefined calls to be de-serialized as call objects
27 allowUndefined
= false
30 -- No real default-option handling needed...
36 function isPattern(value
)
37 return lpeg
.type(value
) == 'pattern'
40 local metaAdd
= getmetatable(lpeg
.P("")).__add
41 function isPattern(value
)
42 return getmetatable(value
).__add
== metaAdd
46 local function buildDefinedCaptures(argumentCapture
, defs
)
48 if not defs
then return end
49 for name
, func
in pairs(defs
) do
50 if type(name
) ~= 'string' and not isPattern(name
) then
51 error("Invalid functionCalls name: " .. tostring(name
) .. " not a string or LPEG pattern")
53 -- Allow boolean or function to match up w/ encoding permissions
54 if type(func
) ~= 'boolean' and type(func
) ~= 'function' then
55 error("Invalid functionCalls item: " .. name
.. " not a function")
58 if type(name
) == 'string' then
59 nameCallCapture
= lpeg
.P(name
.. "(") * lpeg
.Cc(name
)
61 -- Name matcher expected to produce a capture
62 nameCallCapture
= name
* "("
64 -- Call func over nameCallCapture and value to permit function receiving name
66 -- Process 'func' if it is not a function
67 if type(func
) == 'boolean' then
69 func
= function(name
, ...)
71 error("Function call on '" .. name
.. "' not permitted")
73 return buildCall(name
, ...)
76 local inner_func
= func
78 return (inner_func(...))
81 local newCapture
= (nameCallCapture
* argumentCapture
) / func
* ")"
82 if not callCapture
then
83 callCapture
= newCapture
85 callCapture
= callCapture
+ newCapture
91 local function buildCapture(options
, global_options
, state
)
92 if not options
-- No ops, don't bother to parse
93 or not (options
.defs
and (nil ~= next(options
.defs
)) or options
.allowUndefined
) then
96 -- Allow zero or more arguments separated by commas
97 local value
= lpeg
.V(util
.types
.VALUE
)
99 value
= lpeg
.Cmt(lpeg
.Cp(), function(str
, i
)
100 -- Decode one value then return
101 local END_MARKER
= {}
103 -- Found empty segment
104 #lpeg
.P(')' * lpeg
.Cc(END_MARKER
) * lpeg
.Cp())
105 -- Found a value + captured, check for required , or ) + capture next pos
106 + state
.VALUE_MATCH
* #(lpeg
.P(',') + lpeg
.P(')')) * lpeg
.Cp()
107 local capture
, i
= pattern
:match(str
, i
)
108 if END_MARKER
== capture
then
110 elseif (i
== nil and capture
== nil) then
117 local argumentCapture
= (value
* (lpeg
.P(",") * value
)^
0) + 0
118 local callCapture
= buildDefinedCaptures(argumentCapture
, options
.defs
)
119 if options
.allowUndefined
then
120 local function func(name
, ...)
121 return buildCall(name
, ...)
123 -- Identifier-type-match
124 local nameCallCapture
= lpeg
.C(util
.identifier
) * "("
125 local newCapture
= (nameCallCapture
* argumentCapture
) / func
* ")"
126 if not callCapture
then
127 callCapture
= newCapture
129 callCapture
= callCapture
+ newCapture
135 local function load_types(options
, global_options
, grammar
, state
)
136 local capture
= buildCapture(options
, global_options
, state
)
138 util
.append_grammar_item(grammar
, "VALUE", capture
)
145 load_types
= load_types
149 _G
.json
= _G
.json
or {}
150 _G
.json
.decode
= _G
.json
.decode
151 _G
.json
.decode
.calls
= calls