decoder: learns how to handle arbitrary sets of defaults (now + simple)
[luajson.git] / lua / json / decode.lua
blob02de5b9f741ec5f72326403b218bdebed34a52fe
1 --[[
2 Licensed according to the included 'LICENSE' document
3 Author: Thomas Harning Jr <harningt@gmail.com>
4 ]]
5 local lpeg = require("lpeg")
7 local error = error
9 local object = require("json.decode.object")
10 local array = require("json.decode.array")
12 local merge = require("json.util").merge
13 local util = require("json.decode.util")
15 local setmetatable, getmetatable = setmetatable, getmetatable
16 local assert = assert
17 local ipairs, pairs = ipairs, pairs
18 local string_char = string.char
20 local require = require
21 module("json.decode")
23 local modulesToLoad = {
24 "array",
25 "object",
26 "strings",
27 "number",
28 "calls",
29 "others"
31 local loadedModules = {
34 default = {
35 unicodeWhitespace = true,
36 initialObject = false
39 local modes_defined = { "default", "strict", "simple" }
41 simple = {}
43 strict = {
44 unicodeWhitespace = true,
45 initialObject = true
48 -- Register generic value type
49 util.register_type("VALUE")
50 for _,name in ipairs(modulesToLoad) do
51 local mod = require("json.decode." .. name)
52 for _, mode in pairs(modes_defined) do
53 if mod[mode] then
54 _M[mode][name] = mod[mode]
55 end
56 end
57 loadedModules[name] = mod
58 -- Register types
59 if mod.register_types then
60 mod.register_types()
61 end
62 end
64 -- Shift over default into defaultOptions to permit build optimization
65 local defaultOptions = default
66 default = nil
69 local function buildDecoder(mode)
70 mode = mode and merge({}, defaultOptions, mode) or defaultOptions
71 local ignored = mode.unicodeWhitespace and util.unicode_ignored or util.ascii_ignored
72 -- Store 'ignored' in the global options table
73 mode.ignored = ignored
75 local value_id = util.types.VALUE
76 local value_type = lpeg.V(value_id)
77 local object_type = lpeg.V(util.types.OBJECT)
78 local array_type = lpeg.V(util.types.ARRAY)
79 local grammar = {
80 [1] = mode.initialObject and (ignored * (object_type + array_type)) or value_type
82 for _, name in pairs(modulesToLoad) do
83 local mod = loadedModules[name]
84 mod.load_types(mode[name], mode, grammar)
85 end
86 -- HOOK VALUE TYPE WITH WHITESPACE
87 grammar[value_id] = ignored * grammar[value_id] * ignored
88 grammar = lpeg.P(grammar) * ignored * -1
89 return function(data)
90 local ret, err = lpeg.match(grammar, data)
91 assert(nil ~= ret, err or "Invalid JSON data")
92 return ret
93 end
94 end
96 local strictDecoder, defaultDecoder = buildDecoder(strict), buildDecoder(default)
97 --[[
98 Options:
99 number => number decode options
100 string => string decode options
101 array => array decode options
102 object => object decode options
103 initialObject => whether or not to require the initial object to be a table/array
104 allowUndefined => whether or not to allow undefined values
106 function getDecoder(mode)
107 mode = mode == true and strict or mode or default
108 if mode == strict and strictDecoder then
109 return strictDecoder
110 elseif mode == default and defaultDecoder then
111 return defaultDecoder
113 return buildDecoder(mode)
116 function decode(data, mode)
117 local decoder = getDecoder(mode)
118 return decoder(data)
121 local mt = getmetatable(_M) or {}
122 mt.__call = function(self, ...)
123 return decode(...)
125 setmetatable(_M, mt)