aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md201
-rw-r--r--examples/listId1.lua11
-rw-r--r--examples/listId2.lua32
-rw-r--r--examples/listIdCatch.lua35
-rw-r--r--examples/listIdRe1.lua35
-rw-r--r--examples/listIdRe2.lua30
-rw-r--r--relabel.lua11
7 files changed, 250 insertions, 105 deletions
diff --git a/README.md b/README.md
index c58f8a2..eb9658a 100644
--- a/README.md
+++ b/README.md
@@ -43,6 +43,9 @@ of the new functions provided by LpegLabel:
43<tr><td><a href="#re-lc"><code>p1 /{l1, ..., ln} p2</code></a></td> 43<tr><td><a href="#re-lc"><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> 44 <td>Syntax of <em>relabel</em> module. Equivalent to <code>lpeg.Lc(p1, p2, l1, ..., ln)</code>
45 </td></tr> 45 </td></tr>
46<tr><td><a href="#re-line"><code>relabel.calcline(subject, i)</code></a></td>
47 <td>Calculates line and column information regarding position <i>i</i> of the subject</code>
48 </td></tr>
46<tr><td><a href="#re-setl"><code>relabel.setlabels (tlabel)</code></a></td> 49<tr><td><a href="#re-setl"><code>relabel.setlabels (tlabel)</code></a></td>
47 <td>Allows to specicify a table with mnemonic labels. 50 <td>Allows to specicify a table with mnemonic labels.
48 </td></tr> 51 </td></tr>
@@ -92,6 +95,11 @@ but a single choice can not mix them. That is, the parser of `relabel`
92module will not recognize a pattern as `p1 / p2 /{l1} p3`. 95module will not recognize a pattern as `p1 / p2 /{l1} p3`.
93 96
94 97
98#### <a name="re-line"></a><code>relabel.calcline (subject, i)</code>
99
100Returns line and column information regarding position <i>i</i> of the subject.
101
102
95#### <a name="re-setl"></a><code>relabel.setlabels (tlabel)</code> 103#### <a name="re-setl"></a><code>relabel.setlabels (tlabel)</code>
96 104
97Allows to specicify a table with labels. They keys of 105Allows to specicify a table with labels. They keys of
@@ -101,22 +109,21 @@ and the associated values should be strings.
101 109
102### Examples 110### Examples
103 111
104#### Throwing a label 112Below there a few examples of usage of LPegLabel.
113The code of these and of other examples is available
114in the *examples* directory.
115
116
117#### Matching a list of identifiers separated by commas
105 118
106The following example defines a grammar that matches 119The following example defines a grammar that matches
107a list of identifiers separated by commas. A label 120a list of identifiers separated by commas. A label
108is thrown when there is an error matching an identifier 121is thrown when there is an error matching an identifier
109or a comma: 122or a comma:
110 123
111```lua 124```lua
112local m = require'lpeglabel' 125local m = require'lpeglabel'
113 126local re = require'relabel'
114local function calcline (s, i)
115 if i == 1 then return 1, 1 end
116 local rest, line = s:sub(1,i):gsub("[^\n]*\n", "")
117 local col = #rest
118 return 1 + line, col ~= 0 and col or 1
119end
120 127
121local g = m.P{ 128local g = m.P{
122 "S", 129 "S",
@@ -130,7 +137,7 @@ local g = m.P{
130function mymatch (g, s) 137function mymatch (g, s)
131 local r, e, sfail = g:match(s) 138 local r, e, sfail = g:match(s)
132 if not r then 139 if not r then
133 local line, col = calcline(s, #s - #sfail) 140 local line, col = re.calcline(s, #s - #sfail)
134 local msg = "Error at line " .. line .. " (col " .. col .. ")" 141 local msg = "Error at line " .. line .. " (col " .. col .. ")"
135 if e == 1 then 142 if e == 1 then
136 return r, msg .. ": expecting an identifier before '" .. sfail .. "'" 143 return r, msg .. ": expecting an identifier before '" .. sfail .. "'"
@@ -142,6 +149,61 @@ function mymatch (g, s)
142 end 149 end
143 return r 150 return r
144end 151end
152
153print(mymatch(g, "one,two")) --> 8
154print(mymatch(g, "one two")) --> nil Error at line 1 (col 3): expecting ',' before ' two'
155print(mymatch(g, "one,\n two,\nthree,")) --> nil Error at line 3 (col 6): expecting an identifier before ''
156```
157
158In this example we could think about writing rule <em>List</em> as follows:
159```lua
160List = ((m.V"Comma" + m.T(2)) * (m.V"Id" + m.T(1)))^0,
161```
162
163but when matching this expression agains the end of input
164we would get a failure whose associated label would be **2**,
165and this would cause the failure of the *whole* repetition.
166
167
168##### Mnemonics instead of numbers
169
170In the previous example we could have created a table
171with the error messages to improve the readbility of the PEG.
172Below we rewrite the previous grammar following this approach:
173
174```lua
175local m = require'lpeglabel'
176local re = require'relabel'
177
178local terror = {}
179
180local function newError(s)
181 table.insert(terror, s)
182 return #terror
183end
184
185local errUndef = newError("undefined")
186local errId = newError("expecting an identifier")
187local errComma = newError("expecting ','")
188
189local g = m.P{
190 "S",
191 S = m.V"Id" * m.V"List",
192 List = -m.P(1) + (m.V"Comma" + m.T(errComma)) * (m.V"Id" + m.T(errId)) * m.V"List",
193 Id = m.V"Sp" * m.R'az'^1,
194 Comma = m.V"Sp" * ",",
195 Sp = m.S" \n\t"^0,
196}
197
198function mymatch (g, s)
199 local r, e, sfail = g:match(s)
200 if not r then
201 local line, col = re.calcline(s, #s - #sfail)
202 local msg = "Error at line " .. line .. " (col " .. col .. "): "
203 return r, msg .. terror[e] .. " before '" .. sfail .. "'"
204 end
205 return r
206end
145 207
146print(mymatch(g, "one,two")) --> 8 208print(mymatch(g, "one,two")) --> 8
147print(mymatch(g, "one two")) --> nil Error at line 1 (col 3): expecting ',' before ' two' 209print(mymatch(g, "one two")) --> nil Error at line 1 (col 3): expecting ',' before ' two'
@@ -149,6 +211,88 @@ print(mymatch(g, "one,\n two,\nthree,")) --> nil Error at line 3 (col 6): expec
149``` 211```
150 212
151 213
214##### *relabel* syntax
215
216Now we rewrite the previous example using the syntax
217supported by *relabel*:
218
219```lua
220local re = require 'relabel'
221
222local g = re.compile[[
223 S <- Id List
224 List <- !. / (',' / %{2}) (Id / %{1}) List
225 Id <- Sp [a-z]+
226 Comma <- Sp ','
227 Sp <- %s*
228]]
229
230function mymatch (g, s)
231 local r, e, sfail = g:match(s)
232 if not r then
233 local line, col = re.calcline(s, #s - #sfail)
234 local msg = "Error at line " .. line .. " (col " .. col .. ")"
235 if e == 1 then
236 return r, msg .. ": expecting an identifier before '" .. sfail .. "'"
237 elseif e == 2 then
238 return r, msg .. ": expecting ',' before '" .. sfail .. "'"
239 else
240 return r, msg
241 end
242 end
243 return r
244end
245
246print(mymatch(g, "one,two")) --> 8
247print(mymatch(g, "one two")) --> nil Error at line 1 (col 3): expecting ',' before ' two'
248print(mymatch(g, "one,\n two,\nthree,")) --> nil Error at line 3 (col 6): expecting an identifier before ''
249```
250
251With the help of function *setlabels* we can also rewrite the previous example to use
252mnemonic labels instead of plain numbers:
253
254```lua
255local re = require 'relabel'
256
257local errinfo = {
258 {"errUndef", "undefined"},
259 {"errId", "expecting an identifier"},
260 {"errComma", "expecting ','"},
261}
262
263local errmsgs = {}
264local labels = {}
265
266for i, err in ipairs(errinfo) do
267 errmsgs[i] = err[2]
268 labels[err[1]] = i
269end
270
271re.setlabels(labels)
272
273local g = re.compile[[
274 S <- Id List
275 List <- !. / (',' / %{errComma}) (Id / %{errId}) List
276 Id <- Sp [a-z]+
277 Comma <- Sp ','
278 Sp <- %s*
279]]
280
281function mymatch (g, s)
282 local r, e, sfail = g:match(s)
283 if not r then
284 local line, col = re.calcline(s, #s - #sfail)
285 local msg = "Error at line " .. line .. " (col " .. col .. "): "
286 return r, msg .. errmsgs[e] .. " before '" .. sfail .. "'"
287 end
288 return r
289end
290
291print(mymatch(g, "one,two")) --> 8
292print(mymatch(g, "one two")) --> nil Error at line 1 (col 3): expecting ',' before ' two'
293print(mymatch(g, "one,\n two,\nthree,")) --> nil Error at line 3 (col 6): expecting an identifier before ''
294```
295
152#### Arithmetic Expressions 296#### Arithmetic Expressions
153 297
154Here's an example of an LPegLabel grammar that make its own function called 298Here's an example of an LPegLabel grammar that make its own function called
@@ -239,3 +383,40 @@ print(eval "-1+(1-(1*2))/2")
239--> syntax error: no expression found (at index 1) 383--> syntax error: no expression found (at index 1)
240``` 384```
241 385
386#### Catching labels
387
388When a label is thrown, the grammar itself can handle this label
389by using the labeled ordered choice. Below we rewrite the example
390of the list of identifiers to show this feature:
391
392
393```lua
394local m = require'lpeglabel'
395
396local terror = {}
397
398local function newError(s)
399 table.insert(terror, s)
400 return #terror
401end
402
403local errUndef = newError("undefined")
404local errId = newError("expecting an identifier")
405local errComma = newError("expecting ','")
406
407local g = m.P{
408 "S",
409 S = m.Lc(m.Lc(m.V"Id" * m.V"List", m.V"ErrId", errId),
410 m.V"ErrComma", errComma),
411 List = -m.P(1) + (m.V"Comma" + m.T(errComma)) * (m.V"Id" + m.T(errId)) * m.V"List",
412 Id = m.V"Sp" * m.R'az'^1,
413 Comma = m.V"Sp" * ",",
414 Sp = m.S" \n\t"^0,
415 ErrId = m.Cc(errId) / terror,
416 ErrComma = m.Cc(errComma) / terror
417}
418
419print(m.match(g, "one,two")) --> 8
420print(m.match(g, "one two")) --> expecting ','
421print(m.match(g, "one,\n two,\nthree,")) --> expecting an identifier
422```
diff --git a/examples/listId1.lua b/examples/listId1.lua
index 12c0678..8976f5f 100644
--- a/examples/listId1.lua
+++ b/examples/listId1.lua
@@ -1,11 +1,5 @@
1local m = require'lpeglabel' 1local m = require'lpeglabel'
2 2local re = require'relabel'
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 3
10local g = m.P{ 4local g = m.P{
11 "S", 5 "S",
@@ -19,7 +13,7 @@ local g = m.P{
19function mymatch (g, s) 13function mymatch (g, s)
20 local r, e, sfail = g:match(s) 14 local r, e, sfail = g:match(s)
21 if not r then 15 if not r then
22 local line, col = calcline(s, #s - #sfail) 16 local line, col = re.calcline(s, #s - #sfail)
23 local msg = "Error at line " .. line .. " (col " .. col .. ")" 17 local msg = "Error at line " .. line .. " (col " .. col .. ")"
24 if e == 1 then 18 if e == 1 then
25 return r, msg .. ": expecting an identifier before '" .. sfail .. "'" 19 return r, msg .. ": expecting an identifier before '" .. sfail .. "'"
@@ -35,4 +29,3 @@ end
35print(mymatch(g, "one,two")) 29print(mymatch(g, "one,two"))
36print(mymatch(g, "one two")) 30print(mymatch(g, "one two"))
37print(mymatch(g, "one,\n two,\nthree,")) 31print(mymatch(g, "one,\n two,\nthree,"))
38
diff --git a/examples/listId2.lua b/examples/listId2.lua
index 48157f1..509fda4 100644
--- a/examples/listId2.lua
+++ b/examples/listId2.lua
@@ -1,42 +1,36 @@
1local m = require'lpeglabel' 1local m = require'lpeglabel'
2local re = require'relabel'
2 3
3local terror = {} 4local terror = {}
4 5
5local function newError(s) 6local function newError(s)
6 table.insert(terror, s) 7 table.insert(terror, s)
7 return #terror 8 return #terror
8end 9end
9 10
10local errUndef = newError("undefined") 11local errUndef = newError("undefined")
11local errId = newError("expecting an identifier") 12local errId = newError("expecting an identifier")
12local errComma = newError("expecting ','") 13local errComma = newError("expecting ','")
13 14
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
20
21local g = m.P{ 15local g = m.P{
22 "S", 16 "S",
23 S = m.V"Id" * m.V"List", 17 S = 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", 18 List = -m.P(1) + (m.V"Comma" + m.T(errComma)) * (m.V"Id" + m.T(errId)) * m.V"List",
25 Id = m.V"Sp" * m.R'az'^1, 19 Id = m.V"Sp" * m.R'az'^1,
26 Comma = m.V"Sp" * ",", 20 Comma = m.V"Sp" * ",",
27 Sp = m.S" \n\t"^0, 21 Sp = m.S" \n\t"^0,
28} 22}
29 23
30function mymatch (g, s) 24function mymatch (g, s)
31 local r, e, sfail = g:match(s) 25 local r, e, sfail = g:match(s)
32 if not r then 26 if not r then
33 local line, col = calcline(s, #s - #sfail) 27 local line, col = re.calcline(s, #s - #sfail)
34 local msg = "Error at line " .. line .. " (col " .. col .. "): " 28 local msg = "Error at line " .. line .. " (col " .. col .. "): "
35 return r, msg .. terror[e] .. " before '" .. sfail .. "'" 29 return r, msg .. terror[e] .. " before '" .. sfail .. "'"
36 end 30 end
37 return r 31 return r
38end 32end
39 33
40print(mymatch(g, "one,two")) 34print(mymatch(g, "one,two"))
41print(mymatch(g, "one two")) 35print(mymatch(g, "one two"))
42print(mymatch(g, "one,\n two,\nthree,")) 36print(mymatch(g, "one,\n two,\nthree,"))
diff --git a/examples/listIdCatch.lua b/examples/listIdCatch.lua
index 3cbc834..5ad6f2d 100644
--- a/examples/listIdCatch.lua
+++ b/examples/listIdCatch.lua
@@ -3,43 +3,26 @@ local m = require'lpeglabel'
3local terror = {} 3local terror = {}
4 4
5local function newError(s) 5local function newError(s)
6 table.insert(terror, s) 6 table.insert(terror, s)
7 return #terror 7 return #terror
8end 8end
9 9
10local errUndef = newError("undefined") 10local errUndef = newError("undefined")
11local errId = newError("expecting an identifier") 11local errId = newError("expecting an identifier")
12local errComma = newError("expecting ','") 12local errComma = newError("expecting ','")
13 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
20
21local g = m.P{ 14local g = m.P{
22 "S", 15 "S",
23 S = m.Lc(m.Lc(m.V"Id" * m.V"List", m.V"ErrId", errId), 16 S = m.Lc(m.Lc(m.V"Id" * m.V"List", m.V"ErrId", errId),
24 m.V"ErrComma", errComma), 17 m.V"ErrComma", errComma),
25 List = -m.P(1) + (m.V"Comma" + m.T(errComma)) * (m.V"Id" + m.T(errId)) * m.V"List", 18 List = -m.P(1) + (m.V"Comma" + m.T(errComma)) * (m.V"Id" + m.T(errId)) * m.V"List",
26 Id = m.V"Sp" * m.R'az'^1, 19 Id = m.V"Sp" * m.R'az'^1,
27 Comma = m.V"Sp" * ",", 20 Comma = m.V"Sp" * ",",
28 Sp = m.S" \n\t"^0, 21 Sp = m.S" \n\t"^0,
29 ErrId = m.Cc(errId) / terror, 22 ErrId = m.Cc(errId) / terror,
30 ErrComma = m.Cc(errComma) / terror 23 ErrComma = m.Cc(errComma) / terror
31} 24}
32 25
33function mymatch (g, s) 26print(m.match(g, "one,two"))
34 local r, e, sfail = g:match(s) 27print(m.match(g, "one two"))
35 if not r then 28print(m.match(g, "one,\n two,\nthree,"))
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
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 361e53f..d092566 100644
--- a/examples/listIdRe1.lua
+++ b/examples/listIdRe1.lua
@@ -1,34 +1,27 @@
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
10local g = re.compile[[ 3local g = re.compile[[
11 S <- Id List 4 S <- Id List
12 List <- !. / (',' / %{2}) (Id / %{1}) List 5 List <- !. / (',' / %{2}) (Id / %{1}) List
13 Id <- Sp [a-z]+ 6 Id <- Sp [a-z]+
14 Comma <- Sp ',' 7 Comma <- Sp ','
15 Sp <- %s* 8 Sp <- %s*
16]] 9]]
17 10
18function mymatch (g, s) 11function mymatch (g, s)
19 local r, e, sfail = g:match(s) 12 local r, e, sfail = g:match(s)
20 if not r then 13 if not r then
21 local line, col = calcline(s, #s - #sfail) 14 local line, col = re.calcline(s, #s - #sfail)
22 local msg = "Error at line " .. line .. " (col " .. col .. ")" 15 local msg = "Error at line " .. line .. " (col " .. col .. ")"
23 if e == 1 then 16 if e == 1 then
24 return r, msg .. ": expecting an identifier before '" .. sfail .. "'" 17 return r, msg .. ": expecting an identifier before '" .. sfail .. "'"
25 elseif e == 2 then 18 elseif e == 2 then
26 return r, msg .. ": expecting ',' before '" .. sfail .. "'" 19 return r, msg .. ": expecting ',' before '" .. sfail .. "'"
27 else 20 else
28 return r, msg 21 return r, msg
29 end 22 end
30 end 23 end
31 return r 24 return r
32end 25end
33 26
34print(mymatch(g, "one,two")) 27print(mymatch(g, "one,two"))
diff --git a/examples/listIdRe2.lua b/examples/listIdRe2.lua
index ccb7ce5..fe30535 100644
--- a/examples/listIdRe2.lua
+++ b/examples/listIdRe2.lua
@@ -1,9 +1,9 @@
1local re = require 'relabel' 1local re = require 'relabel'
2 2
3local errinfo = { 3local errinfo = {
4 {"errUndef", "undefined"}, 4 {"errUndef", "undefined"},
5 {"errId", "expecting an identifier"}, 5 {"errId", "expecting an identifier"},
6 {"errComma", "expecting ','"}, 6 {"errComma", "expecting ','"},
7} 7}
8 8
9local errmsgs = {} 9local errmsgs = {}
@@ -16,30 +16,22 @@ end
16 16
17re.setlabels(labels) 17re.setlabels(labels)
18 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
25
26
27local g = re.compile[[ 19local g = re.compile[[
28 S <- Id List 20 S <- Id List
29 List <- !. / (',' / %{errComma}) (Id / %{errId}) List 21 List <- !. / (',' / %{errComma}) (Id / %{errId}) List
30 Id <- Sp [a-z]+ 22 Id <- Sp [a-z]+
31 Comma <- Sp ',' 23 Comma <- Sp ','
32 Sp <- %s* 24 Sp <- %s*
33]] 25]]
34 26
35function mymatch (g, s) 27function mymatch (g, s)
36 local r, e, sfail = g:match(s) 28 local r, e, sfail = g:match(s)
37 if not r then 29 if not r then
38 local line, col = calcline(s, #s - #sfail) 30 local line, col = re.calcline(s, #s - #sfail)
39 local msg = "Error at line " .. line .. " (col " .. col .. "): " 31 local msg = "Error at line " .. line .. " (col " .. col .. "): "
40 return r, msg .. errmsgs[e] .. " before '" .. sfail .. "'" 32 return r, msg .. errmsgs[e] .. " before '" .. sfail .. "'"
41 end 33 end
42 return r 34 return r
43end 35end
44 36
45print(mymatch(g, "one,two")) 37print(mymatch(g, "one,two"))
diff --git a/relabel.lua b/relabel.lua
index 003a0d2..ff2e486 100644
--- a/relabel.lua
+++ b/relabel.lua
@@ -433,6 +433,14 @@ local function setlabels (t)
433 tlabels = t 433 tlabels = t
434end 434end
435 435
436local function calcline (s, i)
437 if i == 1 then return 1, 1 end
438 local rest, line = s:sub(1,i):gsub("[^\n]*\n", "")
439 local col = #rest
440 return 1 + line, col ~= 0 and col or 1
441end
442
443
436-- exported names 444-- exported names
437local re = { 445local re = {
438 compile = compile, 446 compile = compile,
@@ -440,7 +448,8 @@ local re = {
440 find = find, 448 find = find,
441 gsub = gsub, 449 gsub = gsub,
442 updatelocale = updatelocale, 450 updatelocale = updatelocale,
443 setlabels = setlabels 451 setlabels = setlabels,
452 calcline = calcline
444} 453}
445 454
446if version == "Lua 5.1" then _G.re = re end 455if version == "Lua 5.1" then _G.re = re end