2 Licensed according to the included 'LICENSE' document
3 Author: Thomas Harning Jr <harningt@gmail.com>
5 local lpeg
= require("lpeg")
7 local pairs
, ipairs
= pairs
, ipairs
8 local tonumber = tonumber
9 local string_char
= require("string").char
11 local jsonutil
= require("json.util")
14 local setmetatable
= setmetatable
16 local table_concat
= require("table").concat
18 local merge
= require("json.util").merge
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
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
)
40 local function unexpected()
41 local msg
= "unexpected character"
42 return build_report(msg
)
44 local function denied(item
, option
)
47 msg
= ("'%s' denied by option set '%s'"):format(item
, option
)
49 msg
= ("'%s' denied"):format(item
)
51 return build_report(msg
)
54 -- 09, 0A, 0B, 0C, 0D, 20
55 local ascii_space
= lpeg
.S("\t\n\v\f\r ")
58 local chr
= string_char
59 local u_space
= ascii_space
61 u_space
= u_space
+ lpeg
.P(chr(0xC2)) * lpeg
.S(chr(0x85) .. chr(0xA0))
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
= ""
67 spacing_end
= spacing_end
.. chr(i
)
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
)
73 u_space
= u_space
+ lpeg
.P(chr(0xE2, 0x81, 0x9F))
75 u_space
= u_space
+ lpeg
.P(chr(0xE3, 0x80, 0x80))
77 u_space
= u_space
+ lpeg
.P(chr(0xEF, 0xBB, 0xBF))
78 unicode_space
= u_space
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
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
102 (type(lpeg
.version
) == "string" and lpeg
.version
or lpeg
.version()):match("(%d+%.%d+)")
106 local function setObjectKeyForceNumber(t
, key
, value
)
107 key
= tonumber(key
) or key
108 return rawset(t
, key
, value
)
112 unexpected
= unexpected
,
114 ascii_space
= ascii_space
,
115 unicode_space
= unicode_space
,
116 identifier
= identifier
,
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