Merge branch '1.1.x'
[luajson.git] / lua / json / decode / object.lua
blobad1beacd9384be8526170656620595be1f51a366
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 util = require("json.decode.util")
8 local merge = require("json.util").merge
10 local tonumber = tonumber
11 local unpack = unpack
12 local print = print
13 local tostring = tostring
15 local rawset = rawset
17 module("json.decode.object")
19 -- BEGIN LPEG < 0.9 SUPPORT
20 local initObject, applyObjectKey
21 if not (lpeg.Cg and lpeg.Cf and lpeg.Ct) then
22 function initObject()
23 return {}
24 end
25 function applyObjectKey(tab, key, val)
26 tab[key] = val
27 return tab
28 end
29 end
30 -- END LPEG < 0.9 SUPPORT
32 local defaultOptions = {
33 number = true,
34 identifier = true,
35 trailingComma = true
38 default = nil -- Let the buildCapture optimization take place
40 strict = {
41 number = false,
42 identifier = false,
43 trailingComma = false
46 local function buildItemSequence(objectItem, ignored)
47 return (objectItem * (ignored * lpeg.P(",") * ignored * objectItem)^0) + 0
48 end
50 local function buildCapture(options, global_options, state)
51 local ignored = global_options.ignored
52 local string_type = lpeg.V(util.types.STRING)
53 local integer_type = lpeg.V(util.types.INTEGER)
54 local value_type = lpeg.V(util.types.VALUE)
55 -- If match-time capture supported, use it to remove stack limit for JSON
56 if lpeg.Cmt then
57 value_type = lpeg.Cmt(lpeg.Cp(), function(str, i)
58 -- Decode one value then return
59 local END_MARKER = {}
60 local pattern =
61 -- Found empty segment
62 #lpeg.P('}' * lpeg.Cc(END_MARKER) * lpeg.Cp())
63 -- Found a value + captured, check for required , or } + capture next pos
64 + state.VALUE_MATCH * #(lpeg.P(',') + lpeg.P('}')) * lpeg.Cp()
65 local capture, i = pattern:match(str, i)
66 if END_MARKER == capture then
67 return i
68 elseif (i == nil and capture == nil) then
69 return false
70 else
71 return i, capture
72 end
73 end)
74 end
77 options = options and merge({}, defaultOptions, options) or defaultOptions
78 local key = string_type
79 if options.identifier then
80 key = key + lpeg.C(util.identifier)
81 end
82 if options.number then
83 key = key + integer_type
84 end
85 local objectItems
86 local objectItem = (key * ignored * lpeg.P(":") * ignored * value_type)
87 -- BEGIN LPEG < 0.9 SUPPORT
88 if not (lpeg.Cg and lpeg.Cf and lpeg.Ct) then
89 local set_key = applyObjectKey
90 if options.setObjectKey then
91 local setObjectKey = options.setObjectKey
92 set_key = function(tab, key, val)
93 setObjectKey(tab, key, val)
94 return tab
95 end
96 end
98 objectItems = buildItemSequence(objectItem / set_key, ignored)
99 objectItems = lpeg.Ca(lpeg.Cc(false) / initObject * objectItems)
100 -- END LPEG < 0.9 SUPPORT
101 else
102 objectItems = buildItemSequence(lpeg.Cg(objectItem), ignored)
103 objectItems = lpeg.Cf(lpeg.Ct(0) * objectItems, options.setObjectKey or rawset)
107 local capture = lpeg.P("{") * ignored
108 capture = capture * objectItems * ignored
109 if options.trailingComma then
110 capture = capture * (lpeg.P(",") + 0) * ignored
112 capture = capture * lpeg.P("}")
113 return capture
116 function register_types()
117 util.register_type("OBJECT")
120 function load_types(options, global_options, grammar, state)
121 local capture = buildCapture(options, global_options, state)
122 local object_id = util.types.OBJECT
123 grammar[object_id] = capture
124 util.append_grammar_item(grammar, "VALUE", lpeg.V(object_id))