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