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
= {
35 -- By default, do not allow undefined calls to be de-serialized as call objects
36 allowUndefined
= false
47 trailingComma
= false,
54 local function BEGIN_ARRAY(state
)
58 local function END_ARRAY(state
)
63 local function BEGIN_OBJECT(state
)
67 local function END_OBJECT(state
)
72 local function END_CALL(state
)
77 local function SET_KEY(state
)
81 local function NEXT_VALUE(state
)
85 local function mergeOptions(options
, mode
)
86 jsonutil
.doOptionMerge(options
, true, 'array', defaultOptions
, mode
and modeOptions
[mode
])
87 jsonutil
.doOptionMerge(options
, true, 'object', defaultOptions
, mode
and modeOptions
[mode
])
88 jsonutil
.doOptionMerge(options
, true, 'calls', defaultOptions
, mode
and modeOptions
[mode
])
94 function isPattern(value
)
95 return lpeg
.type(value
) == 'pattern'
98 local metaAdd
= getmetatable(lpeg
.P("")).__add
99 function isPattern(value
)
100 return getmetatable(value
).__add
== metaAdd
105 local function generateSingleCallLexer(name
, func
)
106 if type(name
) ~= 'string' and not isPattern(name
) then
107 error("Invalid functionCalls name: " .. tostring(name
) .. " not a string or LPEG pattern")
109 -- Allow boolean or function to match up w/ encoding permissions
110 if type(func
) ~= 'boolean' and type(func
) ~= 'function' then
111 error("Invalid functionCalls item: " .. name
.. " not a function")
113 local function buildCallCapture(name
)
114 return function(state
)
115 if func
== false then
116 error("Function call on '" .. name
.. "' not permitted")
119 state
:new_call(name
, func
)
122 local nameCallCapture
123 if type(name
) == 'string' then
124 nameCallCapture
= lpeg
.P(name
.. "(") * lpeg
.Cc(name
) / buildCallCapture
126 -- Name matcher expected to produce a capture
127 nameCallCapture
= name
* "(" / buildCallCapture
129 -- Call func over nameCallCapture and value to permit function receiving name
130 return nameCallCapture
133 local function generateNamedCallLexers(options
)
134 if not options
.calls
or not options
.calls
.defs
then
138 for name
, func
in pairs(options
.calls
.defs
) do
139 local newCapture
= generateSingleCallLexer(name
, func
)
140 if not callCapture
then
141 callCapture
= newCapture
143 callCapture
= callCapture
+ newCapture
149 local function generateCallLexer(options
)
151 local namedCapture
= generateNamedCallLexers(options
)
152 if options
.calls
and options
.calls
.allowUndefined
then
153 lexer
= generateSingleCallLexer(lpeg
.C(util
.identifier
), true)
156 lexer
= lexer
and lexer
+ namedCapture
or namedCapture
159 lexer
= lexer
+ lpeg
.P(")") * lpeg
.Cc(END_CALL
)
164 local function generateLexer(options
)
165 local ignored
= options
.ignored
166 local array_options
, object_options
= options
.array
, options
.object
168 lpeg
.P("[") * lpeg
.Cc(BEGIN_ARRAY
)
169 + lpeg
.P("]") * lpeg
.Cc(END_ARRAY
)
170 + lpeg
.P("{") * lpeg
.Cc(BEGIN_OBJECT
)
171 + lpeg
.P("}") * lpeg
.Cc(END_OBJECT
)
172 + lpeg
.P(":") * lpeg
.Cc(SET_KEY
)
173 + lpeg
.P(",") * lpeg
.Cc(NEXT_VALUE
)
174 if object_options
.identifier
then
175 -- Add identifier match w/ validation check that it is in key
176 lexer
= lexer
+ lpeg
.C(util
.identifier
) * ignored
* lpeg
.P(":") * lpeg
.Cc(SET_KEY
)
178 local callLexers
= generateCallLexer(options
)
180 lexer
= lexer
+ callLexers
186 mergeOptions
= mergeOptions
,
187 generateLexer
= generateLexer