blob: 3086366fa20cc4b4bac518fd05737f2419451977 [file] [log] [blame] [edit]
--[[
Licensed according to the included 'LICENSE' document
Author: Thomas Harning Jr <harningt@gmail.com>
]]
local lpeg = require("lpeg")
local error = error
local object = require("json.decode.object")
local array = require("json.decode.array")
local merge = require("json.util").merge
local util = require("json.decode.util")
local setmetatable, getmetatable = setmetatable, getmetatable
local assert = assert
local ipairs, pairs = ipairs, pairs
local string_char = require("string").char
local require = require
module("json.decode")
local modulesToLoad = {
"array",
"object",
"strings",
"number",
"calls",
"others"
}
local loadedModules = {
}
default = {
unicodeWhitespace = true,
initialObject = false
}
local modes_defined = { "default", "strict", "simple" }
simple = {}
strict = {
unicodeWhitespace = true,
initialObject = true
}
-- Register generic value type
util.register_type("VALUE")
for _,name in ipairs(modulesToLoad) do
local mod = require("json.decode." .. name)
for _, mode in pairs(modes_defined) do
if mod[mode] then
_M[mode][name] = mod[mode]
end
end
loadedModules[name] = mod
-- Register types
if mod.register_types then
mod.register_types()
end
end
-- Shift over default into defaultOptions to permit build optimization
local defaultOptions = default
default = nil
local function buildDecoder(mode)
mode = mode and merge({}, defaultOptions, mode) or defaultOptions
local ignored = mode.unicodeWhitespace and util.unicode_ignored or util.ascii_ignored
-- Store 'ignored' in the global options table
mode.ignored = ignored
local value_id = util.types.VALUE
local value_type = lpeg.V(value_id)
local object_type = lpeg.V(util.types.OBJECT)
local array_type = lpeg.V(util.types.ARRAY)
local grammar = {
[1] = mode.initialObject and (ignored * (object_type + array_type)) or value_type
}
for _, name in pairs(modulesToLoad) do
local mod = loadedModules[name]
mod.load_types(mode[name], mode, grammar)
end
-- HOOK VALUE TYPE WITH WHITESPACE
grammar[value_id] = ignored * grammar[value_id] * ignored
grammar = lpeg.P(grammar) * ignored * lpeg.Cp() * -1
return function(data)
local ret, next_index = lpeg.match(grammar, data)
assert(nil ~= next_index, "Invalid JSON data")
return ret
end
end
-- Since 'default' is nil, we cannot take map it
local defaultDecoder = buildDecoder(default)
local prebuilt_decoders = {}
for _, mode in pairs(modes_defined) do
if _M[mode] ~= nil then
prebuilt_decoders[_M[mode]] = buildDecoder(_M[mode])
end
end
--[[
Options:
number => number decode options
string => string decode options
array => array decode options
object => object decode options
initialObject => whether or not to require the initial object to be a table/array
allowUndefined => whether or not to allow undefined values
]]
function getDecoder(mode)
mode = mode == true and strict or mode or default
local decoder = mode == nil and defaultDecoder or prebuilt_decoders[mode]
if decoder then
return decoder
end
return buildDecoder(mode)
end
function decode(data, mode)
local decoder = getDecoder(mode)
return decoder(data)
end
local mt = getmetatable(_M) or {}
mt.__call = function(self, ...)
return decode(...)
end
setmetatable(_M, mt)