1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
|
--[[
Licensed according to the included 'LICENSE' document
Author: Thomas Harning Jr <harningt@gmail.com>
]]
local setmetatable = setmetatable
local jsonutil = require("json.util")
local assert = assert
local type = type
local next = next
local unpack = require("table").unpack or unpack
local _ENV = nil
local state_ops = {}
local state_mt = {
__index = state_ops
}
function state_ops.pop(self)
self.previous_set = true
self.previous = self.active
local i = self.i
-- Load in this array into the active item
self.active = self.stack[i]
self.active_state = self.state_stack[i]
self.active_key = self.key_stack[i]
self.stack[i] = nil
self.state_stack[i] = nil
self.key_stack[i] = nil
self.i = i - 1
end
function state_ops.push(self)
local i = self.i + 1
self.i = i
self.stack[i] = self.active
self.state_stack[i] = self.active_state
self.key_stack[i] = self.active_key
end
function state_ops.put_object_value(self, trailing)
local object_options = self.options.object
if trailing and object_options.trailingComma then
if not self.active_key then
return
end
end
assert(self.active_key, "Missing key value")
object_options.setObjectKey(self.active, self.active_key, self:grab_value(object_options.allowEmptyElement))
self.active_key = nil
end
function state_ops.put_array_value(self, trailing)
local array_options = self.options.array
-- Safety check
if trailing and not self.previous_set and array_options.trailingComma then
return
end
local new_index = self.active_state + 1
self.active_state = new_index
self.active[new_index] = self:grab_value(array_options.allowEmptyElement)
end
function state_ops.put_call_value(self, trailing)
local call_options = self.options.calls
-- Safety check
if trailing and not self.previous_set and call_options.trailingComma then
return
end
local new_index = self.active_state + 1
self.active_state = new_index
self.active[new_index] = self:grab_value(call_options.allowEmptyElement)
end
function state_ops.put_value(self, trailing)
if self.active_state == 'object' then
self:put_object_value(trailing)
elseif self.active.func then
self:put_call_value(trailing)
else
self:put_array_value(trailing)
end
end
function state_ops.new_array(self)
local new_array = {}
if jsonutil.InitArray then
new_array = jsonutil.InitArray(new_array) or new_array
end
self.active = new_array
self.active_state = 0
self.active_key = nil
self:unset_value()
end
function state_ops.end_array(self)
if self.previous_set or self.active_state ~= 0 then
-- Not an empty array
self:put_value(true)
end
if self.active_state ~= #self.active then
-- Store the length in
self.active.n = self.active_state
end
end
function state_ops.new_object(self)
local new_object = {}
self.active = new_object
self.active_state = 'object'
self.active_key = nil
self:unset_value()
end
function state_ops.end_object(self)
if self.active_key or self.previous_set or next(self.active) then
-- Not an empty object
self:put_value(true)
end
end
function state_ops.new_call(self, name, func)
-- TODO setup properly
local new_call = {}
new_call.name = name
new_call.func = func
self.active = new_call
self.active_state = 0
self.active_key = nil
self:unset_value()
end
function state_ops.end_call(self)
if self.previous_set or self.active_state ~= 0 then
-- Not an empty array
self:put_value(true)
end
if self.active_state ~= #self.active then
-- Store the length in
self.active.n = self.active_state
end
local func = self.active.func
if func == true then
func = jsonutil.buildCall
end
self.active = func(self.active.name, unpack(self.active, 1, self.active.n or #self.active))
end
function state_ops.unset_value(self)
self.previous_set = false
self.previous = nil
end
function state_ops.grab_value(self, allowEmptyValue)
if not self.previous_set and allowEmptyValue then
-- Calculate an appropriate empty-value
return self.emptyValue
end
assert(self.previous_set, "Previous value not set")
self.previous_set = false
return self.previous
end
function state_ops.set_value(self, value)
assert(not self.previous_set, "Value set when one already in slot")
self.previous_set = true
self.previous = value
end
function state_ops.set_key(self)
assert(self.active_state == 'object', "Cannot set key on array")
local value = self:grab_value()
local value_type = type(value)
if self.options.object.number then
assert(value_type == 'string' or value_type == 'number', "As configured, a key must be a number or string")
else
assert(value_type == 'string', "As configured, a key must be a string")
end
self.active_key = value
end
local function create(options)
local emptyValue
-- Calculate an empty value up front
if options.others.allowUndefined then
emptyValue = options.others.undefined or nil
else
emptyValue = options.others.null or nil
end
local ret = {
options = options,
stack = {},
state_stack = {},
key_stack = {},
i = 0,
active = nil,
active_key = nil,
previous = nil,
active_state = nil,
emptyValue = emptyValue
}
return setmetatable(ret, state_mt)
end
local state = {
create = create
}
return state
|