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 expected(...)
46 msg
= "expected one of '" .. table_concat(items
, "','") .. "'"
48 msg
= "expected '" .. items
[1] .. "'"
50 return build_report(msg
)
52 local function denied(item
, option
)
55 msg
= ("'%s' denied by option set '%s'"):format(item
, option
)
57 msg
= ("'%s' denied"):format(item
)
59 return build_report(msg
)
62 -- 09, 0A, 0B, 0C, 0D, 20
63 local ascii_space
= lpeg
.S("\t\n\v\f\r ")
66 local chr
= string_char
67 local u_space
= ascii_space
69 u_space
= u_space
+ lpeg
.P(chr(0xC2)) * lpeg
.S(chr(0x85) .. chr(0xA0))
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
= ""
75 spacing_end
= spacing_end
.. chr(i
)
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
)
81 u_space
= u_space
+ lpeg
.P(chr(0xE2, 0x81, 0x9F))
83 u_space
= u_space
+ lpeg
.P(chr(0xE3, 0x80, 0x80))
85 u_space
= u_space
+ lpeg
.P(chr(0xEF, 0xBB, 0xBF))
86 unicode_space
= u_space
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
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
)
115 unexpected
= unexpected
,
118 ascii_space
= ascii_space
,
119 unicode_space
= unicode_space
,
120 identifier
= identifier
,
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