2 Licensed according to the included 'LICENSE' document
3 Author: Thomas Harning Jr <harningt@gmail.com>
8 local lpeg
= require("lpeg")
10 local util
= require("json.decode.util")
11 local jsonutil
= require("json.util")
16 local tostring = tostring
19 local getmetatable
= getmetatable
23 local defaultOptions
= {
25 allowEmptyElement
= false,
29 allowEmptyElement
= false,
36 allowEmptyElement
= false,
38 -- By default, do not allow undefined calls to be de-serialized as call objects
39 allowUndefined
= false,
51 trailingComma
= false,
58 local function BEGIN_ARRAY(state
)
62 local function END_ARRAY(state
)
67 local function BEGIN_OBJECT(state
)
71 local function END_OBJECT(state
)
76 local function END_CALL(state
)
81 local function SET_KEY(state
)
85 local function NEXT_VALUE(state
)
89 local function mergeOptions(options
, mode
)
90 jsonutil
.doOptionMerge(options
, true, 'array', defaultOptions
, mode
and modeOptions
[mode
])
91 jsonutil
.doOptionMerge(options
, true, 'object', defaultOptions
, mode
and modeOptions
[mode
])
92 jsonutil
.doOptionMerge(options
, true, 'calls', defaultOptions
, mode
and modeOptions
[mode
])
98 function isPattern(value
)
99 return lpeg
.type(value
) == 'pattern'
102 local metaAdd
= getmetatable(lpeg
.P("")).__add
103 function isPattern(value
)
104 return getmetatable(value
).__add
== metaAdd
109 local function generateSingleCallLexer(name
, func
)
110 if type(name
) ~= 'string' and not isPattern(name
) then
111 error("Invalid functionCalls name: " .. tostring(name
) .. " not a string or LPEG pattern")
113 -- Allow boolean or function to match up w/ encoding permissions
114 if type(func
) ~= 'boolean' and type(func
) ~= 'function' then
115 error("Invalid functionCalls item: " .. name
.. " not a function")
117 local function buildCallCapture(name
)
118 return function(state
)
119 if func
== false then
120 error("Function call on '" .. name
.. "' not permitted")
123 state
:new_call(name
, func
)
126 local nameCallCapture
127 if type(name
) == 'string' then
128 nameCallCapture
= lpeg
.P(name
.. "(") * lpeg
.Cc(name
) / buildCallCapture
130 -- Name matcher expected to produce a capture
131 nameCallCapture
= name
* "(" / buildCallCapture
133 -- Call func over nameCallCapture and value to permit function receiving name
134 return nameCallCapture
137 local function generateNamedCallLexers(options
)
138 if not options
.calls
or not options
.calls
.defs
then
142 for name
, func
in pairs(options
.calls
.defs
) do
143 local newCapture
= generateSingleCallLexer(name
, func
)
144 if not callCapture
then
145 callCapture
= newCapture
147 callCapture
= callCapture
+ newCapture
153 local function generateCallLexer(options
)
155 local namedCapture
= generateNamedCallLexers(options
)
156 if options
.calls
and options
.calls
.allowUndefined
then
157 lexer
= generateSingleCallLexer(lpeg
.C(util
.identifier
), true)
160 lexer
= lexer
and lexer
+ namedCapture
or namedCapture
163 lexer
= lexer
+ lpeg
.P(")") * lpeg
.Cc(END_CALL
)
168 local function generateLexer(options
)
169 local ignored
= options
.ignored
170 local array_options
, object_options
= options
.array
, options
.object
172 lpeg
.P("[") * lpeg
.Cc(BEGIN_ARRAY
)
173 + lpeg
.P("]") * lpeg
.Cc(END_ARRAY
)
174 + lpeg
.P("{") * lpeg
.Cc(BEGIN_OBJECT
)
175 + lpeg
.P("}") * lpeg
.Cc(END_OBJECT
)
176 + lpeg
.P(":") * lpeg
.Cc(SET_KEY
)
177 + lpeg
.P(",") * lpeg
.Cc(NEXT_VALUE
)
178 if object_options
.identifier
then
179 -- Add identifier match w/ validation check that it is in key
180 lexer
= lexer
+ lpeg
.C(util
.identifier
) * ignored
* lpeg
.P(":") * lpeg
.Cc(SET_KEY
)
182 local callLexers
= generateCallLexer(options
)
184 lexer
= lexer
+ callLexers
190 mergeOptions
= mergeOptions
,
191 generateLexer
= generateLexer