docs: updates README to indicate updated testing results and drops mention of travis...
[luajson.git] / lua / json / decode / util.lua
blobb90c0b7c802cdcab09fb3d0d9bde7cb5715808f4
1 --[[
2 Licensed according to the included 'LICENSE' document
3 Author: Thomas Harning Jr <harningt@gmail.com>
4 ]]
5 local lpeg = require("lpeg")
6 local select = select
7 local pairs, ipairs = pairs, ipairs
8 local tonumber = tonumber
9 local string_char = require("string").char
10 local rawset = rawset
11 local jsonutil = require("json.util")
13 local error = error
14 local setmetatable = setmetatable
16 local table_concat = require("table").concat
18 local merge = require("json.util").merge
20 local _ENV = nil
22 local function get_invalid_character_info(input, index)
23 local parsed = input:sub(1, index)
24 local bad_character = input:sub(index, index)
25 local _, line_number = parsed:gsub('\n',{})
26 local last_line = parsed:match("\n([^\n]+.)$") or parsed
27 return line_number, #last_line, bad_character, last_line
28 end
30 local function build_report(msg)
31 local fmt = msg:gsub("%%", "%%%%") .. " @ character: %i %i:%i [%s] line:\n%s"
32 return lpeg.P(function(data, pos)
33 local line, line_index, bad_char, last_line = get_invalid_character_info(data, pos)
34 local text = fmt:format(pos, line, line_index, bad_char, last_line)
35 error(text)
36 end) * 1
37 end
38 local function unexpected()
39 local msg = "unexpected character"
40 return build_report(msg)
41 end
42 local function expected(...)
43 local items = {...}
44 local msg
45 if #items > 1 then
46 msg = "expected one of '" .. table_concat(items, "','") .. "'"
47 else
48 msg = "expected '" .. items[1] .. "'"
49 end
50 return build_report(msg)
51 end
52 local function denied(item, option)
53 local msg
54 if option then
55 msg = ("'%s' denied by option set '%s'"):format(item, option)
56 else
57 msg = ("'%s' denied"):format(item)
58 end
59 return build_report(msg)
60 end
62 -- 09, 0A, 0B, 0C, 0D, 20
63 local ascii_space = lpeg.S("\t\n\v\f\r ")
64 local unicode_space
66 local chr = string_char
67 local u_space = ascii_space
68 -- \u0085 \u00A0
69 u_space = u_space + lpeg.P(chr(0xC2)) * lpeg.S(chr(0x85) .. chr(0xA0))
70 -- \u1680 \u180E
71 u_space = u_space + lpeg.P(chr(0xE1)) * (lpeg.P(chr(0x9A, 0x80)) + chr(0xA0, 0x8E))
72 -- \u2000 - \u200A, also 200B
73 local spacing_end = ""
74 for i = 0x80,0x8b do
75 spacing_end = spacing_end .. chr(i)
76 end
77 -- \u2028 \u2029 \u202F
78 spacing_end = spacing_end .. chr(0xA8) .. chr(0xA9) .. chr(0xAF)
79 u_space = u_space + lpeg.P(chr(0xE2, 0x80)) * lpeg.S(spacing_end)
80 -- \u205F
81 u_space = u_space + lpeg.P(chr(0xE2, 0x81, 0x9F))
82 -- \u3000
83 u_space = u_space + lpeg.P(chr(0xE3, 0x80, 0x80))
84 -- BOM \uFEFF
85 u_space = u_space + lpeg.P(chr(0xEF, 0xBB, 0xBF))
86 unicode_space = u_space
87 end
89 local identifier = lpeg.R("AZ","az","__") * lpeg.R("AZ","az", "__", "09") ^0
91 local hex = lpeg.R("09","AF","af")
92 local hexpair = hex * hex
94 local comments = {
95 cpp = lpeg.P("//") * (1 - lpeg.P("\n"))^0 * lpeg.P("\n"),
96 c = lpeg.P("/*") * (1 - lpeg.P("*/"))^0 * lpeg.P("*/")
99 local comment = comments.cpp + comments.c
101 local ascii_ignored = (ascii_space + comment)^0
103 local unicode_ignored = (unicode_space + comment)^0
105 -- Parse the lpeg version skipping patch-values
106 -- LPEG <= 0.7 have no version value... so 0.7 is value
107 local DecimalLpegVersion = lpeg.version and tonumber(lpeg.version():match("^(%d+%.%d+)")) or 0.7
109 local function setObjectKeyForceNumber(t, key, value)
110 key = tonumber(key) or key
111 return rawset(t, key, value)
114 local util = {
115 unexpected = unexpected,
116 expected = expected,
117 denied = denied,
118 ascii_space = ascii_space,
119 unicode_space = unicode_space,
120 identifier = identifier,
121 hex = hex,
122 hexpair = hexpair,
123 comments = comments,
124 comment = comment,
125 ascii_ignored = ascii_ignored,
126 unicode_ignored = unicode_ignored,
127 DecimalLpegVersion = DecimalLpegVersion,
128 get_invalid_character_info = get_invalid_character_info,
129 setObjectKeyForceNumber = setObjectKeyForceNumber
132 return util