aboutsummaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rw-r--r--README.md201
1 files changed, 191 insertions, 10 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```