aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md669
-rw-r--r--examples/expect.lua (renamed from examples/expect.md)7
-rw-r--r--examples/listId1.lua45
-rw-r--r--examples/listId2.lua42
-rw-r--r--examples/listIdCatch.lua48
-rw-r--r--examples/listIdRe1.lua30
-rw-r--r--examples/listIdRe2.lua49
7 files changed, 301 insertions, 589 deletions
diff --git a/README.md b/README.md
index 25d9141..221f06d 100644
--- a/README.md
+++ b/README.md
@@ -1,588 +1,243 @@
1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 1### LPegLabel - Parsing Expression Grammars (with Labels) for Lua
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 2
3<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 3<center>
4<head> 4<img src="https://github.com/sqmedeiros/lpeglabel/raw/master/lpeglabel-logo.gif" alt="LPegLabel" style="width: 128px;"/>
5 <title>LPegLabLabel - Parsing Expression Grammars For Lua</title> 5</center>
6 <link rel="stylesheet" 6
7 href="http://www.inf.puc-rio.br/~roberto/lpeg/doc.css" 7---
8 type="text/css"/> 8
9 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 9#### Introduction
10</head> 10
11<body> 11LPegLabel is a conservative extension of the
12 12[LPeg](http://www.inf.puc-rio.br/~roberto/lpeg)
13<div id="container"> 13library that provides an implementation of Parsing
14 14Expression Grammars (PEGs) with labeled failures.
15<div id="product"> 15Labels can be used to signal different kinds of erros
16 <div id="product_logo"> 16and to specify which alternative in a labeled ordered
17 <a href="https://github.com/sqmedeiros/lpeglabel"> 17choice should handle a given label. Labels can also be
18 <img alt="LPegLabel logo" src="lpeglabel-logo.gif" width="128"/></a> 18combined with the standard patterns of LPeg.
19 19
20 </div>
21 <div id="product_name"><big><strong>LPegLabel</strong></big></div>
22 <div id="product_description">
23 Parsing Expression Grammars For Lua with Labels, version 0.1
24 </div>
25</div> <!-- id="product" -->
26
27<div id="main">
28
29<div id="navigation">
30<h1>LPeg</h1>
31
32<ul>
33 <li><strong>Home</strong>
34 <ul>
35 <li><a href="#intro">Introduction</a></li>
36 <li><a href="#func">Functions</a></li>
37 <li><a href="#ex">Some Examples</a></li>
38 <li><a href="#download">Download</a></li>
39 <li><a href="#license">License</a></li>
40 </ul>
41 </li>
42</ul>
43</div> <!-- id="navigation" -->
44
45<div id="content">
46
47
48<h2><a name="intro">Introduction</a></h2>
49
50<p>
51<em>LPegLabel</em> is an extension of the
52<a href="http://www.inf.puc-rio.br/~roberto/lpeg/">LPeg</a>
53library that provides an implementation of Parsing Expression
54Grammars (PEGs) with labeled failures. Labels can be
55used to signal different kinds of erros and to
56specify which alternative in a labeled ordered choice
57should handle a given label. Labels can also be combined
58with the standard patterns of LPeg.
59</p>
60
61<p>
62This document describes the new functions available 20This document describes the new functions available
63in LpegLabel and presents some examples of usage. 21in LpegLabel and presents some examples of usage.
22For a more detailed discussion about PEGs with labeled failures
23please see [A Parsing Machine for Parsing Expression
24Grammars with Labeled Failures](https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxzcW1lZGVpcm9zfGd4OjMzZmE3YzM0Y2E2MGM5Y2M).
25
26
64In LPegLabel, the result of an unsuccessful matching 27In LPegLabel, the result of an unsuccessful matching
65is a triple <code>nil, lab, sfail</code>, where <code>lab</code> 28is a triple **nil, lab, sfail**, where **lab**
66is the label associated with the failure, and 29is the label associated with the failure, and
67<code>sfail</code> is the suffix input where 30**sfail** is the suffix input being matched when
68the label was thrown. 31**lab** was thrown. Below there is a brief summary
69</p> 32of the new functions provided by LpegLabel:
70 33
71<p>
72Below there is a brief summary of the new functions
73provided by LpegLabel:
74</p>
75<table border="1"> 34<table border="1">
76<tbody><tr><td><b>Function</b></td><td><b>Description</b></td></tr> 35<tbody><tr><td><b>Function</b></td><td><b>Description</b></td></tr>
77<tr><td><a href="#f-t"><code>lpeglabel.T (l)</code></a></td> 36<tr><td><a href="#f-t"><code>lpeglabel.T (l)</code></a></td>
78 <td>Throws label <code>l</code></td></tr> 37 <td>Throws label <code>l</code></td></tr>
79<tr><td><a href="#f-lc"><code>lpeglabel.Lc (p1, p2, l<sub>1</sub>, ..., l<sub>n</sub>)</code></a></td> 38<tr><td><a href="#f-lc"><code>lpeglabel.Lc (p1, p2, l1, ..., ln)</code></a></td>
80 <td>Matches <code>p1</code> and tries to match <code>p2</code> 39 <td>Matches <code>p1</code> and tries to match <code>p2</code>
81 if the matching of <code>p1</code> gives one of l<sub>1</sub>, ..., l<sub>n</sub> 40 if the matching of <code>p1</code> gives one of l<sub>1</sub>, ..., l<sub>n</sub>
82 </td></tr> 41 </td></tr>
83<tr><td><a href="#re-t"><code>%{l}</code></a></td> 42<tr><td><a href="#re-t"><code>%{l}</code></a></td>
84 <td>Syntax of <em>relabel</em> module. Equivalent to <code>lpeg.T(l)</code> 43 <td>Syntax of <em>relabel</em> module. Equivalent to <code>lpeg.T(l)</code>
85 </td></tr> 44 </td></tr>
86<tr><td><a href="#re-lc"><code>p1 /{l<sub>1</sub>, ..., l<sub>n</sub>} p2</code></a></td> 45<tr><td><a href="#re-lc"><code>p1 /{l1, ..., ln} p2</code></a></td>
87 <td>Syntax of <em>relabel</em> module. Equivalent to <code>lpeg.Lc(p1, p2, l<sub>1</sub>, ..., l<sub>n</sub>)</code> 46 <td>Syntax of <em>relabel</em> module. Equivalent to <code>lpeg.Lc(p1, p2, l1, ..., ln)</code>
88 </td></tr> 47 </td></tr>
89<tr><td><a href="#re-setl"><code>relabel.setlabels (tlabel)</code></a></td> 48<tr><td><a href="#re-setl"><code>relabel.setlabels (tlabel)</code></a></td>
90 <td>Allows to specicify a table with mnemonic labels. 49 <td>Allows to specicify a table with mnemonic labels.
91 </td></tr> 50 </td></tr>
92</tbody></table> 51</tbody></table>
93 52
94<p> 53
95For a more detailed and formal discussion about 54#### Functions
96PEGs with labels please see 55
97<a href="http://www.inf.puc-rio.br/~roberto/docs/sblp2013-1.pdf"> 56
98Exception Handling for Error Reporting in Parsing Expression Grammars</a>, 57##### <a name="f-t"></a><code>lpeglabel.T(l)</code>
99<a href="http://arxiv.org/abs/1405.6646">Error Reporting in Parsing Expression Grammars</a>, 58
100and <a href="http://dx.doi.org/10.1145/2851613.2851750"> 59
101A parsing machine for parsing expression grammars with labeled failures</a>. 60Returns a pattern that throws the label `l`.
102</p> 61A label must be an integer between 0 and 63.
103 62
104<!-- 63The label 0 is equivalent to the regular failure of PEGs.
105<p> 64
106In case of an unsucessful matching, the <em>match</em> function returns 65
107<code>nil</code> plus a list of labels. These labels may be used to build 66#### <a name="f-lc"></a><code>lpeglabel.Lc(p1, p2, l1, ..., ln)</code>#
108a good error message. 67
109</p> 68Returns a pattern equivalent to a *labeled ordered choice*.
110--> 69If the matching of `p1` gives one of the labels `l1, ..., ln`,
111 70then the matching of `p2` is tried from the same position. Otherwise,
112<h2><a name="func">Functions</a></h2> 71the result of the matching of `p1` is the pattern's result.
113 72
114 73The labeled ordered choice `lpeg.Lc(p1, p2, 0)` is equivalent to the
115<h3><a name="f-t"></a><code>lpeglabel.T(l)</code></h3> 74regular ordered choice `p1 / p2`.
116<p> 75
117Returns a pattern that throws the label <code>l</code>.
118A label must be an integer between <code>0</code> and <code>63</code>.
119
120The label <code>0</code> is equivalent to the regular failure of PEGs.
121
122
123<h3><a name="f-lc"></a><code>lpeglabel.Lc(p1, p2, l<sub>1</sub>, ..., l<sub>n</sub>)</code></h3>
124<p>
125Returns a pattern equivalent to a <em>labeled ordered choice</em>.
126If the matching of <code>p1</code> gives one of the labels <code>l<sub>1</sub>, ..., l<sub>n</sub></code>,
127then the matching of <code>p2</code> is tried from the same position. Otherwise,
128the result of the matching of <code>p1</code> is the pattern's result.
129</p>
130
131<p>
132The labeled ordered choice <code>lpeg.Lc(p1, p2, 0)</code> is equivalent to the
133regular ordered choice <code>p1 / p2</code>.
134</p>
135
136<p>
137Although PEG's ordered choice is associative, the labeled ordered choice is not. 76Although PEG's ordered choice is associative, the labeled ordered choice is not.
138When using this function, the user should take care to build a left-associative 77When using this function, the user should take care to build a left-associative
139labeled ordered choice pattern. 78labeled ordered choice pattern.
140</p>
141 79
142 80
143<h3><a name="re-t"></a><code>%{l}</code></h3> 81#### <a name="re-t"></a><code>%{l}</code>
144<p> 82
145Syntax of <em>relabel</em> module. Equivalent to <code>lpeg.T(l)</code>. 83Syntax of *relabel* module. Equivalent to `lpeg.T(l)`.
146</p>
147 84
148 85
149<h3><a name="re-lc"></a><code>p1 /{l<sub>1</sub>, ..., l<sub>n</sub>} p2</code></h3> 86#### <a name="re-lc"></a><code>p1 /{l1, ..., ln} p2</code>
150<p>
151Syntax of <em>relabel</em> module. Equivalent to <code>lpeg.Lc(p1, p2, l<sub>1</sub>, ..., l<sub>n</sub>)</code>.
152</p>
153 87
154<p> 88Syntax of *relabel* module. Equivalent to `lpeg.Lc(p1, p2, l1, ..., ln)`.
155The <code>/{}</code> operator is left-associative.
156</p>
157 89
158<p> 90The `/{}` operator is left-associative.
159A grammar can use both choice operators (<code>/</code> and <code>/{}</code>),
160but a single choice can not mix them. That is, the parser
161of <code>relabel</code> module will not recognize a pattern as
162<code>p1 / p2 /{l<sub>1</sub>} p3</code>.
163</p>
164 91
92A grammar can use both choice operators (`/` and `/{}`),
93but a single choice can not mix them. That is, the parser of `relabel`
94module will not recognize a pattern as `p1 / p2 /{l1} p3`.
165 95
166<h3><a name="re-setl"></a><code>relabel.setlabels (tlabel)</code></h3>
167 96
168<p>Allows to specicify a table with labels. They keys of 97#### <a name="re-setl"></a><code>relabel.setlabels (tlabel)</code>
169<code>tlabel</code> must be integers between <code>0</code> and <code>63</code>, 98
99Allows to specicify a table with labels. They keys of
100`tlabel` must be integers between 0 and 63,
170and the associated values should be strings. 101and the associated values should be strings.
171</p>
172 102
173 103
104### Examples
174 105
175<h2><a name="ex">Some Examples</a></h2> 106#### Throwing a label
176 107
177<h3>Throwing a label</h3>
178<p>
179The following example defines a grammar that matches 108The following example defines a grammar that matches
180a list of identifiers separated by commas. A label 109a list of identifiers separated by commas. A label
181is thrown when there is an error matching an identifier 110is thrown when there is an error matching an identifier
182or a comma: 111or a comma:
183</p> 112
184<pre class="example"> 113```lua
185local m = require'lpeglabel' 114local m = require'lpeglabel'
186 115
116local function calcline (s, i)
117 if i == 1 then return 1, 1 end
118 local rest, line = s:sub(1,i):gsub("[^\n]*\n", "")
119 local col = #rest
120 return 1 + line, col ~= 0 and col or 1
121end
122
187local g = m.P{ 123local g = m.P{
188 "S", 124 "S",
189 S = m.V"Id" * m.V"List", 125 S = m.V"Id" * m.V"List",
190 List = -m.P(1) + ("," + m.T(2)) * m.V"Id" * m.V"List", 126 List = -m.P(1) + (m.V"Comma" + m.T(2)) * (m.V"Id" + m.T(1)) * m.V"List",
191 Id = m.R'az'^1 + m.T(1), 127 Id = m.V"Sp" * m.R'az'^1,
128 Comma = m.V"Sp" * ",",
129 Sp = m.S" \n\t"^0,
192} 130}
193 131
194function mymatch (g, s) 132function mymatch (g, s)
195 local r, e = g:match(s) 133 local r, e, sfail = g:match(s)
196 if not r then 134 if not r then
135 local line, col = calcline(s, #s - #sfail)
136 local msg = "Error at line " .. line .. " (col " .. col .. ")"
197 if e == 1 then 137 if e == 1 then
198 return "Error: expecting an identifier" 138 return r, msg .. ": expecting an identifier before '" .. sfail .. "'"
199 elseif e == 2 then 139 elseif e == 2 then
200 return "Error: expecting ','" 140 return r, msg .. ": expecting ',' before '" .. sfail .. "'"
201 else 141 else
202 return "Error" 142 return r, msg
203 end 143 end
204 end 144 end
205 return r 145 return r
206end 146end
207 147
208print(mymatch(g, "a,b")) 148print(mymatch(g, "one,two")) --> 8
209print(mymatch(g, "a b")) 149print(mymatch(g, "one two")) --> nil Error at line 1 (col 3): expecting ',' before ' two'
210print(mymatch(g, ", b")) 150print(mymatch(g, "one,\n two,\nthree,")) --> nil Error at line 3 (col 6): expecting an identifier before ''
211</pre> 151```
212<p>
213In this example we could think about writing rule <em>List</em> as follows:
214<pre class="example">
215List = m.P(("," + m.T(2)) * m.V"Id")^0
216</pre>
217but this would give us an expression that when matching
218the end of input would result in a failure whose associated
219label would be <em>2</em>.
220</p>
221
222<p>
223In the previous example we could have also created a table
224with the error messages to improve the readbility of the PEG.
225Below we rewrite the grammar following this approach:
226</p>
227
228<pre class="example">
229local m = require'lpeglabel'
230 152
231local errUndef = 0
232local errId = 1
233local errComma = 2
234 153
235local terror = { 154#### Arithmetic Expressions
236 [errUndef] = "Error",
237 [errId] = "Error: expecting an identifier",
238 [errComma] = "Error: expecting ','",
239}
240 155
241local g = m.P{ 156Here's an example of an LPegLabel grammar that make its own function called
242 "S", 157'expect', which takes a pattern and a label as parameters and throws the label
243 S = m.V"Id" * m.V"List", 158if the pattern fails to be matched. This function can be extended later on to
244 List = -m.P(1) + ("," + m.T(errComma)) * m.V"Id" * m.V"List", 159record all errors encountered once error recovery is implemented.
245 Id = m.R'az'^1 + m.T(errId), 160
161```lua
162local lpeg = require"lpeglabel"
163
164local R, S, P, V, C, Ct, T = lpeg.R, lpeg.S, lpeg.P, lpeg.V, lpeg.C, lpeg.Ct, lpeg.T
165
166local labels = {
167 {"NoExp", "no expression found"},
168 {"Extra", "extra characters found after the expression"},
169 {"ExpTerm", "expected a term after the operator"},
170 {"ExpExp", "expected an expression after the parenthesis"},
171 {"MisClose", "missing a closing ')' after the expression"},
246} 172}
247 173
248function mymatch (g, s) 174local function expect(patt, labname)
249 local r, e = g:match(s) 175 for i, elem in ipairs(labels) do
250 if not r then 176 if elem[1] == labname then
251 return terror[e] 177 return patt + T(i)
178 end
252 end 179 end
253 return r 180
181 error("could not find label: " .. labname)
254end 182end
255
256print(mymatch(g, "a,b"))
257print(mymatch(g, "a b"))
258print(mymatch(g, ", b"))
259</pre>
260
261<h3>Throwing a label using the <em>relabel</em> module</h3>
262
263<p>
264We can also rewrite the previous example using the <em>relabel</em> module
265as follows:
266</p>
267<pre class="example">
268local re = require 'relabel'
269
270local g = re.compile[[
271 S <- Id List
272 List <- !. / (',' / %{2}) Id List
273 Id <- [a-z] / %{1}
274]]
275 183
276function mymatch (g, s) 184local num = R("09")^1 / tonumber
277 local r, e = g:match(s) 185local op = S("+-*/")
278 if not r then 186
279 if e == 1 then 187local function compute(tokens)
280 return "Error: expecting an identifier" 188 local result = tokens[1]
281 elseif e == 2 then 189 for i = 2, #tokens, 2 do
282 return "Error: expecting ','" 190 if tokens[i] == '+' then
191 result = result + tokens[i+1]
192 elseif tokens[i] == '-' then
193 result = result - tokens[i+1]
194 elseif tokens[i] == '*' then
195 result = result * tokens[i+1]
196 elseif tokens[i] == '/' then
197 result = result / tokens[i+1]
283 else 198 else
284 return "Error" 199 error('unknown operation: ' .. tokens[i])
285 end 200 end
286 end 201 end
287 return r 202 return result
288end 203end
289
290print(mymatch(g, "a,b"))
291print(mymatch(g, "a b"))
292print(mymatch(g, ", b"))
293</pre>
294
295<p>
296Another way to describe the previous example using the <em>relabel</em> module
297is by using a table with the description of the errors (<em>terror</em>) and
298another table that associates a name to a given label (<em>tlabels</em>):
299</p>
300<pre class="example">
301local re = require 'relabel'
302
303local errUndef, errId, errComma = 0, 1, 2
304
305local terror = {
306 [errUndef] = "Error",
307 [errId] = "Error: expecting an identifier",
308 [errComma] = "Error: expecting ','",
309}
310 204
311local tlabels = { ["errUndef"] = errUndef, 205local g = P {
312 ["errId"] = errId, 206 "Exp",
313 ["errComma"] = errComma } 207 Exp = Ct(V"Term" * (C(op) * expect(V"Term", "ExpTerm"))^0) / compute;
314 208 Term = num + V"Group";
315re.setlabels(tlabels) 209 Group = "(" * expect(V"Exp", "ExpExp") * expect(")", "MisClose");
316
317local g = re.compile[[
318 S <- Id List
319 List <- !. / (',' / %{errComma}) Id List
320 Id <- [a-z] / %{errId}
321]]
322
323function mymatch (g, s)
324 local r, e = g:match(s)
325 if not r then
326 return terror[e]
327 end
328 return r
329end
330
331print(mymatch(g, "a,b"))
332print(mymatch(g, "a b"))
333print(mymatch(g, ", b"))
334</pre>
335
336
337
338<h3>Throwing and catching a label</h3>
339
340<p>
341When a label is thrown, the grammar itself can handle this label
342by using the labeled ordered choice. Below we rewrite the example
343of the list of identifiers to show this feature:
344</p>
345<pre class="example">
346local m = require'lpeglabel'
347
348local errUndef, errId, errComma = 0, 1, 2
349
350local terror = {
351 [errUndef] = "Error",
352 [errId] = "Error: expecting an identifier",
353 [errComma] = "Error: expecting ','",
354} 210}
355 211
356g = m.P{ 212g = expect(g, "NoExp") * expect(-P(1), "Extra")
357 "S",
358 S = m.Lc(m.Lc(m.V"Id" * m.V"List", m.V"ErrId", errId),
359 m.V"ErrComma", errComma),
360 List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
361 Id = m.R'az'^1 + m.T(errId),
362 Comma = "," + m.T(errComma),
363 ErrId = m.Cc(errId) / terror,
364 ErrComma = m.Cc(errComma) / terror
365}
366 213
367print(g:match("a,b")) 214local function eval(input)
368print(g:match("a b")) 215 local result, label, suffix = g:match(input)
369print(g:match(",b")) 216 if result ~= nil then
370</pre> 217 return result
371 218 else
372<p> 219 local pos = input:len() - suffix:len() + 1
373As was pointed out <a href="#f-lc">before</a>, the labeled ordered 220 local msg = labels[label][2]
374choice is not associative, so we should impose a left-associative 221 return nil, "syntax error: " .. msg .. " (at index " .. pos .. ")"
375order when using function <code>Lc</code>. 222 end
376</p>
377<p>
378Below we use the <em>re</em> module to throw and catch labels.
379As was pointed out <a href="#re-lc">before</a>, the <code>/{}</code>
380operator is left-associative, so we do not need to manually impose
381a left-associative order as we did in the previous example that
382used <code>Lc</code>:
383</p>
384<pre class="example">
385local re = require'relabel'
386
387local terror = {}
388
389local function newError(l, msg)
390 table.insert(terror, { l = l, msg = msg } )
391end 223end
392 224
393newError("errId", "Error: expecting an identifier") 225print(eval "98-76*(54/32)")
394newError("errComma", "Error: expecting ','") 226--> 37.125
395
396local labelCode = {}
397local labelMsg = {}
398for k, v in ipairs(terror) do
399 labelCode[v.l] = k
400 labelMsg[v.l] = v.msg
401end
402 227
403re.setlabels(labelCode) 228print(eval "(1+1-1*2/2")
404 229--> syntax error: missing a closing ')' after the expression (at index 11)
405local p = re.compile([[
406 S <- Id List /{errId} ErrId /{errComma} ErrComma
407 List <- !. / Comma Id List
408 Id <- [a-z]+ / %{errId}
409 Comma <- ',' / %{errComma}
410 ErrId <- '' -> errId
411 ErrComma <- '' -> errComma
412]], labelMsg)
413
414print(p:match("a,b"))
415print(p:match("a b"))
416print(p:match(",b"))
417</pre>
418
419
420<h3>Tiny Language</h3>
421<p>
422As a more complex example, below we have the grammar
423for the Tiny language, as described in
424<a href="http://arxiv.org/abs/1405.6646">this</a> paper.
425The example below can also show the line where the syntactic
426error probably happened.
427</p>
428<pre class="example">
429local re = require 'relabel'
430
431local terror = {}
432
433local function newError(l, msg)
434 table.insert(terror, { l = l, msg = msg} )
435end
436 230
437newError("errSemi", "Error: missing ';'") 231print(eval "(1+)-1*(2/2)")
438newError("errExpIf", "Error: expected expression after 'if'") 232--> syntax error: expected a term after the operator (at index 4)
439newError("errThen", "Error: expected 'then' keyword")
440newError("errCmdSeq1", "Error: expected at least a command after 'then'")
441newError("errCmdSeq2", "Error: expected at least a command after 'else'")
442newError("errEnd", "Error: expected 'end' keyword")
443newError("errCmdSeqRep", "Error: expected at least a command after 'repeat'")
444newError("errUntil", "Error: expected 'until' keyword")
445newError("errExpRep", "Error: expected expression after 'until'")
446newError("errAssignOp", "Error: expected ':=' in assigment")
447newError("errExpAssign", "Error: expected expression after ':='")
448newError("errReadName", "Error: expected an identifier after 'read'")
449newError("errWriteExp", "Error: expected expression after 'write'")
450newError("errSimpExp", "Error: expected '(', ID, or number after '<' or '='")
451newError("errTerm", "Error: expected '(', ID, or number after '+' or '-'")
452newError("errFactor", "Error: expected '(', ID, or number after '*' or '/'")
453newError("errExpFac", "Error: expected expression after '('")
454newError("errClosePar", "Error: expected ')' after expression")
455
456local line
457
458local function incLine()
459 line = line + 1
460 return true
461end
462 233
463local function countLine(s, i) 234print(eval "(1+1)-1*(/2)")
464 line = 1 235--> syntax error: expected an expression after the parenthesis (at index 10)
465 local p = re.compile([[
466 S <- (%nl -> incLine / .)*
467 ]], { incLine = incLine})
468 p:match(s:sub(1, i))
469 return true
470end
471 236
472local labelCode = {} 237print(eval "1+(1-(1*2))/2x")
473for k, v in ipairs(terror) do 238--> syntax error: extra chracters found after the expression (at index 14)
474 labelCode[v.l] = k
475end
476 239
477re.setlabels(labelCode) 240print(eval "-1+(1-(1*2))/2")
478 241--> syntax error: no expression found (at index 1)
479local g = re.compile([[ 242```
480 Tiny <- CmdSeq
481 CmdSeq <- (Cmd (SEMICOLON / ErrSemi)) (Cmd (SEMICOLON / ErrSemi))*
482 Cmd <- IfCmd / RepeatCmd / ReadCmd / WriteCmd / AssignCmd
483 IfCmd <- IF (Exp / ErrExpIf) (THEN / ErrThen) (CmdSeq / ErrCmdSeq1) (ELSE (CmdSeq / ErrCmdSeq2) / '') (END / ErrEnd)
484 RepeatCmd <- REPEAT (CmdSeq / ErrCmdSeqRep) (UNTIL / ErrUntil) (Exp / ErrExpRep)
485 AssignCmd <- NAME (ASSIGNMENT / ErrAssignOp) (Exp / ErrExpAssign)
486 ReadCmd <- READ (NAME / ErrReadName)
487 WriteCmd <- WRITE (Exp / ErrWriteExp)
488 Exp <- SimpleExp ((LESS / EQUAL) (SimpleExp / ErrSimpExp) / '')
489 SimpleExp <- Term ((ADD / SUB) (Term / ErrTerm))*
490 Term <- Factor ((MUL / DIV) (Factor / ErrFactor))*
491 Factor <- OPENPAR (Exp / ErrExpFac) (CLOSEPAR / ErrClosePar) / NUMBER / NAME
492 ErrSemi <- ErrCount %{errSemi}
493 ErrExpIf <- ErrCount %{errExpIf}
494 ErrThen <- ErrCount %{errThen}
495 ErrCmdSeq1 <- ErrCount %{errCmdSeq1}
496 ErrCmdSeq2 <- ErrCount %{errCmdSeq2}
497 ErrEnd <- ErrCount %{errEnd}
498 ErrCmdSeqRep <- ErrCount %{errCmdSeqRep}
499 ErrUntil <- ErrCount %{errUntil}
500 ErrExpRep <- ErrCount %{errExpRep}
501 ErrAssignOp <- ErrCount %{errAssignOp}
502 ErrExpAssign <- ErrCount %{errExpAssign}
503 ErrReadName <- ErrCount %{errReadName}
504 ErrWriteExp <- ErrCount %{errWriteExp}
505 ErrSimpExp <- ErrCount %{errSimpExp}
506 ErrTerm <- ErrCount %{errTerm}
507 ErrFactor <- ErrCount %{errFactor}
508 ErrExpFac <- ErrCount %{errExpFac}
509 ErrClosePar <- ErrCount %{errClosePar}
510 ErrCount <- '' => countLine
511 ADD <- Sp '+'
512 ASSIGNMENT <- Sp ':='
513 CLOSEPAR <- Sp ')'
514 DIV <- Sp '/'
515 IF <- Sp 'if'
516 ELSE <- Sp 'else'
517 END <- Sp 'end'
518 EQUAL <- Sp '='
519 LESS <- Sp '<'
520 MUL <- Sp '*'
521 NAME <- Sp !RESERVED [a-z]+
522 NUMBER <- Sp [0-9]+
523 OPENPAR <- Sp '('
524 READ <- Sp 'read'
525 REPEAT <- Sp 'repeat'
526 SEMICOLON <- Sp ';'
527 SUB <- Sp '-'
528 THEN <- Sp 'then'
529 UNTIL <- Sp 'until'
530 WRITE <- Sp 'write'
531 RESERVED <- (IF / ELSE / END / READ / REPEAT / THEN / UNTIL / WRITE) ![a-z]+
532 Sp <- %s*
533]], { countLine = countLine })
534</pre>
535
536
537<h2><a name="download"></a>Download</h2>
538
539<p>LPegLabel
540<a href="https://github.com/sqmedeiros/lpeglabel/archive/master.zip">source code</a>.</p>
541
542
543<h2><a name="license">License</a></h2>
544
545<p>
546The MIT License (MIT)
547</p>
548<p>
549Copyright (c) 2014-2015 Sérgio Medeiros
550</p>
551<p>
552Permission is hereby granted, free of charge, to any person obtaining a copy
553of this software and associated documentation files (the "Software"), to deal
554in the Software without restriction, including without limitation the rights
555to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
556copies of the Software, and to permit persons to whom the Software is
557furnished to do so, subject to the following conditions:
558</p>
559<p>
560The above copyright notice and this permission notice shall be included in all
561copies or substantial portions of the Software.
562</p>
563<p>
564THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
565IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
566FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
567AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
568LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
569OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
570SOFTWARE.
571
572
573
574
575</p>
576
577</div> <!-- id="content" -->
578
579</div> <!-- id="main" -->
580
581<div id="about">
582</div> <!-- id="about" -->
583
584</div> <!-- id="container" -->
585
586</body>
587</html>
588 243
diff --git a/examples/expect.md b/examples/expect.lua
index d7d2d3e..2b7e904 100644
--- a/examples/expect.md
+++ b/examples/expect.lua
@@ -1,9 +1,3 @@
1Here's an example of an LPegLabel grammar that make its own function called
2'expect', which takes a pattern and a label as parameters and throws the label
3if the pattern fails to be matched. This function can be extended later on to
4record all errors encountered once error recovery is implemented.
5
6```lua
7local lpeg = require"lpeglabel" 1local lpeg = require"lpeglabel"
8 2
9local R, S, P, V, C, Ct, T = lpeg.R, lpeg.S, lpeg.P, lpeg.V, lpeg.C, lpeg.Ct, lpeg.T 3local R, S, P, V, C, Ct, T = lpeg.R, lpeg.S, lpeg.P, lpeg.V, lpeg.C, lpeg.Ct, lpeg.T
@@ -84,4 +78,3 @@ print(eval "1+(1-(1*2))/2x")
84 78
85print(eval "-1+(1-(1*2))/2") 79print(eval "-1+(1-(1*2))/2")
86--> syntax error: no expression found (at index 1) 80--> syntax error: no expression found (at index 1)
87```
diff --git a/examples/listId1.lua b/examples/listId1.lua
index 0bf26a1..12c0678 100644
--- a/examples/listId1.lua
+++ b/examples/listId1.lua
@@ -1,27 +1,38 @@
1local m = require'lpeglabel' 1local m = require'lpeglabel'
2 2
3local function calcline (s, i)
4 if i == 1 then return 1, 1 end
5 local rest, line = s:sub(1,i):gsub("[^\n]*\n", "")
6 local col = #rest
7 return 1 + line, col ~= 0 and col or 1
8end
9
3local g = m.P{ 10local g = m.P{
4 "S", 11 "S",
5 S = m.V"Id" * m.V"List", 12 S = m.V"Id" * m.V"List",
6 List = -m.P(1) + ("," + m.T(2)) * m.V"Id" * m.V"List", 13 List = -m.P(1) + (m.V"Comma" + m.T(2)) * (m.V"Id" + m.T(1)) * m.V"List",
7 Id = m.R'az'^1 + m.T(1), 14 Id = m.V"Sp" * m.R'az'^1,
15 Comma = m.V"Sp" * ",",
16 Sp = m.S" \n\t"^0,
8} 17}
9 18
10function mymatch (g, s) 19function mymatch (g, s)
11 local r, e, sfail = g:match(s) 20 local r, e, sfail = g:match(s)
12 if not r then 21 if not r then
13 if e == 1 then 22 local line, col = calcline(s, #s - #sfail)
14 return r, "Error: expecting an identifier before '" .. sfail .. "'" 23 local msg = "Error at line " .. line .. " (col " .. col .. ")"
15 elseif e == 2 then 24 if e == 1 then
16 return r, "Error: expecting ',' before '" .. sfail .. "'" 25 return r, msg .. ": expecting an identifier before '" .. sfail .. "'"
17 else 26 elseif e == 2 then
18 return r, "Error" 27 return r, msg .. ": expecting ',' before '" .. sfail .. "'"
19 end 28 else
20 end 29 return r, msg
21 return r 30 end
31 end
32 return r
22end 33end
23 34
24print(mymatch(g, "a,b")) 35print(mymatch(g, "one,two"))
25print(mymatch(g, "a b")) 36print(mymatch(g, "one two"))
26print(mymatch(g, ", b")) 37print(mymatch(g, "one,\n two,\nthree,"))
27 38
diff --git a/examples/listId2.lua b/examples/listId2.lua
index 75060f9..48157f1 100644
--- a/examples/listId2.lua
+++ b/examples/listId2.lua
@@ -1,32 +1,42 @@
1local m = require'lpeglabel' 1local m = require'lpeglabel'
2 2
3local errUndef = 0 3local terror = {}
4local errId = 1
5local errComma = 2
6 4
7local terror = { 5local function newError(s)
8 [errUndef] = "Error", 6 table.insert(terror, s)
9 [errId] = "Error: expecting an identifier", 7 return #terror
10 [errComma] = "Error: expecting ','", 8end
11} 9
10local errUndef = newError("undefined")
11local errId = newError("expecting an identifier")
12local errComma = newError("expecting ','")
13
14local function calcline (s, i)
15 if i == 1 then return 1, 1 end
16 local rest, line = s:sub(1,i):gsub("[^\n]*\n", "")
17 local col = #rest
18 return 1 + line, col ~= 0 and col or 1
19end
12 20
13local g = m.P{ 21local g = m.P{
14 "S", 22 "S",
15 S = m.V"Id" * m.V"List", 23 S = m.V"Id" * m.V"List",
16 List = -m.P(1) + ("," + m.T(errComma)) * m.V"Id" * m.V"List", 24 List = -m.P(1) + (m.V"Comma" + m.T(errComma)) * (m.V"Id" + m.T(errId)) * m.V"List",
17 Id = m.R'az'^1 + m.T(errId), 25 Id = m.V"Sp" * m.R'az'^1,
26 Comma = m.V"Sp" * ",",
27 Sp = m.S" \n\t"^0,
18} 28}
19 29
20function mymatch (g, s) 30function mymatch (g, s)
21 local r, e, sfail = g:match(s) 31 local r, e, sfail = g:match(s)
22 if not r then 32 if not r then
23 return r, terror[e] .. " before '" .. sfail .. "'" 33 local line, col = calcline(s, #s - #sfail)
34 local msg = "Error at line " .. line .. " (col " .. col .. "): "
35 return r, msg .. terror[e] .. " before '" .. sfail .. "'"
24 end 36 end
25 return r 37 return r
26end 38end
27 39
28print(mymatch(g, "a,b")) 40print(mymatch(g, "one,two"))
29print(mymatch(g, "a b")) 41print(mymatch(g, "one two"))
30print(mymatch(g, ", b")) 42print(mymatch(g, "one,\n two,\nthree,"))
31
32
diff --git a/examples/listIdCatch.lua b/examples/listIdCatch.lua
index 38ad2e5..3cbc834 100644
--- a/examples/listIdCatch.lua
+++ b/examples/listIdCatch.lua
@@ -1,25 +1,45 @@
1local m = require'lpeglabel' 1local m = require'lpeglabel'
2 2
3local errUndef, errId, errComma = 0, 1, 2 3local terror = {}
4 4
5local terror = { 5local function newError(s)
6 [errUndef] = "Error", 6 table.insert(terror, s)
7 [errId] = "Error: expecting an identifier", 7 return #terror
8 [errComma] = "Error: expecting ','", 8end
9} 9
10local errUndef = newError("undefined")
11local errId = newError("expecting an identifier")
12local errComma = newError("expecting ','")
13
14local function calcline (s, i)
15 if i == 1 then return 1, 1 end
16 local rest, line = s:sub(1,i):gsub("[^\n]*\n", "")
17 local col = #rest
18 return 1 + line, col ~= 0 and col or 1
19end
10 20
11g = m.P{ 21local g = m.P{
12 "S", 22 "S",
13 S = m.Lc(m.Lc(m.V"Id" * m.V"List", m.V"ErrId", errId), 23 S = m.Lc(m.Lc(m.V"Id" * m.V"List", m.V"ErrId", errId),
14 m.V"ErrComma", errComma), 24 m.V"ErrComma", errComma),
15 List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List", 25 List = -m.P(1) + (m.V"Comma" + m.T(errComma)) * (m.V"Id" + m.T(errId)) * m.V"List",
16 Id = m.R'az'^1 + m.T(errId), 26 Id = m.V"Sp" * m.R'az'^1,
17 Comma = "," + m.T(errComma), 27 Comma = m.V"Sp" * ",",
28 Sp = m.S" \n\t"^0,
18 ErrId = m.Cc(errId) / terror, 29 ErrId = m.Cc(errId) / terror,
19 ErrComma = m.Cc(errComma) / terror 30 ErrComma = m.Cc(errComma) / terror
20} 31}
21 32
22print(g:match("a,b")) 33function mymatch (g, s)
23print(g:match("a b")) 34 local r, e, sfail = g:match(s)
24print(g:match(",b")) 35 if not r then
36 local line, col = calcline(s, #s - #sfail)
37 local msg = "Error at line " .. line .. " (col " .. col .. "): "
38 return r, msg .. terror[e] .. " before '" .. sfail .. "'"
39 end
40 return r
41end
25 42
43print(mymatch(g, "one,two"))
44print(mymatch(g, "one two"))
45print(mymatch(g, "one,\n two,\nthree,"))
diff --git a/examples/listIdRe1.lua b/examples/listIdRe1.lua
index fc213bc..361e53f 100644
--- a/examples/listIdRe1.lua
+++ b/examples/listIdRe1.lua
@@ -1,27 +1,37 @@
1local re = require 'relabel' 1local re = require 'relabel'
2 2
3local function calcline (s, i)
4 if i == 1 then return 1, 1 end
5 local rest, line = s:sub(1,i):gsub("[^\n]*\n", "")
6 local col = #rest
7 return 1 + line, col ~= 0 and col or 1
8end
9
3local g = re.compile[[ 10local g = re.compile[[
4 S <- Id List 11 S <- Id List
5 List <- !. / (',' / %{2}) Id List 12 List <- !. / (',' / %{2}) (Id / %{1}) List
6 Id <- [a-z] / %{1} 13 Id <- Sp [a-z]+
14 Comma <- Sp ','
15 Sp <- %s*
7]] 16]]
8 17
9function mymatch (g, s) 18function mymatch (g, s)
10 local r, e, sfail = g:match(s) 19 local r, e, sfail = g:match(s)
11 if not r then 20 if not r then
21 local line, col = calcline(s, #s - #sfail)
22 local msg = "Error at line " .. line .. " (col " .. col .. ")"
12 if e == 1 then 23 if e == 1 then
13 return r, "Error: expecting an identifier before '" .. sfail .. "'" 24 return r, msg .. ": expecting an identifier before '" .. sfail .. "'"
14 elseif e == 2 then 25 elseif e == 2 then
15 return r, "Error: expecting ',' before '" .. sfail .. "'" 26 return r, msg .. ": expecting ',' before '" .. sfail .. "'"
16 else 27 else
17 return r, "Error" 28 return r, msg
18 end 29 end
19 end 30 end
20 return r 31 return r
21end 32end
22
23print(mymatch(g, "a,b"))
24print(mymatch(g, "a b"))
25print(mymatch(g, ", b"))
26 33
34print(mymatch(g, "one,two"))
35print(mymatch(g, "one two"))
36print(mymatch(g, "one,\n two,\nthree,"))
27 37
diff --git a/examples/listIdRe2.lua b/examples/listIdRe2.lua
index 4cd140b..ccb7ce5 100644
--- a/examples/listIdRe2.lua
+++ b/examples/listIdRe2.lua
@@ -1,35 +1,48 @@
1local re = require 'relabel' 1local re = require 'relabel'
2 2
3local errUndef, errId, errComma = 0, 1, 2 3local errinfo = {
4 4 {"errUndef", "undefined"},
5local terror = { 5 {"errId", "expecting an identifier"},
6 [errUndef] = "Error", 6 {"errComma", "expecting ','"},
7 [errId] = "Error: expecting an identifier",
8 [errComma] = "Error: expecting ','",
9} 7}
10 8
11local tlabels = { ["errUndef"] = errUndef, 9local errmsgs = {}
12 ["errId"] = errId, 10local labels = {}
13 ["errComma"] = errComma } 11
12for i, err in ipairs(errinfo) do
13 errmsgs[i] = err[2]
14 labels[err[1]] = i
15end
16
17re.setlabels(labels)
18
19local function calcline (s, i)
20 if i == 1 then return 1, 1 end
21 local rest, line = s:sub(1,i):gsub("[^\n]*\n", "")
22 local col = #rest
23 return 1 + line, col ~= 0 and col or 1
24end
14 25
15re.setlabels(tlabels)
16 26
17local g = re.compile[[ 27local g = re.compile[[
18 S <- Id List 28 S <- Id List
19 List <- !. / (',' / %{errComma}) Id List 29 List <- !. / (',' / %{errComma}) (Id / %{errId}) List
20 Id <- [a-z] / %{errId} 30 Id <- Sp [a-z]+
31 Comma <- Sp ','
32 Sp <- %s*
21]] 33]]
22 34
23function mymatch (g, s) 35function mymatch (g, s)
24 local r, e, sfail = g:match(s) 36 local r, e, sfail = g:match(s)
25 if not r then 37 if not r then
26 return r, terror[e] .. " before '" .. sfail .. "'" 38 local line, col = calcline(s, #s - #sfail)
39 local msg = "Error at line " .. line .. " (col " .. col .. "): "
40 return r, msg .. errmsgs[e] .. " before '" .. sfail .. "'"
27 end 41 end
28 return r 42 return r
29end 43end
30
31print(mymatch(g, "a,b"))
32print(mymatch(g, "a b"))
33print(mymatch(g, ", b"))
34 44
45print(mymatch(g, "one,two"))
46print(mymatch(g, "one two"))
47print(mymatch(g, "one,\n two,\nthree,"))
35 48