aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergio Queiroz <sqmedeiros@gmail.com>2016-11-14 17:15:27 -0300
committerSergio Queiroz <sqmedeiros@gmail.com>2016-11-14 17:15:27 -0300
commit448762908fd822fbc101a4fe66fac9cd8aa913b5 (patch)
treec7bc865aa66f557be6d3d9d422bcaec42a8be0be
parentfd28f9d9e54f33bf7ae3a5e12dc71478f9c91aea (diff)
downloadlpeglabel-448762908fd822fbc101a4fe66fac9cd8aa913b5.tar.gz
lpeglabel-448762908fd822fbc101a4fe66fac9cd8aa913b5.tar.bz2
lpeglabel-448762908fd822fbc101a4fe66fac9cd8aa913b5.zip
Changing documentation and examples with recovery
-rw-r--r--README.md296
-rw-r--r--examples/listId1.lua12
-rw-r--r--examples/listId2.lua13
-rw-r--r--examples/listId2Rec2.lua67
-rw-r--r--lptree.c2
-rw-r--r--lpvm.c8
6 files changed, 329 insertions, 69 deletions
diff --git a/README.md b/README.md
index b882968..fe7c14d 100644
--- a/README.md
+++ b/README.md
@@ -10,43 +10,47 @@ LPegLabel is a conservative extension of the
10[LPeg](http://www.inf.puc-rio.br/~roberto/lpeg) 10[LPeg](http://www.inf.puc-rio.br/~roberto/lpeg)
11library that provides an implementation of Parsing 11library that provides an implementation of Parsing
12Expression Grammars (PEGs) with labeled failures. 12Expression Grammars (PEGs) with labeled failures.
13Labels can be used to signal different kinds of erros 13Labels can be used to signal different kinds of errors
14and to specify which alternative in a labeled ordered 14and to specify which recovery pattern should handle a
15choice should handle a given label. Labels can also be 15given label. Labels can also be combined with the standard
16combined with the standard patterns of LPeg. 16patterns of LPeg.
17 17
18This document describes the new functions available 18This document describes the new functions available
19in LpegLabel and presents some examples of usage. 19in LpegLabel and presents some examples of usage.
20For a more detailed discussion about PEGs with labeled failures
21please see [A Parsing Machine for Parsing Expression
22Grammars with Labeled Failures](https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxzcW1lZGVpcm9zfGd4OjMzZmE3YzM0Y2E2MGM5Y2M).
23
24 20
25In LPegLabel, the result of an unsuccessful matching 21In LPegLabel, the result of an unsuccessful matching
26is a triple **nil, lab, sfail**, where **lab** 22is a triple **nil, lab, sfail**, where **lab**
27is the label associated with the failure, and 23is the label associated with the failure, and
28**sfail** is the suffix input being matched when 24**sfail** is the suffix input being matched when
29**lab** was thrown. Below there is a brief summary 25**lab** was thrown.
26
27With labeled failures it is possible to distinguish
28between a regular failure and an error. Usually, a
29regular failure is produced when the matching of a
30character fails, and it is caught by an ordered choice.
31An error, by its turn, is produced by the throw operator
32and may be caught by the recovery operator.
33
34Below there is a brief summary
30of the new functions provided by LpegLabel: 35of the new functions provided by LpegLabel:
31 36
32<table border="1"> 37<table border="1">
33<tbody><tr><td><b>Function</b></td><td><b>Description</b></td></tr> 38<tbody><tr><td><b>Function</b></td><td><b>Description</b></td></tr>
34<tr><td><a href="#f-t"><code>lpeglabel.T (l)</code></a></td> 39<tr><td><a href="#f-t"><code>lpeglabelrec.T (l)</code></a></td>
35 <td>Throws label <code>l</code></td></tr> 40 <td>Throws a label <code>l</code> to signal an error</td></tr>
36<tr><td><a href="#f-lc"><code>lpeglabel.Lc (p1, p2, l1, ..., ln)</code></a></td> 41<tr><td><a href="#f-rec"><code>lpeglabelrec.Rec (p1, p2, l1, [l2, ..., ln])</code></a></td>
37 <td>Matches <code>p1</code> and tries to match <code>p2</code> 42 <td>Specifies a recovery pattern <code>p2</code> for <code>p1</code>,
38 if the matching of <code>p1</code> gives one of l<sub>1</sub>, ..., l<sub>n</sub> 43 when the matching of <code>p1</code> gives one of the labels l1, ..., ln.</td></tr>
39 </td></tr>
40<tr><td><a href="#re-t"><code>%{l}</code></a></td> 44<tr><td><a href="#re-t"><code>%{l}</code></a></td>
41 <td>Syntax of <em>relabel</em> module. Equivalent to <code>lpeg.T(l)</code> 45 <td>Syntax of <em>relabelrec</em> module. Equivalent to <code>lpeglabelrec.T(l)</code>
42 </td></tr> 46 </td></tr>
43<tr><td><a href="#re-lc"><code>p1 /{l1, ..., ln} p2</code></a></td> 47<tr><td><a href="#re-rec"><code>p1 //{l1, ..., ln} p2</code></a></td>
44 <td>Syntax of <em>relabel</em> module. Equivalent to <code>lpeg.Lc(p1, p2, l1, ..., ln)</code> 48 <td>Syntax of <em>relabelrec</em> module. Equivalent to <code>lpeglabelrec.Rec(p1, p2, l1, ..., ln)</code>
45 </td></tr> 49 </td></tr>
46<tr><td><a href="#re-line"><code>relabel.calcline(subject, i)</code></a></td> 50<tr><td><a href="#re-line"><code>relabelrec.calcline(subject, i)</code></a></td>
47 <td>Calculates line and column information regarding position <i>i</i> of the subject</code> 51 <td>Calculates line and column information regarding position <i>i</i> of the subject</code>
48 </td></tr> 52 </td></tr>
49<tr><td><a href="#re-setl"><code>relabel.setlabels (tlabel)</code></a></td> 53<tr><td><a href="#re-setl"><code>relabelrec.setlabels (tlabel)</code></a></td>
50 <td>Allows to specicify a table with mnemonic labels. 54 <td>Allows to specicify a table with mnemonic labels.
51 </td></tr> 55 </td></tr>
52</tbody></table> 56</tbody></table>
@@ -55,55 +59,44 @@ of the new functions provided by LpegLabel:
55### Functions 59### Functions
56 60
57 61
58#### <a name="f-t"></a><code>lpeglabel.T(l)</code> 62#### <a name="f-t"></a><code>lpeglabelrec.T(l)</code>
59 63
60 64
61Returns a pattern that throws the label `l`. 65Returns a pattern that throws the label `l`.
62A label must be an integer between 0 and 255. 66A label must be an integer between 1 and 255.
63
64The label 0 is equivalent to the regular failure of PEGs.
65 67
66 68
67#### <a name="f-lc"></a><code>lpeglabel.Lc(p1, p2, l1, ..., ln)</code># 69#### <a name="f-rec"></a><code>lpeglabelrec.Rec(p1, p2, l1, ..., ln)</code>
68 70
69Returns a pattern equivalent to a *labeled ordered choice*. 71Returns a *recovery pattern*.
70If the matching of `p1` gives one of the labels `l1, ..., ln`, 72If the matching of `p1` gives one of the labels `l1, ..., ln`,
71then the matching of `p2` is tried from the same position. Otherwise, 73then the matching of `p2` is tried from the failure position of `p1`.
72the result of the matching of `p1` is the pattern's result. 74Otherwise, the result of the matching of `p1` is the pattern's result.
73 75
74The labeled ordered choice `lpeg.Lc(p1, p2, 0)` is equivalent to the
75regular ordered choice `p1 / p2`.
76
77Although PEG's ordered choice is associative, the labeled ordered choice is not.
78When using this function, the user should take care to build a left-associative
79labeled ordered choice pattern.
80 76
81 77
82#### <a name="re-t"></a><code>%{l}</code> 78#### <a name="re-t"></a><code>%{l}</code>
83 79
84Syntax of *relabel* module. Equivalent to `lpeg.T(l)`. 80Syntax of *relabelrec* module. Equivalent to `lpeg.T(l)`.
85 81
86 82
87#### <a name="re-lc"></a><code>p1 /{l1, ..., ln} p2</code> 83#### <a name="re-lc"></a><code>p1 //{l1, ..., ln} p2</code>
88 84
89Syntax of *relabel* module. Equivalent to `lpeg.Lc(p1, p2, l1, ..., ln)`. 85Syntax of *relabelrec* module. Equivalent to `lpeglabelrec.Rec(p1, p2, l1, ..., ln)`.
90 86
91The `/{}` operator is left-associative. 87The `//{}` operator is left-associative.
92 88
93A grammar can use both choice operators (`/` and `/{}`),
94but a single choice can not mix them. That is, the parser of `relabel`
95module will not recognize a pattern as `p1 / p2 /{l1} p3`.
96 89
97 90
98#### <a name="re-line"></a><code>relabel.calcline (subject, i)</code> 91#### <a name="re-line"></a><code>relabelrec.calcline (subject, i)</code>
99 92
100Returns line and column information regarding position <i>i</i> of the subject. 93Returns line and column information regarding position <i>i</i> of the subject.
101 94
102 95
103#### <a name="re-setl"></a><code>relabel.setlabels (tlabel)</code> 96#### <a name="re-setl"></a><code>relabelrec.setlabels (tlabel)</code>
104 97
105Allows to specicify a table with labels. They keys of 98Allows to specicify a table with labels. They keys of
106`tlabel` must be integers between 0 and 255, 99`tlabel` must be integers between 1 and 255,
107and the associated values should be strings. 100and the associated values should be strings.
108 101
109 102
@@ -122,8 +115,8 @@ is thrown when there is an error matching an identifier
122or a comma: 115or a comma:
123 116
124```lua 117```lua
125local m = require'lpeglabel' 118local m = require'lpeglabelrec'
126local re = require'relabel' 119local re = require'relabelrec'
127 120
128local g = m.P{ 121local g = m.P{
129 "S", 122 "S",
@@ -160,7 +153,7 @@ In this example we could think about writing rule <em>List</em> as follows:
160List = ((m.V"Comma" + m.T(2)) * (m.V"Id" + m.T(1)))^0, 153List = ((m.V"Comma" + m.T(2)) * (m.V"Id" + m.T(1)))^0,
161``` 154```
162 155
163but when matching this expression agains the end of input 156but when matching this expression against the end of input
164we would get a failure whose associated label would be **2**, 157we would get a failure whose associated label would be **2**,
165and this would cause the failure of the *whole* repetition. 158and this would cause the failure of the *whole* repetition.
166 159
@@ -168,12 +161,12 @@ and this would cause the failure of the *whole* repetition.
168##### Mnemonics instead of numbers 161##### Mnemonics instead of numbers
169 162
170In the previous example we could have created a table 163In the previous example we could have created a table
171with the error messages to improve the readbility of the PEG. 164with the error messages to improve the readability of the PEG.
172Below we rewrite the previous grammar following this approach: 165Below we rewrite the previous grammar following this approach:
173 166
174```lua 167```lua
175local m = require'lpeglabel' 168local m = require'lpeglabelrec'
176local re = require'relabel' 169local re = require'relabelrec'
177 170
178local terror = {} 171local terror = {}
179 172
@@ -210,14 +203,99 @@ print(mymatch(g, "one two")) --> nil Error at line 1 (col 3): expec
210print(mymatch(g, "one,\n two,\nthree,")) --> nil Error at line 3 (col 6): expecting an identifier before '' 203print(mymatch(g, "one,\n two,\nthree,")) --> nil Error at line 3 (col 6): expecting an identifier before ''
211``` 204```
212 205
206#### Error Recovery
207
208By using the recovery operator we can specify a recovery pattern that
209should be matched when a label is thrown. After matching this pattern,
210and possibly recording the error, the parser can continue parsing to
211find more errors.
212
213Below we rewrite the previous example to illustrate a recovery strategy.
214Grammar `g` remains the same, but we add a recovery grammar `grec` that
215handles the labels thrown by `g`.
216
217arithmetic expression example and modify
218the `expect` function to use the recovery operator for error recovery:
219
220```lua
221local m = require'lpeglabelrec'
222local re = require'relabelrec'
223
224local terror = {}
225
226local function newError(s)
227 table.insert(terror, s)
228 return #terror
229end
230
231local errUndef = newError("undefined")
232local errId = newError("expecting an identifier")
233local errComma = newError("expecting ','")
234
235local id = m.R'az'^1
236
237local g = m.P{
238 "S",
239 S = m.V"Id" * m.V"List",
240 List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
241 Id = m.V"Sp" * id + m.T(errId),
242 Comma = m.V"Sp" * "," + m.T(errComma),
243 Sp = m.S" \n\t"^0,
244}
245
246local subject, errors
247
248function recorderror(pos, lab)
249 local line, col = re.calcline(subject, pos)
250 table.insert(errors, { line = line, col = col, msg = terror[lab] })
251end
252
253function record (lab)
254 return (m.Cp() * m.Cc(lab)) / recorderror
255end
256
257function sync (p)
258 return (-p * m.P(1))^0
259end
260
261local grec = m.P{
262 "S",
263 S = m.Rec(m.Rec(g, m.V"ErrComma", errComma), m.V"ErrId", errId),
264 ErrComma = record(errComma) * sync(-m.P(1) + id),
265 ErrId = record(errId) * sync(-m.P(1) + ",")
266}
267
268
269function mymatch (g, s)
270 errors = {}
271 subject = s
272 local r, e, sfail = g:match(s)
273 if #errors > 0 then
274 local out = {}
275 for i, err in ipairs(errors) do
276 local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg
277 table.insert(out, msg)
278 end
279 return nil, table.concat(out, "\n")
280 end
281 return r
282end
283
284print(mymatch(grec, "one,two"))
285print(mymatch(grec, "one two three"))
286print(mymatch(grec, "1,\n two, \n3,"))
287print(mymatch(grec, "one\n two123, \nthree,"))
288```
289
213 290
214##### *relabel* syntax 291
292##### *relabelrec* syntax
215 293
216Now we rewrite the previous example using the syntax 294Now we rewrite the previous example using the syntax
217supported by *relabel*: 295supported by *relabelrec*:
218 296
219```lua 297```lua
220local re = require 'relabel' 298local re = require 'relabelrec'
221 299
222local g = re.compile[[ 300local g = re.compile[[
223 S <- Id List 301 S <- Id List
@@ -252,7 +330,7 @@ With the help of function *setlabels* we can also rewrite the previous example t
252mnemonic labels instead of plain numbers: 330mnemonic labels instead of plain numbers:
253 331
254```lua 332```lua
255local re = require 'relabel' 333local re = require 'relabelrec'
256 334
257local errinfo = { 335local errinfo = {
258 {"errUndef", "undefined"}, 336 {"errUndef", "undefined"},
@@ -293,6 +371,11 @@ print(mymatch(g, "one two")) --> nil Error at line 1 (col 3): expec
293print(mymatch(g, "one,\n two,\nthree,")) --> nil Error at line 3 (col 6): expecting an identifier before '' 371print(mymatch(g, "one,\n two,\nthree,")) --> nil Error at line 3 (col 6): expecting an identifier before ''
294``` 372```
295 373
374
375
376
377
378
296#### Arithmetic Expressions 379#### Arithmetic Expressions
297 380
298Here's an example of an LPegLabel grammar that make its own function called 381Here's an example of an LPegLabel grammar that make its own function called
@@ -420,3 +503,108 @@ print(m.match(g, "one,two")) --> 8
420print(m.match(g, "one two")) --> expecting ',' 503print(m.match(g, "one two")) --> expecting ','
421print(m.match(g, "one,\n two,\nthree,")) --> expecting an identifier 504print(m.match(g, "one,\n two,\nthree,")) --> expecting an identifier
422``` 505```
506
507#### Error Recovery
508
509By using labeled ordered choice or the recovery operator, when a label
510is thrown, the parser may record the error and still continue parsing
511to find more errors. We can even record the error right away without
512actually throwing a label (relying on the regular PEG failure instead).
513Below we rewrite the arithmetic expression example and modify
514the `expect` function to use the recovery operator for error recovery:
515
516```lua
517local lpeg = require"lpeglabel"
518
519local R, S, P, V = lpeg.R, lpeg.S, lpeg.P, lpeg.V
520local C, Cc, Ct, Cmt, Carg = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cmt, lpeg.Carg
521local T, Lc, Rec = lpeg.T, lpeg.Lc, lpeg.Rec
522
523local labels = {
524 {"NoExp", "no expression found"},
525 {"Extra", "extra characters found after the expression"},
526 {"ExpTerm", "expected a term after the operator"},
527 {"ExpExp", "expected an expression after the parenthesis"},
528 {"MisClose", "missing a closing ')' after the expression"},
529}
530
531local function labelindex(labname)
532 for i, elem in ipairs(labels) do
533 if elem[1] == labname then
534 return i
535 end
536 end
537 error("could not find label: " .. labname)
538end
539
540local function expect(patt, labname, recpatt)
541 local i = labelindex(labname)
542 local function recorderror(input, pos, errors)
543 table.insert(errors, {i, pos})
544 return true
545 end
546 if not recpatt then recpatt = P"" end
547 return Rec(patt, Cmt(Carg(1), recorderror) * recpatt)
548end
549
550local num = R("09")^1 / tonumber
551local op = S("+-*/")
552
553local function compute(tokens)
554 local result = tokens[1]
555 for i = 2, #tokens, 2 do
556 if tokens[i] == '+' then
557 result = result + tokens[i+1]
558 elseif tokens[i] == '-' then
559 result = result - tokens[i+1]
560 elseif tokens[i] == '*' then
561 result = result * tokens[i+1]
562 elseif tokens[i] == '/' then
563 result = result / tokens[i+1]
564 else
565 error('unknown operation: ' .. tokens[i])
566 end
567 end
568 return result
569end
570
571
572local g = P {
573 "Exp",
574 Exp = Ct(V"Term" * (C(op) * V"Operand")^0) / compute;
575 Operand = expect(V"Term", "ExpTerm", Cc(0));
576 Term = num + V"Group";
577 Group = "(" * V"InnerExp" * expect(")", "MisClose");
578 InnerExp = expect(V"Exp", "ExpExp", (P(1) - ")")^0 * Cc(0));
579}
580
581g = expect(g, "NoExp", P(1)^0) * expect(-P(1), "Extra")
582
583local function eval(input)
584 local errors = {}
585 local result, label, suffix = g:match(input, 1, errors)
586 if #errors == 0 then
587 return result
588 else
589 local out = {}
590 for i, err in ipairs(errors) do
591 local pos = err[2]
592 local msg = labels[err[1]][2]
593 table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")")
594 end
595 return nil, table.concat(out, "\n")
596 end
597end
598
599print(eval "98-76*(54/32)")
600--> 37.125
601
602print(eval "-1+(1-(1*2))/2")
603--> syntax error: no expression found (at index 1)
604
605print(eval "(1+1-1*(2/2+)-():")
606--> syntax error: expected a term after the operator (at index 13)
607--> syntax error: expected an expression after the parenthesis (at index 16)
608--> syntax error: missing a closing ')' after the expression (at index 17)
609--> syntax error: extra characters found after the expression (at index 17)
610```
diff --git a/examples/listId1.lua b/examples/listId1.lua
index 8976f5f..dee46e9 100644
--- a/examples/listId1.lua
+++ b/examples/listId1.lua
@@ -1,12 +1,14 @@
1local m = require'lpeglabel' 1local m = require'lpeglabelrec'
2local re = require'relabel' 2local re = require'relabelrec'
3
4local id = m.R'az'^1
3 5
4local g = m.P{ 6local g = m.P{
5 "S", 7 "S",
6 S = m.V"Id" * m.V"List", 8 S = m.V"Id" * m.V"List",
7 List = -m.P(1) + (m.V"Comma" + m.T(2)) * (m.V"Id" + m.T(1)) * m.V"List", 9 List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
8 Id = m.V"Sp" * m.R'az'^1, 10 Id = m.V"Sp" * id + m.T(1),
9 Comma = m.V"Sp" * ",", 11 Comma = m.V"Sp" * "," + m.T(2),
10 Sp = m.S" \n\t"^0, 12 Sp = m.S" \n\t"^0,
11} 13}
12 14
diff --git a/examples/listId2.lua b/examples/listId2.lua
index 509fda4..46f0063 100644
--- a/examples/listId2.lua
+++ b/examples/listId2.lua
@@ -1,5 +1,5 @@
1local m = require'lpeglabel' 1local m = require'lpeglabelrec'
2local re = require'relabel' 2local re = require'relabelrec'
3 3
4local terror = {} 4local terror = {}
5 5
@@ -12,15 +12,18 @@ local errUndef = newError("undefined")
12local errId = newError("expecting an identifier") 12local errId = newError("expecting an identifier")
13local errComma = newError("expecting ','") 13local errComma = newError("expecting ','")
14 14
15local id = m.R'az'^1
16
15local g = m.P{ 17local g = m.P{
16 "S", 18 "S",
17 S = m.V"Id" * m.V"List", 19 S = m.V"Id" * m.V"List",
18 List = -m.P(1) + (m.V"Comma" + m.T(errComma)) * (m.V"Id" + m.T(errId)) * m.V"List", 20 List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
19 Id = m.V"Sp" * m.R'az'^1, 21 Id = m.V"Sp" * id + m.T(errId),
20 Comma = m.V"Sp" * ",", 22 Comma = m.V"Sp" * "," + m.T(errComma),
21 Sp = m.S" \n\t"^0, 23 Sp = m.S" \n\t"^0,
22} 24}
23 25
26
24function mymatch (g, s) 27function mymatch (g, s)
25 local r, e, sfail = g:match(s) 28 local r, e, sfail = g:match(s)
26 if not r then 29 if not r then
diff --git a/examples/listId2Rec2.lua b/examples/listId2Rec2.lua
new file mode 100644
index 0000000..a347a5b
--- /dev/null
+++ b/examples/listId2Rec2.lua
@@ -0,0 +1,67 @@
1local m = require'lpeglabelrec'
2local re = require'relabelrec'
3
4local terror = {}
5
6local function newError(s)
7 table.insert(terror, s)
8 return #terror
9end
10
11local errUndef = newError("undefined")
12local errId = newError("expecting an identifier")
13local errComma = newError("expecting ','")
14
15local id = m.R'az'^1
16
17local g = m.P{
18 "S",
19 S = m.V"Id" * m.V"List",
20 List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
21 Id = m.V"Sp" * id + m.T(errId),
22 Comma = m.V"Sp" * "," + m.T(errComma),
23 Sp = m.S" \n\t"^0,
24}
25
26local subject, errors
27
28function recorderror(pos, lab)
29 local line, col = re.calcline(subject, pos)
30 table.insert(errors, { line = line, col = col, msg = terror[lab] })
31end
32
33function record (lab)
34 return (m.Cp() * m.Cc(lab)) / recorderror
35end
36
37function sync (p)
38 return (-p * m.P(1))^0
39end
40
41local grec = m.P{
42 "S",
43 S = m.Rec(m.Rec(g, m.V"ErrComma", errComma), m.V"ErrId", errId),
44 ErrComma = record(errComma) * sync(-m.P(1) + id),
45 ErrId = record(errId) * sync(-m.P(1) + ",")
46}
47
48
49function mymatch (g, s)
50 errors = {}
51 subject = s
52 local r, e, sfail = g:match(s)
53 if #errors > 0 then
54 local out = {}
55 for i, err in ipairs(errors) do
56 local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg
57 table.insert(out, msg)
58 end
59 return nil, table.concat(out, "\n")
60 end
61 return r
62end
63
64print(mymatch(grec, "one,two"))
65print(mymatch(grec, "one two three"))
66print(mymatch(grec, "1,\n two, \n3,"))
67print(mymatch(grec, "one\n two123, \nthree,"))
diff --git a/lptree.c b/lptree.c
index 9861cfe..e15198e 100644
--- a/lptree.c
+++ b/lptree.c
@@ -28,7 +28,7 @@ const byte numsiblings[] = {
28 0, 0, 2, 1, /* call, opencall, rule, grammar */ 28 0, 0, 2, 1, /* call, opencall, rule, grammar */
29 1, /* behind */ 29 1, /* behind */
30 1, 1, /* capture, runtime capture */ 30 1, 1, /* capture, runtime capture */
31 0, 2, 2 /* labeled failure throw, labeled choice, recovery */ 31 0, 2 /* labeled failure throw, recovery */
32}; 32};
33 33
34 34
diff --git a/lpvm.c b/lpvm.c
index 122c8f4..a934478 100644
--- a/lpvm.c
+++ b/lpvm.c
@@ -336,8 +336,8 @@ const char *match (lua_State *L, const char *o, const char *s, const char *e,
336 do { /* remove pending calls */ 336 do { /* remove pending calls */
337 assert(pstack > getstackbase(L, ptop)); 337 assert(pstack > getstackbase(L, ptop));
338 auxlab = (--pstack)->ls; 338 auxlab = (--pstack)->ls;
339 } while (auxlab == NULL || (pstack->p != &giveup && labelf != LFAIL && !testlabel(pstack->ls->cs, *labelf))); 339 } while (auxlab == NULL || !(pstack->p == &giveup || testlabel(pstack->ls->cs, *labelf)));
340 if (pstack->p == &giveup || pstack->s != NULL) { /* labeled failure: giveup or backtrack frame */ 340 if (pstack->s != NULL) { /* labeled failure: giveup or backtrack frame */
341 stack = pstack; 341 stack = pstack;
342 s = stack->s; 342 s = stack->s;
343 if (ndyncap > 0) /* is there matchtime captures? */ 343 if (ndyncap > 0) /* is there matchtime captures? */
@@ -349,7 +349,7 @@ const char *match (lua_State *L, const char *o, const char *s, const char *e,
349 stack->s = NULL; 349 stack->s = NULL;
350 stack->p = pk; /* save return address */ 350 stack->p = pk; /* save return address */
351 stack->ls = NULL; 351 stack->ls = NULL;
352 stack->caplevel = captop; /* TODO: necessary?? */ 352 stack->caplevel = captop; /* TODO: really necessary?? */
353 stack++; 353 stack++;
354 } 354 }
355 p = pstack->p; 355 p = pstack->p;
@@ -366,7 +366,7 @@ const char *match (lua_State *L, const char *o, const char *s, const char *e,
366 res = resdyncaptures(L, fr, s - o, e - o); /* get result */ 366 res = resdyncaptures(L, fr, s - o, e - o); /* get result */
367 if (res == -1) { /* fail? */ 367 if (res == -1) { /* fail? */
368 *labelf = LFAIL; /* labeled failure */ 368 *labelf = LFAIL; /* labeled failure */
369 *sfail = (const char *) s; /* TODO: ??? */ 369 *sfail = (const char *) s;
370 pk = NULL; 370 pk = NULL;
371 goto fail; 371 goto fail;
372 } 372 }