1-- Obtained from https://github.com/rxi/json.lua. 2 3local json = { _version = "0.1.2" } 4 5------------------------------------------------------------------------------- 6-- Encode 7------------------------------------------------------------------------------- 8 9local encode 10 11local escape_char_map = { 12 [ "\\" ] = "\\", 13 [ "\"" ] = "\"", 14 [ "\b" ] = "b", 15 [ "\f" ] = "f", 16 [ "\n" ] = "n", 17 [ "\r" ] = "r", 18 [ "\t" ] = "t", 19} 20 21local escape_char_map_inv = { [ "/" ] = "/" } 22for k, v in pairs(escape_char_map) do 23 escape_char_map_inv[v] = k 24end 25 26 27local function escape_char(c) 28 return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte())) 29end 30 31 32local function encode_nil(val) 33 return "null" 34end 35 36 37local function encode_table(val, stack) 38 local res = {} 39 stack = stack or {} 40 41 -- Circular reference? 42 if stack[val] then error("circular reference") end 43 44 stack[val] = true 45 46 if rawget(val, 1) ~= nil or next(val) == nil then 47 -- Treat as array -- check keys are valid and it is not sparse 48 local n = 0 49 for k in pairs(val) do 50 if type(k) ~= "number" then 51 error("invalid table: mixed or invalid key types") 52 end 53 n = n + 1 54 end 55 if n ~= #val then 56 error("invalid table: sparse array") 57 end 58 -- Encode 59 for i, v in ipairs(val) do 60 table.insert(res, encode(v, stack)) 61 end 62 stack[val] = nil 63 return "[" .. table.concat(res, ",") .. "]" 64 65 else 66 -- Treat as an object 67 for k, v in pairs(val) do 68 if type(k) ~= "string" then 69 error("invalid table: mixed or invalid key types") 70 end 71 table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) 72 end 73 stack[val] = nil 74 return "{" .. table.concat(res, ",") .. "}" 75 end 76end 77 78 79local function encode_string(val) 80 return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' 81end 82 83 84local function encode_number(val) 85 -- Check for NaN, -inf and inf 86 if val ~= val or val <= -math.huge or val >= math.huge then 87 error("unexpected number value '" .. tostring(val) .. "'") 88 end 89 return string.format("%.14g", val) 90end 91 92 93local type_func_map = { 94 [ "nil" ] = encode_nil, 95 [ "table" ] = encode_table, 96 [ "string" ] = encode_string, 97 [ "number" ] = encode_number, 98 [ "boolean" ] = tostring, 99} 100 101 102encode = function(val, stack) 103 local t = type(val) 104 local f = type_func_map[t] 105 if f then 106 return f(val, stack) 107 end 108 error("unexpected type '" .. t .. "'") 109end 110 111 112function json.encode(val) 113 return ( encode(val) ) 114end 115 116 117------------------------------------------------------------------------------- 118-- Decode 119------------------------------------------------------------------------------- 120 121local parse 122 123local function create_set(...) 124 local res = {} 125 for i = 1, select("#", ...) do 126 res[ select(i, ...) ] = true 127 end 128 return res 129end 130 131local space_chars = create_set(" ", "\t", "\r", "\n") 132local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") 133local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") 134local literals = create_set("true", "false", "null") 135 136local literal_map = { 137 [ "true" ] = true, 138 [ "false" ] = false, 139 [ "null" ] = nil, 140} 141 142 143local function next_char(str, idx, set, negate) 144 for i = idx, #str do 145 if set[str:sub(i, i)] ~= negate then 146 return i 147 end 148 end 149 return #str + 1 150end 151 152 153local function decode_error(str, idx, msg) 154 local line_count = 1 155 local col_count = 1 156 for i = 1, idx - 1 do 157 col_count = col_count + 1 158 if str:sub(i, i) == "\n" then 159 line_count = line_count + 1 160 col_count = 1 161 end 162 end 163 error( string.format("%s at line %d col %d", msg, line_count, col_count) ) 164end 165 166 167local function codepoint_to_utf8(n) 168 -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa 169 local f = math.floor 170 if n <= 0x7f then 171 return string.char(n) 172 elseif n <= 0x7ff then 173 return string.char(f(n / 64) + 192, n % 64 + 128) 174 elseif n <= 0xffff then 175 return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) 176 elseif n <= 0x10ffff then 177 return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, 178 f(n % 4096 / 64) + 128, n % 64 + 128) 179 end 180 error( string.format("invalid unicode codepoint '%x'", n) ) 181end 182 183 184local function parse_unicode_escape(s) 185 local n1 = tonumber( s:sub(1, 4), 16 ) 186 local n2 = tonumber( s:sub(7, 10), 16 ) 187 -- Surrogate pair? 188 if n2 then 189 return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) 190 else 191 return codepoint_to_utf8(n1) 192 end 193end 194 195 196local function parse_string(str, i) 197 local res = "" 198 local j = i + 1 199 local k = j 200 201 while j <= #str do 202 local x = str:byte(j) 203 204 if x < 32 then 205 decode_error(str, j, "control character in string") 206 207 elseif x == 92 then -- `\`: Escape 208 res = res .. str:sub(k, j - 1) 209 j = j + 1 210 local c = str:sub(j, j) 211 if c == "u" then 212 local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) 213 or str:match("^%x%x%x%x", j + 1) 214 or decode_error(str, j - 1, "invalid unicode escape in string") 215 res = res .. parse_unicode_escape(hex) 216 j = j + #hex 217 else 218 if not escape_chars[c] then 219 decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string") 220 end 221 res = res .. escape_char_map_inv[c] 222 end 223 k = j + 1 224 225 elseif x == 34 then -- `"`: End of string 226 res = res .. str:sub(k, j - 1) 227 return res, j + 1 228 end 229 230 j = j + 1 231 end 232 233 decode_error(str, i, "expected closing quote for string") 234end 235 236 237local function parse_number(str, i) 238 local x = next_char(str, i, delim_chars) 239 local s = str:sub(i, x - 1) 240 local n = tonumber(s) 241 if not n then 242 decode_error(str, i, "invalid number '" .. s .. "'") 243 end 244 return n, x 245end 246 247 248local function parse_literal(str, i) 249 local x = next_char(str, i, delim_chars) 250 local word = str:sub(i, x - 1) 251 if not literals[word] then 252 decode_error(str, i, "invalid literal '" .. word .. "'") 253 end 254 return literal_map[word], x 255end 256 257 258local function parse_array(str, i) 259 local res = {} 260 local n = 1 261 i = i + 1 262 while 1 do 263 local x 264 i = next_char(str, i, space_chars, true) 265 -- Empty / end of array? 266 if str:sub(i, i) == "]" then 267 i = i + 1 268 break 269 end 270 -- Read token 271 x, i = parse(str, i) 272 res[n] = x 273 n = n + 1 274 -- Next token 275 i = next_char(str, i, space_chars, true) 276 local chr = str:sub(i, i) 277 i = i + 1 278 if chr == "]" then break end 279 if chr ~= "," then decode_error(str, i, "expected ']' or ','") end 280 end 281 return res, i 282end 283 284 285local function parse_object(str, i) 286 local res = {} 287 i = i + 1 288 while 1 do 289 local key, val 290 i = next_char(str, i, space_chars, true) 291 -- Empty / end of object? 292 if str:sub(i, i) == "}" then 293 i = i + 1 294 break 295 end 296 -- Read key 297 if str:sub(i, i) ~= '"' then 298 decode_error(str, i, "expected string for key") 299 end 300 key, i = parse(str, i) 301 -- Read ':' delimiter 302 i = next_char(str, i, space_chars, true) 303 if str:sub(i, i) ~= ":" then 304 decode_error(str, i, "expected ':' after key") 305 end 306 i = next_char(str, i + 1, space_chars, true) 307 -- Read value 308 val, i = parse(str, i) 309 -- Set 310 res[key] = val 311 -- Next token 312 i = next_char(str, i, space_chars, true) 313 local chr = str:sub(i, i) 314 i = i + 1 315 if chr == "}" then break end 316 if chr ~= "," then decode_error(str, i, "expected '}' or ','") end 317 end 318 return res, i 319end 320 321 322local char_func_map = { 323 [ '"' ] = parse_string, 324 [ "0" ] = parse_number, 325 [ "1" ] = parse_number, 326 [ "2" ] = parse_number, 327 [ "3" ] = parse_number, 328 [ "4" ] = parse_number, 329 [ "5" ] = parse_number, 330 [ "6" ] = parse_number, 331 [ "7" ] = parse_number, 332 [ "8" ] = parse_number, 333 [ "9" ] = parse_number, 334 [ "-" ] = parse_number, 335 [ "t" ] = parse_literal, 336 [ "f" ] = parse_literal, 337 [ "n" ] = parse_literal, 338 [ "[" ] = parse_array, 339 [ "{" ] = parse_object, 340} 341 342 343parse = function(str, idx) 344 local chr = str:sub(idx, idx) 345 local f = char_func_map[chr] 346 if f then 347 return f(str, idx) 348 end 349 decode_error(str, idx, "unexpected character '" .. chr .. "'") 350end 351 352 353function json.decode(str) 354 if type(str) ~= "string" then 355 error("expected argument of type string, got " .. type(str)) 356 end 357 local res, idx = parse(str, next_char(str, 1, space_chars, true)) 358 idx = next_char(str, idx, space_chars, true) 359 if idx <= #str then 360 decode_error(str, idx, "trailing garbage") 361 end 362 return res 363end 364 365 366return json