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
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
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
)
38 local function unexpected()
39 local msg
= "unexpected character"
40 return build_report(msg
)
42 local function denied(item
, option
)
45 msg
= ("'%s' denied by option set '%s'"):format(item
, option
)
47 msg
= ("'%s' denied"):format(item
)
49 return build_report(msg
)
52 -- 09, 0A, 0B, 0C, 0D, 20
53 local ascii_space
= lpeg
.S("\t\n\v\f\r ")
56 local chr
= string_char
57 local u_space
= ascii_space
59 u_space
= u_space
+ lpeg
.P(chr(0xC2)) * lpeg
.S(chr(0x85) .. chr(0xA0))
61 u_space
= u_space
+ lpeg
.P(chr(0xE1)) * (lpeg
.P(chr(0x9A, 0x80)) + chr(0xA0, 0x8E))
62 -- \u2000 - \u200A, also 200B
63 local spacing_end
= ""
65 spacing_end
= spacing_end
.. chr(i
)
67 -- \u2028 \u2029 \u202F
68 spacing_end
= spacing_end
.. chr(0xA8) .. chr(0xA9) .. chr(0xAF)
69 u_space
= u_space
+ lpeg
.P(chr(0xE2, 0x80)) * lpeg
.S(spacing_end
)
71 u_space
= u_space
+ lpeg
.P(chr(0xE2, 0x81, 0x9F))
73 u_space
= u_space
+ lpeg
.P(chr(0xE3, 0x80, 0x80))
75 u_space
= u_space
+ lpeg
.P(chr(0xEF, 0xBB, 0xBF))
76 unicode_space
= u_space
79 local identifier
= lpeg
.R("AZ","az","__") * lpeg
.R("AZ","az", "__", "09") ^
0
81 local hex
= lpeg
.R("09","AF","af")
82 local hexpair
= hex
* hex
85 cpp
= lpeg
.P("//") * (1 - lpeg
.P("\n"))^
0 * lpeg
.P("\n"),
86 c
= lpeg
.P("/*") * (1 - lpeg
.P("*/"))^
0 * lpeg
.P("*/")
89 local comment
= comments
.cpp
+ comments
.c
91 local ascii_ignored
= (ascii_space
+ comment
)^
0
93 local unicode_ignored
= (unicode_space
+ comment
)^
0
95 -- Parse the lpeg version skipping patch-values
96 -- LPEG <= 0.7 have no version value... so 0.7 is value
97 local DecimalLpegVersion
= lpeg
.version
and tonumber(lpeg
.version():match("^(%d+%.%d+)")) or 0.7
99 local function setObjectKeyForceNumber(t
, key
, value
)
100 key
= tonumber(key
) or key
101 return rawset(t
, key
, value
)
105 unexpected
= unexpected
,
107 ascii_space
= ascii_space
,
108 unicode_space
= unicode_space
,
109 identifier
= identifier
,
114 ascii_ignored
= ascii_ignored
,
115 unicode_ignored
= unicode_ignored
,
116 DecimalLpegVersion
= DecimalLpegVersion
,
117 get_invalid_character_info
= get_invalid_character_info
,
118 setObjectKeyForceNumber
= setObjectKeyForceNumber