aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--makefile1
-rw-r--r--relabel.lua320
-rwxr-xr-xtest.lua2
-rw-r--r--testerrors.lua701
4 files changed, 946 insertions, 78 deletions
diff --git a/makefile b/makefile
index b019e15..51d3b15 100644
--- a/makefile
+++ b/makefile
@@ -43,6 +43,7 @@ $(FILES): makefile
43test: test.lua relabel.lua lpeglabel.so 43test: test.lua relabel.lua lpeglabel.so
44 lua test.lua 44 lua test.lua
45 lua testlabel.lua 45 lua testlabel.lua
46 lua testerrors.lua
46 47
47clean: 48clean:
48 rm -f $(FILES) lpeglabel.so 49 rm -f $(FILES) lpeglabel.so
diff --git a/relabel.lua b/relabel.lua
index b66fc2e..6fdbb7c 100644
--- a/relabel.lua
+++ b/relabel.lua
@@ -1,9 +1,11 @@
1-- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $ 1-- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $
2 2
3-- imported functions and modules 3-- imported functions and modules
4local tonumber, type, print, error = tonumber, type, print, error 4local tonumber, type, print, error, ipairs = tonumber, type, print, error, ipairs
5local pcall = pcall
5local setmetatable = setmetatable 6local setmetatable = setmetatable
6local unpack = table.unpack or unpack 7local unpack, tinsert, concat = table.unpack or unpack, table.insert, table.concat
8local rep = string.rep
7local m = require"lpeglabel" 9local m = require"lpeglabel"
8 10
9-- 'm' will be used to parse expressions, and 'mm' will be used to 11-- 'm' will be used to parse expressions, and 'mm' will be used to
@@ -22,6 +24,74 @@ if version == "Lua 5.2" then _ENV = nil end
22 24
23 25
24local any = m.P(1) 26local any = m.P(1)
27local dummy = mm.P(false)
28
29
30local errinfo = {
31 {"NoPatt", "no pattern found"},
32 {"ExtraChars", "unexpected characters after the pattern"},
33
34 {"ExpPatt1", "expected a pattern after '/' or the label(s)"},
35
36 {"ExpPatt2", "expected a pattern after '&'"},
37 {"ExpPatt3", "expected a pattern after '!'"},
38
39 {"ExpPatt4", "expected a pattern after '('"},
40 {"ExpPatt5", "expected a pattern after ':'"},
41 {"ExpPatt6", "expected a pattern after '{~'"},
42 {"ExpPatt7", "expected a pattern after '{|'"},
43
44 {"ExpPatt8", "expected a pattern after '<-'"},
45
46 {"ExpPattOrClose", "expected a pattern or closing '}' after '{'"},
47
48 {"ExpNum", "expected a number after '^', '+' or '-' (no space)"},
49 {"ExpCap", "expected a string, number, '{}' or name after '->'"},
50
51 {"ExpName1", "expected the name of a rule after '=>'"},
52 {"ExpName2", "expected the name of a rule after '=' (no space)"},
53 {"ExpName3", "expected the name of a rule after '<' (no space)"},
54
55 {"ExpLab1", "expected at least one label after '{'"},
56 {"ExpLab2", "expected a label after the comma"},
57
58 {"ExpNameOrLab", "expected a name or label after '%' (no space)"},
59
60 {"ExpItem", "expected at least one item after '[' or '^'"},
61
62 {"MisClose1", "missing closing ')'"},
63 {"MisClose2", "missing closing ':}'"},
64 {"MisClose3", "missing closing '~}'"},
65 {"MisClose4", "missing closing '|}'"},
66 {"MisClose5", "missing closing '}'"}, -- for the captures
67
68 {"MisClose6", "missing closing '>'"},
69 {"MisClose7", "missing closing '}'"}, -- for the labels
70
71 {"MisClose8", "missing closing ']'"},
72
73 {"MisTerm1", "missing terminating single quote"},
74 {"MisTerm2", "missing terminating double quote"},
75}
76
77local errmsgs = {}
78local labels = {}
79
80for i, err in ipairs(errinfo) do
81 errmsgs[i] = err[2]
82 labels[err[1]] = i
83end
84
85local syntaxerrs = {}
86
87local function expect (pattern, labelname)
88 local label = labels[labelname]
89 local record = function (input, pos)
90 tinsert(syntaxerrs, { label = label, pos = pos })
91 return true
92 end
93 return pattern + m.Cmt("", record) * m.T(label)
94end
25 95
26 96
27-- Pre-defined names 97-- Pre-defined names
@@ -75,18 +145,13 @@ local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end)
75 145
76local function getdef (id, defs) 146local function getdef (id, defs)
77 local c = defs and defs[id] 147 local c = defs and defs[id]
78 if not c then error("undefined name: " .. id) end 148 if not c then
149 error("undefined name: " .. id)
150 end
79 return c 151 return c
80end 152end
81 153
82 154
83local function patt_error (s, i)
84 local msg = (#s < i + 20) and s:sub(i)
85 or s:sub(i,i+20) .. "..."
86 msg = ("pattern error near '%s'"):format(msg)
87 error(msg, 2)
88end
89
90local function mult (p, n) 155local function mult (p, n)
91 local np = mm.P(true) 156 local np = mm.P(true)
92 while n >= 1 do 157 while n >= 1 do
@@ -106,40 +171,37 @@ end
106 171
107local S = (Predef.space + "--" * (any - Predef.nl)^0)^0 172local S = (Predef.space + "--" * (any - Predef.nl)^0)^0
108 173
109local name = m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0 174local name = m.C(m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0)
110 175
111local arrow = S * "<-" 176local arrow = S * "<-"
112 177
113local seq_follow = m.P"/" + ")" + "}" + ":}" + "~}" + "|}" + (name * arrow) + -1
114
115name = m.C(name)
116
117
118-- a defined name only have meaning in a given environment 178-- a defined name only have meaning in a given environment
119local Def = name * m.Carg(1) 179local Def = name * m.Carg(1)
120 180
121local num = m.C(m.R"09"^1) * S / tonumber 181local num = m.C(m.R"09"^1) * S / tonumber
122 182
123local String = "'" * m.C((any - "'")^0) * "'" + 183local String = "'" * m.C((any - "'" - m.P"\n")^0) * expect("'", "MisTerm1")
124 '"' * m.C((any - '"')^0) * '"' 184 + '"' * m.C((any - '"' - m.P"\n")^0) * expect('"', "MisTerm2")
125 185
126 186
127local defined = "%" * Def / function (c,Defs) 187local defined = "%" * Def / function (c,Defs)
128 local cat = Defs and Defs[c] or Predef[c] 188 local cat = Defs and Defs[c] or Predef[c]
129 if not cat then error ("name '" .. c .. "' undefined") end 189 if not cat then
190 error("name '" .. c .. "' undefined")
191 end
130 return cat 192 return cat
131end 193end
132 194
133local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R 195local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R
134 196
135local item = defined + Range + m.C(any) 197local item = defined + Range + m.C(any - m.P"\n")
136 198
137local Class = 199local Class =
138 "[" 200 "["
139 * (m.C(m.P"^"^-1)) -- optional complement symbol 201 * (m.C(m.P"^"^-1)) -- optional complement symbol
140 * m.Cf(item * (item - "]")^0, mt.__add) / 202 * m.Cf(expect(item, "ExpItem") * (item - "]")^0, mt.__add)
141 function (c, p) return c == "^" and any - p or p end 203 / function (c, p) return c == "^" and any - p or p end
142 * "]" 204 * expect("]", "MisClose8")
143 205
144local function adddef (t, k, exp) 206local function adddef (t, k, exp)
145 if t[k] then 207 if t[k] then
@@ -161,71 +223,175 @@ local function NT (n, b)
161end 223end
162 224
163local function labchoice (...) 225local function labchoice (...)
164 local t = { ... } 226 local t = { ... }
165 local n = #t 227 local n = #t
166 local p = t[1] 228 local p = t[1]
167 local i = 2 229 local i = 2
168 while i + 1 <= n do 230 while i + 1 <= n do
169 p = mm.Lc(p, t[i+1], unpack(t[i])) 231 -- t[i] == nil when there are no labels
170 i = i + 2 232 p = t[i] and mm.Lc(p, t[i+1], unpack(t[i])) or mt.__add(p, t[i+1])
171 end 233 i = i + 2
172 234 end
173 return p 235
236 return p
237end
238
239-- error recovery
240local skip = m.P { "Skip",
241 Skip = (-m.P"/" * -m.P(name * arrow) * m.V"Ignored")^0 * m.Cc(dummy);
242 Ignored = m.V"Group" + any;
243 Group = "(" * (-m.P")" * m.V"Ignored")^0 * ")"
244 + "{" * (-m.P"}" * m.V"Ignored")^0 * "}"
245 + "[" * (-m.P"]" * m.V"Ignored")^0 * "]"
246 + "'" * (-m.P"'" * m.V"Ignored")^0 * "'"
247 + '"' * (-m.P'"' * m.V"Ignored")^0 * '"';
248}
249
250local ignore = m.Cmt(any, function (input, pos)
251 return syntaxerrs[#syntaxerrs].pos, dummy
252end)
253
254local pointAtStart = m.Cmt(any, function (input, pos)
255 -- like ignore but makes the last syntax error point at the start
256 local ret = syntaxerrs[#syntaxerrs].pos
257 syntaxerrs[#syntaxerrs].pos = pos-1
258 return ret, dummy
259end)
260
261
262local function labify (labelnames)
263 for i, l in ipairs(labelnames) do
264 labelnames[i] = labels[l]
265 end
266 return labelnames
174end 267end
175 268
269local labelset1 = labify {
270 "ExpPatt2", "ExpPatt3",
271 "ExpPatt4", "ExpPatt5", "ExpPatt6", "ExpPatt7",
272 "ExpPatt8", "ExpPattOrClose",
273 "ExpNum", "ExpCap",
274 "ExpName1", "ExpName2", "ExpName3",
275 "ExpNameOrLab", "ExpItem",
276 "MisClose6", "MisClose7"
277}
278
279local labelset2 = labify {
280 "MisClose1", "MisClose2", "MisClose3", "MisClose4", "MisClose5"
281}
282
283local labelset3 = labify {
284 "ExpPatt1", "ExpLab1", "ExpLab2", "MisClose7"
285}
176 286
177local exp = m.P{ "Exp", 287local exp = m.P{ "Exp",
178 Exp = S * ( m.V"Grammar" 288 Exp = S * ( m.V"Grammar"
179 + (m.V"Seq") * ("/" * m.V"Labels" * S * m.V"Seq")^1 / labchoice 289 + (m.V"RecovSeq" * (S * "/" * m.Lc((m.Ct(m.V"Labels") + m.Cc(nil))
180 + m.Cf(m.V"Seq" * ("/" * S * m.V"Seq")^0, mt.__add) ); 290 * expect(S * m.V"RecovSeq",
181 Labels = m.Ct(m.P"{" * S * m.V"Label" * (S * "," * S * m.V"Label")^0 * S * "}"); 291 "ExpPatt1"),
182 Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix"^0 , mt.__mul) 292 m.Cc(nil) * skip,
183 * (#seq_follow + patt_error); 293 unpack(labelset3))
184 Prefix = "&" * S * m.V"Prefix" / mt.__len 294 )^0
185 + "!" * S * m.V"Prefix" / mt.__unm 295 ) / labchoice);
296 Labels = m.P"{" * expect(S * m.V"Label", "ExpLab1")
297 * (S * "," * expect(S * m.V"Label", "ExpLab2"))^0
298 * expect(S * "}", "MisClose7");
299 RecovSeq = m.Lc(m.V"Seq", skip, unpack(labelset1));
300 Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix" * (S * m.V"Prefix")^0, mt.__mul);
301 Prefix = "&" * expect(S * m.V"Prefix", "ExpPatt2") / mt.__len
302 + "!" * expect(S * m.V"Prefix", "ExpPatt3") / mt.__unm
186 + m.V"Suffix"; 303 + m.V"Suffix";
187 Suffix = m.Cf(m.V"Primary" * S * 304 Suffix = m.Cf(m.V"RecovPrimary" *
188 ( ( m.P"+" * m.Cc(1, mt.__pow) 305 ( S * ( m.P"+" * m.Cc(1, mt.__pow)
189 + m.P"*" * m.Cc(0, mt.__pow) 306 + m.P"*" * m.Cc(0, mt.__pow)
190 + m.P"?" * m.Cc(-1, mt.__pow) 307 + m.P"?" * m.Cc(-1, mt.__pow)
191 + "^" * ( m.Cg(num * m.Cc(mult)) 308 + "^" * expect( m.Cg(num * m.Cc(mult))
192 + m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow)) 309 + m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow)
193 ) 310 ),
194 + "->" * S * ( m.Cg((String + num) * m.Cc(mt.__div)) 311 "ExpNum")
195 + m.P"{}" * m.Cc(nil, m.Ct) 312 + "->" * expect(S * ( m.Cg((String + num) * m.Cc(mt.__div))
196 + m.Cg(Def / getdef * m.Cc(mt.__div)) 313 + m.P"{}" * m.Cc(nil, m.Ct)
197 ) 314 + m.Cg(Def / getdef * m.Cc(mt.__div))
198 + "=>" * S * m.Cg(Def / getdef * m.Cc(m.Cmt)) 315 ),
199 ) * S 316 "ExpCap")
317 + "=>" * expect(S * m.Cg(Def / getdef * m.Cc(m.Cmt)),
318 "ExpName1")
319 )
200 )^0, function (a,b,f) return f(a,b) end ); 320 )^0, function (a,b,f) return f(a,b) end );
201 Primary = "(" * m.V"Exp" * ")" 321 RecovPrimary = m.Lc(m.V"Primary", ignore, unpack(labelset2));
202 + String / mm.P 322 Primary = "(" * expect(m.V"Exp", "ExpPatt4") * expect(S * ")", "MisClose1")
203 + Class 323 + m.Lc(String / mm.P, pointAtStart,
204 + defined 324 labels["MisTerm1"], labels["MisTerm2"])
205 + "%{" * S * m.V"Label" * (S * "," * S * m.V"Label")^0 * S * "}" / mm.T 325 + m.Lc(Class, pointAtStart, labels["MisClose8"])
206 + "{:" * (name * ":" + m.Cc(nil)) * m.V"Exp" * ":}" / 326 + defined
207 function (n, p) return mm.Cg(p, n) end 327 + "%" * expect(m.V"Labels", "ExpNameOrLab") / mm.T
208 + "=" * name / function (n) return mm.Cmt(mm.Cb(n), equalcap) end 328 + "{:" * (name * ":" + m.Cc(nil)) * expect(m.V"Exp", "ExpPatt5")
209 + m.P"{}" / mm.Cp 329 * expect(S * ":}", "MisClose2")
210 + "{~" * m.V"Exp" * "~}" / mm.Cs 330 / function (n, p) return mm.Cg(p, n) end
211 + "{|" * m.V"Exp" * "|}" / mm.Ct 331 + "=" * expect(name, "ExpName2")
212 + "{" * m.V"Exp" * "}" / mm.C 332 / function (n) return mm.Cmt(mm.Cb(n), equalcap) end
213 + m.P"." * m.Cc(any) 333 + m.P"{}" / mm.Cp
214 + (name * -arrow + "<" * name * ">") * m.Cb("G") / NT; 334 + "{~" * expect(m.V"Exp", "ExpPatt6")
215 Label = num + name / function (f) return tlabels[f] end; 335 * expect(S * "~}", "MisClose3") / mm.Cs
216 Definition = name * arrow * m.V"Exp"; 336 + "{|" * expect(m.V"Exp", "ExpPatt7")
217 Grammar = m.Cg(m.Cc(true), "G") * 337 * expect(S * "|}", "MisClose4") / mm.Ct
218 m.Cf(m.V"Definition" / firstdef * m.Cg(m.V"Definition")^0, 338 + "{" * expect(m.V"Exp", "ExpPattOrClose")
219 adddef) / mm.P 339 * expect(S * "}", "MisClose5") / mm.C
340 + m.P"." * m.Cc(any)
341 + (name * -arrow + "<" * expect(name, "ExpName3")
342 * expect(">", "MisClose6")) * m.Cb("G") / NT;
343 Label = num + name / function (f) return tlabels[f] end;
344 Definition = name * arrow * expect(m.V"Exp", "ExpPatt8");
345 Grammar = m.Cg(m.Cc(true), "G")
346 * m.Cf(m.V"Definition" / firstdef * (S * m.Cg(m.V"Definition"))^0,
347 adddef) / mm.P;
220} 348}
221 349
222local pattern = S * m.Cg(m.Cc(false), "G") * exp / mm.P * (-any + patt_error) 350local pattern = S * m.Cg(m.Cc(false), "G") * expect(exp, "NoPatt") / mm.P
351 * S * expect(-any, "ExtraChars")
223 352
353local function lineno (s, i)
354 if i == 1 then return 1, 1 end
355 local adjustment = 0
356 -- report the current line if at end of line, not the next
357 if s:sub(i,i) == '\n' then
358 i = i-1
359 adjustment = 1
360 end
361 local rest, num = s:sub(1,i):gsub("[^\n]*\n", "")
362 local r = #rest
363 return 1 + num, (r ~= 0 and r or 1) + adjustment
364end
365
366local function splitlines(str)
367 local t = {}
368 local function helper(line) tinsert(t, line) return "" end
369 helper((str:gsub("(.-)\r?\n", helper)))
370 return t
371end
224 372
225local function compile (p, defs) 373local function compile (p, defs)
226 if mm.type(p) == "pattern" then return p end -- already compiled 374 if mm.type(p) == "pattern" then return p end -- already compiled
227 local cp = pattern:match(p, 1, defs) 375 p = p .. " " -- for better reporting of column numbers in errors when at EOF
228 if not cp then error("incorrect pattern", 3) end 376 local ok, cp, label, suffix = pcall(function() return pattern:match(p, 1, defs) end)
377 if not ok and #syntaxerrs == 0 then
378 if type(cp) == "string" then
379 cp = cp:gsub("^[^:]+:[^:]+: ", "")
380 end
381 error(cp)
382 end
383 if #syntaxerrs > 0 then
384 local lines = splitlines(p)
385 local errors = {}
386 for i, err in ipairs(syntaxerrs) do
387 local line, col = lineno(p, err.pos)
388 tinsert(errors, "L" .. line .. ":C" .. col .. ": " .. errmsgs[err.label])
389 tinsert(errors, lines[line])
390 tinsert(errors, rep(" ", col-1) .. "^")
391 end
392 syntaxerrs = {}
393 error("syntax error(s) in pattern\n" .. concat(errors, "\n"))
394 end
229 return cp 395 return cp
230end 396end
231 397
@@ -264,7 +430,7 @@ local function gsub (s, p, rep)
264end 430end
265 431
266local function setlabels (t) 432local function setlabels (t)
267 tlabels = t 433 tlabels = t
268end 434end
269 435
270-- exported names 436-- exported names
@@ -274,7 +440,7 @@ local re = {
274 find = find, 440 find = find,
275 gsub = gsub, 441 gsub = gsub,
276 updatelocale = updatelocale, 442 updatelocale = updatelocale,
277 setlabels = setlabels 443 setlabels = setlabels
278} 444}
279 445
280if version == "Lua 5.1" then _G.re = re end 446if version == "Lua 5.1" then _G.re = re end
diff --git a/test.lua b/test.lua
index c6049e7..86cda61 100755
--- a/test.lua
+++ b/test.lua
@@ -1420,7 +1420,7 @@ errmsg('aaaa', "rule 'aaaa'")
1420errmsg('a', 'outside') 1420errmsg('a', 'outside')
1421errmsg('b <- a', 'undefined') 1421errmsg('b <- a', 'undefined')
1422errmsg("x <- 'a' x <- 'b'", 'already defined') 1422errmsg("x <- 'a' x <- 'b'", 'already defined')
1423errmsg("'a' -", "near '-'") 1423-- errmsg("'a' -", "near '-'")
1424 1424
1425 1425
1426print"OK" 1426print"OK"
diff --git a/testerrors.lua b/testerrors.lua
new file mode 100644
index 0000000..1c5bb9f
--- /dev/null
+++ b/testerrors.lua
@@ -0,0 +1,701 @@
1local re = require 'relabel'
2
3function testerror(repatt, msg)
4 msg = msg:match("^%s*(.-)%s*$") -- trim
5 local ok, err = pcall(function () re.compile(repatt) end)
6 assert(not ok)
7 if msg:match("^[^\n]*\n(.-)$") then
8 -- expecting a syntax error
9 err = err:match("^[^\n]*\n(.-)$") -- remove first line (filename)
10 err = err:gsub("[ \t]*\n", "\n") -- remove trailing spaces
11 -- if err ~= msg then
12 -- print(#err, #msg)
13 -- print('--')
14 -- print(err)
15 -- print('--')
16 -- print(msg)
17 -- print('--')
18 -- end
19 assert(err == msg)
20 else
21 -- expecting a non-syntax error
22 assert(err:match(msg))
23 end
24end
25
26-- testing NoPatt
27
28testerror([[~]], [[
29L1:C1: no pattern found
30~
31^
32]])
33
34testerror([[???]], [[
35L1:C1: no pattern found
36???
37^
38]])
39
40-- testing ExtraChars
41
42testerror([['p'~]], [[
43L1:C4: unexpected characters after the pattern
44'p'~
45 ^
46]])
47
48testerror([['p'?$?]], [[
49L1:C5: unexpected characters after the pattern
50'p'?$?
51 ^
52]])
53
54-- testing ExpPatt1
55
56testerror([['p' /{1}]], [[
57L1:C9: expected a pattern after '/' or the label(s)
58'p' /{1}
59 ^
60]])
61
62testerror([['p' /{1} /{2} 'q']], [[
63L1:C9: expected a pattern after '/' or the label(s)
64'p' /{1} /{2} 'q'
65 ^
66]])
67
68testerror([['p' /]], [[
69L1:C6: expected a pattern after '/' or the label(s)
70'p' /
71 ^
72]])
73
74testerror([['p' / / 'q']], [[
75L1:C6: expected a pattern after '/' or the label(s)
76'p' / / 'q'
77 ^
78]])
79
80-- testing ExpPatt2
81
82testerror([[&]], [[
83L1:C2: expected a pattern after '&'
84&
85 ^
86]])
87
88testerror([[& / 'p']], [[
89L1:C2: expected a pattern after '&'
90& / 'p'
91 ^
92]])
93
94testerror([['p' &]], [[
95L1:C6: expected a pattern after '&'
96'p' &
97 ^
98]])
99
100testerror([['p' / & / 'q']], [[
101L1:C8: expected a pattern after '&'
102'p' / & / 'q'
103 ^
104]])
105
106testerror([[&&]], [[
107L1:C3: expected a pattern after '&'
108&&
109 ^
110]])
111
112testerror([[!&]], [[
113L1:C3: expected a pattern after '&'
114!&
115 ^
116]])
117
118-- testing ExpPatt3
119
120testerror([[!]], [[
121L1:C2: expected a pattern after '!'
122!
123 ^
124]])
125
126testerror([[! / 'p']], [[
127L1:C2: expected a pattern after '!'
128! / 'p'
129 ^
130]])
131
132testerror([['p' !]], [[
133L1:C6: expected a pattern after '!'
134'p' !
135 ^
136]])
137
138testerror([['p' / ! / 'q']], [[
139L1:C8: expected a pattern after '!'
140'p' / ! / 'q'
141 ^
142]])
143
144testerror([[!!]], [[
145L1:C3: expected a pattern after '!'
146!!
147 ^
148]])
149
150testerror([[&!]], [[
151L1:C3: expected a pattern after '!'
152&!
153 ^
154]])
155
156-- testing ExpPatt4
157
158testerror([[()]], [[
159L1:C2: expected a pattern after '('
160()
161 ^
162]])
163
164testerror([[($$$)]], [[
165L1:C2: expected a pattern after '('
166($$$)
167 ^
168]])
169
170-- testing ExpPatt5
171
172testerror([[{: *** :}]], [[
173L1:C3: expected a pattern after ':'
174{: *** :}
175 ^
176]])
177
178testerror([[{:group: *** :}]], [[
179L1:C9: expected a pattern after ':'
180{:group: *** :}
181 ^
182]])
183
184testerror([[x <- {:x:}]], [[
185L1:C10: expected a pattern after ':'
186x <- {:x:}
187 ^
188]])
189
190-- testing ExpPatt6
191
192testerror([[{~~}]], [[
193L1:C3: expected a pattern after '{~'
194{~~}
195 ^
196]])
197
198testerror([[{ {~ } ~}]], [[
199L1:C5: expected a pattern after '{~'
200{ {~ } ~}
201 ^
202L1:C10: missing closing '}'
203{ {~ } ~}
204 ^
205]])
206
207testerror([[{~ ^_^ ~}]], [[
208L1:C3: expected a pattern after '{~'
209{~ ^_^ ~}
210 ^
211]])
212
213-- testing ExpPatt7
214
215testerror([[{||}]], [[
216L1:C3: expected a pattern after '{|'
217{||}
218 ^
219]])
220
221testerror([[{|@|}]], [[
222L1:C3: expected a pattern after '{|'
223{|@|}
224 ^
225]])
226
227-- testing ExpPatt8
228
229testerror([[S <-]], [[
230L1:C5: expected a pattern after '<-'
231S <-
232 ^
233]])
234
235testerror([[S <- 'p' T <-]], [[
236L1:C14: expected a pattern after '<-'
237S <- 'p' T <-
238 ^
239]])
240
241-- testing ExpPattOrClose
242
243testerror([[{0}]], [[
244L1:C2: expected a pattern or closing '}' after '{'
245{0}
246 ^
247]])
248
249testerror([[{ :'p': }]], [[
250L1:C2: expected a pattern or closing '}' after '{'
251{ :'p': }
252 ^
253]])
254
255-- testing ExpNum
256
257testerror([['p' ^ n]], [[
258L1:C6: expected a number after '^', '+' or '-' (no space)
259'p' ^ n
260 ^
261]])
262
263testerror([['p'^+(+1)]], [[
264L1:C5: expected a number after '^', '+' or '-' (no space)
265'p'^+(+1)
266 ^
267]])
268
269testerror([['p'^-/'q']], [[
270L1:C5: expected a number after '^', '+' or '-' (no space)
271'p'^-/'q'
272 ^
273]])
274
275-- testing ExpCap
276
277testerror([['p' -> {]], [[
278L1:C7: expected a string, number, '{}' or name after '->'
279'p' -> {
280 ^
281]])
282
283testerror([['p' -> {'q'}]], [[
284L1:C7: expected a string, number, '{}' or name after '->'
285'p' -> {'q'}
286 ^
287]])
288
289testerror([['p' -> / 'q']], [[
290L1:C7: expected a string, number, '{}' or name after '->'
291'p' -> / 'q'
292 ^
293]])
294
295testerror([['p' -> [0-9] ]], [[
296L1:C7: expected a string, number, '{}' or name after '->'
297'p' -> [0-9]
298 ^
299]])
300
301-- testing ExpName1
302
303testerror([['p' =>]], [[
304L1:C7: expected the name of a rule after '=>'
305'p' =>
306 ^
307]])
308
309testerror([['p' => 'q']], [[
310L1:C7: expected the name of a rule after '=>'
311'p' => 'q'
312 ^
313]])
314
315-- testing ExpName2
316
317testerror([['<' {:tag: [a-z]+ :} '>' '<' = '>']], [[
318L1:C31: expected the name of a rule after '=' (no space)
319'<' {:tag: [a-z]+ :} '>' '<' = '>'
320 ^
321]])
322
323testerror([['<' {:tag: [a-z]+ :} '>' '<' = tag '>']], [[
324L1:C31: expected the name of a rule after '=' (no space)
325'<' {:tag: [a-z]+ :} '>' '<' = tag '>'
326 ^
327]])
328
329-- testing ExpName3
330
331testerror([[<>]], [[
332L1:C2: expected the name of a rule after '<' (no space)
333<>
334 ^
335]])
336
337testerror([[<123>]], [[
338L1:C2: expected the name of a rule after '<' (no space)
339<123>
340 ^
341]])
342
343testerror([[< hello >]], [[
344L1:C2: expected the name of a rule after '<' (no space)
345< hello >
346 ^
347]])
348
349testerror([[<<S>>]], [[
350L1:C2: expected the name of a rule after '<' (no space)
351<<S>>
352 ^
353]])
354
355-- testing ExpLab1
356
357testerror([['p' /{} 'q']], [[
358L1:C7: expected at least one label after '{'
359'p' /{} 'q'
360 ^
361]])
362
363testerror([[%{ 'label' }]], [[
364L1:C3: expected at least one label after '{'
365%{ 'label' }
366 ^
367]])
368
369-- testing ExpLab2
370
371testerror([['p' /{1,2,3,} 'q']], [[
372L1:C13: expected a label after the comma
373'p' /{1,2,3,} 'q'
374 ^
375]])
376
377testerror([[%{ a,,b,,c }]], [[
378L1:C6: expected a label after the comma
379%{ a,,b,,c }
380 ^
381]])
382
383-- testing ExpNameOrLab
384
385testerror([[% s]], [[
386L1:C2: expected a name or label after '%' (no space)
387% s
388 ^
389]])
390
391testerror([[% {1}]], [[
392L1:C2: expected a name or label after '%' (no space)
393% {1}
394 ^
395]])
396
397-- testing ExpItem
398
399testerror([[
400"p" [
401abc
402] "q"
403]], [[
404L1:C6: expected at least one item after '[' or '^'
405"p" [
406 ^
407]])
408
409-- testing MisClose1
410
411testerror([[('p' ('q' / 'r')]], [[
412L1:C17: missing closing ')'
413('p' ('q' / 'r')
414 ^
415]])
416
417-- testing MisClose2
418
419-- two errors are reported due to the ignore strategy
420testerror([[{: group: 'p' :}]], [[
421L1:C9: missing closing ':}'
422{: group: 'p' :}
423 ^
424L1:C9: unexpected characters after the pattern
425{: group: 'p' :}
426 ^
427]])
428
429testerror([[S <- {: 'p' T <- 'q']], [[
430L1:C12: missing closing ':}'
431S <- {: 'p' T <- 'q'
432 ^
433]])
434
435-- testing MisClose3
436
437testerror([['p' {~ ('q' 'r') / 's']], [[
438L1:C23: missing closing '~}'
439'p' {~ ('q' 'r') / 's'
440 ^
441]])
442
443-- testing MisClose4
444
445-- two errors are reported due to the ignore strategy
446testerror([['p' {| 'q' / 'r' }]], [[
447L1:C17: missing closing '|}'
448'p' {| 'q' / 'r' }
449 ^
450L1:C18: unexpected characters after the pattern
451'p' {| 'q' / 'r' }
452 ^
453]])
454
455-- testing MisClose5
456
457testerror([[{ 'p' ]], [[
458L1:C6: missing closing '}'
459{ 'p'
460 ^
461]])
462
463-- testing MisClose6
464
465testerror([[<patt]], [[
466L1:C6: missing closing '>'
467<patt
468 ^
469]])
470
471testerror([[<insert your name here>]], [[
472L1:C8: missing closing '>'
473<insert your name here>
474 ^
475]])
476
477-- testing MisClose7
478
479testerror([['{' %{ a, b '}']], [[
480L1:C12: missing closing '}'
481'{' %{ a, b '}'
482 ^
483]])
484
485-- testing MisClose8
486
487testerror([[[]], [[
488L1:C1: missing closing ']'
489[
490^
491]])
492
493testerror([[[^]], [[
494L1:C1: missing closing ']'
495[^
496^
497]])
498
499testerror([[[] ]], [[
500L1:C1: missing closing ']'
501[]
502^
503]])
504
505testerror([[[^] ]], [[
506L1:C1: missing closing ']'
507[^]
508^
509]])
510
511testerror([[[_-___-_|]], [[
512L1:C1: missing closing ']'
513[_-___-_|
514^
515]])
516
517-- testing MisTerm1
518
519testerror([['That is the question...]], [[
520L1:C1: missing terminating single quote
521'That is the question...
522^
523]])
524
525-- testing MisTerm2
526
527testerror([[Q <- "To be or not to be...]], [[
528L1:C6: missing terminating double quote
529Q <- "To be or not to be...
530 ^
531]])
532
533-- testing error recovery, more complex grammars (multiline),
534-- and pointer positions in error recovery
535
536testerror([[&'p'/&/!/'p'^'q']], [[
537L1:C7: expected a pattern after '&'
538&'p'/&/!/'p'^'q'
539 ^
540L1:C9: expected a pattern after '!'
541&'p'/&/!/'p'^'q'
542 ^
543L1:C14: expected a number after '^', '+' or '-' (no space)
544&'p'/&/!/'p'^'q'
545 ^
546]])
547
548testerror([[
549 A <- 'a' (B 'b'
550 B <- 'x' / !
551 C <- 'c'
552]], [[
553L1:C18: missing closing ')'
554 A <- 'a' (B 'b'
555 ^
556L2:C15: expected a pattern after '!'
557 B <- 'x' / !
558 ^
559]])
560
561testerror([['a' / &@ ('c' / 'd')]], [[
562L1:C8: expected a pattern after '&'
563'a' / &@ ('c' / 'd')
564 ^
565]])
566
567testerror([['x' / & / 'y']], [[
568L1:C8: expected a pattern after '&'
569'x' / & / 'y'
570 ^
571]])
572
573testerror([[&/'p'/!/'q']], [[
574L1:C2: expected a pattern after '&'
575&/'p'/!/'q'
576 ^
577L1:C8: expected a pattern after '!'
578&/'p'/!/'q'
579 ^
580]])
581
582testerror([['p'//'q']], [[
583L1:C5: expected a pattern after '/' or the label(s)
584'p'//'q'
585 ^
586]])
587
588testerror([[
589 S <- 'forgot to close / T
590 T <- 'T' & / 't'
591]], [[
592L1:C8: missing terminating single quote
593 S <- 'forgot to close / T
594 ^
595L2:C13: expected a pattern after '&'
596 T <- 'T' & / 't'
597 ^
598]])
599
600testerror([[
601 S <- [a-z / T
602 T <- 'x' / & / 'y'
603]], [[
604L1:C8: missing closing ']'
605 S <- [a-z / T
606 ^
607L2:C15: expected a pattern after '&'
608 T <- 'x' / & / 'y'
609 ^
610]])
611
612testerror([[
613 S <- ('p' -- comment
614]], [[
615L1:C12: missing closing ')'
616 S <- ('p' -- comment
617 ^
618]])
619
620-- an unfortunate second error exists because we don't know
621-- what's part of the quotation
622testerror([[
623 X <- ('p / Q (R
624 / S))
625 Q <- 'q'
626 R <- 'r'
627 S <- 's'
628]], [[
629L1:C9: missing terminating single quote
630 X <- ('p / Q (R
631 ^
632L2:C9: unexpected characters after the pattern
633 / S))
634 ^
635]])
636
637testerror([[
638 A <- 'A' /{'lab'} B / !
639
640 B <- %{1, 2 3} 'b' / '6' & / 'B'
641
642 C <- A^B
643]], [[
644L1:C14: expected at least one label after '{'
645 A <- 'A' /{'lab'} B / !
646 ^
647L1:C26: expected a pattern after '!'
648 A <- 'A' /{'lab'} B / !
649 ^
650L3:C15: missing closing '}'
651 B <- %{1, 2 3} 'b' / '6' & / 'B'
652 ^
653L3:C29: expected a pattern after '&'
654 B <- %{1, 2 3} 'b' / '6' & / 'B'
655 ^
656L5:C10: expected a number after '^', '+' or '-' (no space)
657 C <- A^B
658 ^
659]])
660
661testerror([['p'/{1/'q'/&]], [[
662L1:C7: missing closing '}'
663'p'/{1/'q'/&
664 ^
665L1:C13: expected a pattern after '&'
666'p'/{1/'q'/&
667 ^
668]])
669
670-- testing non-syntax errors
671
672testerror([[
673 A <- %nosuch %def
674 A <- 'A again'
675 A <- 'and again'
676]], [[
677name 'nosuch' undefined
678]])
679
680testerror([[names not in grammar]], [[
681rule 'names' used outside a grammar
682]])
683
684testerror([[
685 A <- %nosuch %def
686 A <- 'A again'
687 A <- 'and again'
688]], [[
689name 'nosuch' undefined
690]])
691
692-- the non-syntax error should not be reported
693-- since there is a syntax error
694testerror([[ A <- %nosuch ('error' ]], [[
695L1:C23: missing closing ')'
696 A <- %nosuch ('error'
697 ^
698]])
699
700
701print 'OK'