fix: fix lpeg version detection
[luajson.git] / lua / json / decode / util.lua
blob481565074cfc803a07f10ea8f30303811fc6a1c3
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 type = type
22 local _ENV = nil
24 local function get_invalid_character_info(input, index)
25 local parsed = input:sub(1, index)
26 local bad_character = input:sub(index, index)
27 local _, line_number = parsed:gsub('\n',{})
28 local last_line = parsed:match("\n([^\n]+.)$") or parsed
29 return line_number, #last_line, bad_character, last_line
30 end
32 local function build_report(msg)
33 local fmt = msg:gsub("%%", "%%%%") .. " @ character: %i %i:%i [%s] line:\n%s"
34 return lpeg.P(function(data, pos)
35 local line, line_index, bad_char, last_line = get_invalid_character_info(data, pos)
36 local text = fmt:format(pos, line, line_index, bad_char, last_line)
37 error(text)
38 end) * 1
39 end
40 local function unexpected()
41 local msg = "unexpected character"
42 return build_report(msg)
43 end
44 local function denied(item, option)
45 local msg
46 if option then
47 msg = ("'%s' denied by option set '%s'"):format(item, option)
48 else
49 msg = ("'%s' denied"):format(item)
50 end
51 return build_report(msg)
52 end
54 -- 09, 0A, 0B, 0C, 0D, 20
55 local ascii_space = lpeg.S("\t\n\v\f\r ")
56 local unicode_space
58 local chr = string_char
59 local u_space = ascii_space
60 -- \u0085 \u00A0
61 u_space = u_space + lpeg.P(chr(0xC2)) * lpeg.S(chr(0x85) .. chr(0xA0))
62 -- \u1680 \u180E
63 u_space = u_space + lpeg.P(chr(0xE1)) * (lpeg.P(chr(0x9A, 0x80)) + chr(0xA0, 0x8E))
64 -- \u2000 - \u200A, also 200B
65 local spacing_end = ""
66 for i = 0x80,0x8b do
67 spacing_end = spacing_end .. chr(i)
68 end
69 -- \u2028 \u2029 \u202F
70 spacing_end = spacing_end .. chr(0xA8) .. chr(0xA9) .. chr(0xAF)
71 u_space = u_space + lpeg.P(chr(0xE2, 0x80)) * lpeg.S(spacing_end)
72 -- \u205F
73 u_space = u_space + lpeg.P(chr(0xE2, 0x81, 0x9F))
74 -- \u3000
75 u_space = u_space + lpeg.P(chr(0xE3, 0x80, 0x80))
76 -- BOM \uFEFF
77 u_space = u_space + lpeg.P(chr(0xEF, 0xBB, 0xBF))
78 unicode_space = u_space
79 end
81 local identifier = lpeg.R("AZ","az","__") * lpeg.R("AZ","az", "__", "09") ^0
83 local hex = lpeg.R("09","AF","af")
84 local hexpair = hex * hex
86 local comments = {
87 cpp = lpeg.P("//") * (1 - lpeg.P("\n"))^0 * lpeg.P("\n"),
88 c = lpeg.P("/*") * (1 - lpeg.P("*/"))^0 * lpeg.P("*/")
91 local comment = comments.cpp + comments.c
93 local ascii_ignored = (ascii_space + comment)^0
95 local unicode_ignored = (unicode_space + comment)^0
97 -- Parse the lpeg version skipping patch-values
98 -- LPEG <= 0.7 have no version value... so 0.7 is value
99 -- LPEG >= 1.1 uses a string for the version instead of function
100 local DecimalLpegVersion = lpeg.version
101 and tonumber(
102 (type(lpeg.version) == "string" and lpeg.version or lpeg.version()):match("(%d+%.%d+)")
104 or 0.7
106 local function setObjectKeyForceNumber(t, key, value)
107 key = tonumber(key) or key
108 return rawset(t, key, value)
111 local util = {
112 unexpected = unexpected,
113 denied = denied,
114 ascii_space = ascii_space,
115 unicode_space = unicode_space,
116 identifier = identifier,
117 hex = hex,
118 hexpair = hexpair,
119 comments = comments,
120 comment = comment,
121 ascii_ignored = ascii_ignored,
122 unicode_ignored = unicode_ignored,
123 DecimalLpegVersion = DecimalLpegVersion,
124 get_invalid_character_info = get_invalid_character_info,
125 setObjectKeyForceNumber = setObjectKeyForceNumber
128 return util