all: sets _ENV to nil using local _ENV = nil to avoid global writing
[luajson.git] / lua / json / decode / composite.lua
blobcd9c2896f9234f0bb5abc9d81479bdfe97ce0d5c
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 trailingComma = true
27 object = {
28 trailingComma = true,
29 number = true,
30 identifier = true,
31 setObjectKey = rawset
33 calls = {
34 defs = nil,
35 -- By default, do not allow undefined calls to be de-serialized as call objects
36 allowUndefined = false
40 local modeOptions = {
41 default = nil,
42 strict = {
43 array = {
44 trailingComma = false
46 object = {
47 trailingComma = false,
48 number = false,
49 identifier = false
54 local function BEGIN_ARRAY(state)
55 state:push()
56 state:new_array()
57 end
58 local function END_ARRAY(state)
59 state:end_array()
60 state:pop()
61 end
63 local function BEGIN_OBJECT(state)
64 state:push()
65 state:new_object()
66 end
67 local function END_OBJECT(state)
68 state:end_object()
69 state:pop()
70 end
72 local function END_CALL(state)
73 state:end_call()
74 state:pop()
75 end
77 local function SET_KEY(state)
78 state:set_key()
79 end
81 local function NEXT_VALUE(state)
82 state:put_value()
83 end
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])
89 end
92 local isPattern
93 if lpeg.type then
94 function isPattern(value)
95 return lpeg.type(value) == 'pattern'
96 end
97 else
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")
118 state:push()
119 state:new_call(name, func)
122 local nameCallCapture
123 if type(name) == 'string' then
124 nameCallCapture = lpeg.P(name .. "(") * lpeg.Cc(name) / buildCallCapture
125 else
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
135 return
137 local callCapture
138 for name, func in pairs(options.calls.defs) do
139 local newCapture = generateSingleCallLexer(name, func)
140 if not callCapture then
141 callCapture = newCapture
142 else
143 callCapture = callCapture + newCapture
146 return callCapture
149 local function generateCallLexer(options)
150 local lexer
151 local namedCapture = generateNamedCallLexers(options)
152 if options.calls and options.calls.allowUndefined then
153 lexer = generateSingleCallLexer(lpeg.C(util.identifier), true)
155 if namedCapture then
156 lexer = lexer and lexer + namedCapture or namedCapture
158 if lexer then
159 lexer = lexer + lpeg.P(")") * lpeg.Cc(END_CALL)
161 return lexer
164 local function generateLexer(options)
165 local ignored = options.ignored
166 local array_options, object_options = options.array, options.object
167 local lexer =
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)
179 if callLexers then
180 lexer = lexer + callLexers
182 return lexer
185 local composite = {
186 mergeOptions = mergeOptions,
187 generateLexer = generateLexer
190 return composite