Merge pull request #49 from jokajak/bugfix/lpeg_version_check
[luajson.git] / lua / json / decode / composite.lua
blob8dae0f5eab48b4cbf10fa951d2e9e93b1b1fcce7
1 --[[
2 Licensed according to the included 'LICENSE' document
3 Author: Thomas Harning Jr <harningt@gmail.com>
4 ]]
5 local pairs = pairs
6 local type = type
8 local lpeg = require("lpeg")
10 local util = require("json.decode.util")
11 local jsonutil = require("json.util")
13 local rawset = rawset
15 local assert = assert
16 local tostring = tostring
18 local error = error
19 local getmetatable = getmetatable
21 local _ENV = nil
23 local defaultOptions = {
24 array = {
25 allowEmptyElement = false,
26 ignoreLength = false,
27 trailingComma = true
29 object = {
30 allowEmptyElement = false,
31 trailingComma = true,
32 number = true,
33 identifier = true,
34 setObjectKey = rawset
36 calls = {
37 allowEmptyElement = false,
38 defs = nil,
39 -- By default, do not allow undefined calls to be de-serialized as call objects
40 allowUndefined = false,
41 trailingComma = true
45 local modeOptions = {
46 default = nil,
47 strict = {
48 array = {
49 trailingComma = false
51 object = {
52 trailingComma = false,
53 number = false,
54 identifier = false
59 local function BEGIN_ARRAY(state)
60 state:push()
61 state:new_array()
62 end
63 local function END_ARRAY(state)
64 state:end_array()
65 state:pop()
66 end
68 local function BEGIN_OBJECT(state)
69 state:push()
70 state:new_object()
71 end
72 local function END_OBJECT(state)
73 state:end_object()
74 state:pop()
75 end
77 local function END_CALL(state)
78 state:end_call()
79 state:pop()
80 end
82 local function SET_KEY(state)
83 state:set_key()
84 end
86 local function NEXT_VALUE(state)
87 state:put_value()
88 end
90 local function mergeOptions(options, mode)
91 jsonutil.doOptionMerge(options, true, 'array', defaultOptions, mode and modeOptions[mode])
92 jsonutil.doOptionMerge(options, true, 'object', defaultOptions, mode and modeOptions[mode])
93 jsonutil.doOptionMerge(options, true, 'calls', defaultOptions, mode and modeOptions[mode])
94 end
97 local isPattern
98 if lpeg.type then
99 function isPattern(value)
100 return lpeg.type(value) == 'pattern'
102 else
103 local metaAdd = getmetatable(lpeg.P("")).__add
104 function isPattern(value)
105 return getmetatable(value).__add == metaAdd
110 local function generateSingleCallLexer(name, func)
111 if type(name) ~= 'string' and not isPattern(name) then
112 error("Invalid functionCalls name: " .. tostring(name) .. " not a string or LPEG pattern")
114 -- Allow boolean or function to match up w/ encoding permissions
115 if type(func) ~= 'boolean' and type(func) ~= 'function' then
116 error("Invalid functionCalls item: " .. name .. " not a function")
118 local function buildCallCapture(name)
119 return function(state)
120 if func == false then
121 error("Function call on '" .. name .. "' not permitted")
123 state:push()
124 state:new_call(name, func)
127 local nameCallCapture
128 if type(name) == 'string' then
129 nameCallCapture = lpeg.P(name .. "(") * lpeg.Cc(name) / buildCallCapture
130 else
131 -- Name matcher expected to produce a capture
132 nameCallCapture = name * "(" / buildCallCapture
134 -- Call func over nameCallCapture and value to permit function receiving name
135 return nameCallCapture
138 local function generateNamedCallLexers(options)
139 if not options.calls or not options.calls.defs then
140 return
142 local callCapture
143 for name, func in pairs(options.calls.defs) do
144 local newCapture = generateSingleCallLexer(name, func)
145 if not callCapture then
146 callCapture = newCapture
147 else
148 callCapture = callCapture + newCapture
151 return callCapture
154 local function generateCallLexer(options)
155 local lexer
156 local namedCapture = generateNamedCallLexers(options)
157 if options.calls and options.calls.allowUndefined then
158 lexer = generateSingleCallLexer(lpeg.C(util.identifier), true)
160 if namedCapture then
161 lexer = lexer and lexer + namedCapture or namedCapture
163 if lexer then
164 lexer = lexer + lpeg.P(")") * lpeg.Cc(END_CALL)
166 return lexer
169 local function generateLexer(options)
170 local ignored = options.ignored
171 local array_options, object_options = options.array, options.object
172 local lexer =
173 lpeg.P("[") * lpeg.Cc(BEGIN_ARRAY)
174 + lpeg.P("]") * lpeg.Cc(END_ARRAY)
175 + lpeg.P("{") * lpeg.Cc(BEGIN_OBJECT)
176 + lpeg.P("}") * lpeg.Cc(END_OBJECT)
177 + lpeg.P(":") * lpeg.Cc(SET_KEY)
178 + lpeg.P(",") * lpeg.Cc(NEXT_VALUE)
179 if object_options.identifier then
180 -- Add identifier match w/ validation check that it is in key
181 lexer = lexer + lpeg.C(util.identifier) * ignored * lpeg.P(":") * lpeg.Cc(SET_KEY)
183 local callLexers = generateCallLexer(options)
184 if callLexers then
185 lexer = lexer + callLexers
187 return lexer
190 local composite = {
191 mergeOptions = mergeOptions,
192 generateLexer = generateLexer
195 return composite