diff options
author | Sergio Queiroz <sqmedeiros@gmail.com> | 2016-12-14 08:16:29 -0300 |
---|---|---|
committer | Sergio Queiroz <sqmedeiros@gmail.com> | 2016-12-14 08:16:29 -0300 |
commit | 98e54ce388233170cd3a3feb2bcc4449670ccabc (patch) | |
tree | b02c02adf0dbdfe0d700d20cb204ed834b99e740 | |
parent | 20cb6e1443f3b79a4db3fedf25bc2eff91ef7d70 (diff) | |
download | lpeglabel-98e54ce388233170cd3a3feb2bcc4449670ccabc.tar.gz lpeglabel-98e54ce388233170cd3a3feb2bcc4449670ccabc.tar.bz2 lpeglabel-98e54ce388233170cd3a3feb2bcc4449670ccabc.zip |
Removing files related to the previous version (with labeled ordered choice)
-rw-r--r-- | examples/recoveryRe.lua | 141 | ||||
-rw-r--r-- | lpeglabel.html | 587 |
2 files changed, 0 insertions, 728 deletions
diff --git a/examples/recoveryRe.lua b/examples/recoveryRe.lua deleted file mode 100644 index 3b83d88..0000000 --- a/examples/recoveryRe.lua +++ /dev/null | |||
@@ -1,141 +0,0 @@ | |||
1 | local re = require"relabel" | ||
2 | |||
3 | -- The `errinfo` table contains the list of labels that we will be using | ||
4 | -- as well as the corresponding error message for each label, which will | ||
5 | -- be used in our error reporting later on. | ||
6 | local errinfo = { | ||
7 | {"NoExp", "no expression found"}, | ||
8 | {"Extra", "extra characters found after the expression"}, | ||
9 | {"ExpTerm", "expected a term after the operator"}, | ||
10 | {"ExpExp", "expected an expression after the parenthesis"}, | ||
11 | {"MisClose", "missing a closing ')' after the expression"}, | ||
12 | } | ||
13 | |||
14 | -- We split the errinfo table into two tables: `labels` which is a | ||
15 | -- mapping from the label names to its integer representation, and | ||
16 | -- `errmsgs` which is a mapping from the label names to its | ||
17 | -- corresponding error message. | ||
18 | local labels = {} | ||
19 | local errmsgs = {} | ||
20 | |||
21 | for i, err in ipairs(errinfo) do | ||
22 | labels[err[1]] = i | ||
23 | errmsgs[err[1]] = err[2] | ||
24 | end | ||
25 | |||
26 | -- The `labels` table is especially useful for making our re grammar more | ||
27 | -- readable through the use of the `setlabels` function which allows us | ||
28 | -- to use the label names directly in the re grammar instead of the integers. | ||
29 | re.setlabels(labels) | ||
30 | |||
31 | -- The `errors` table will hold the list of errors recorded during parsing | ||
32 | local errors = {} | ||
33 | |||
34 | -- The `recorderror` function simply records the label and position of | ||
35 | -- the failure (index in input string) into the `errors` table. | ||
36 | -- Note: The unused `input` parameter is necessary, as this will be called | ||
37 | -- by LPeg's match-time capture. | ||
38 | local function recorderror(input, pos, label) | ||
39 | table.insert(errors, {label, pos}) | ||
40 | return true | ||
41 | end | ||
42 | |||
43 | -- The `compute` function takes an alternating list of numbers and | ||
44 | -- operators and computes the result of applying the operations | ||
45 | -- to the numbers in a left to right order (no operator precedence). | ||
46 | local function compute(tokens) | ||
47 | local result = tokens[1] | ||
48 | for i = 2, #tokens, 2 do | ||
49 | if tokens[i] == '+' then | ||
50 | result = result + tokens[i+1] | ||
51 | elseif tokens[i] == '-' then | ||
52 | result = result - tokens[i+1] | ||
53 | elseif tokens[i] == '*' then | ||
54 | result = result * tokens[i+1] | ||
55 | elseif tokens[i] == '/' then | ||
56 | result = result / tokens[i+1] | ||
57 | else | ||
58 | error('unknown operation: ' .. tokens[i]) | ||
59 | end | ||
60 | end | ||
61 | return result | ||
62 | end | ||
63 | |||
64 | -- Our grammar is a simple arithmetic expression of integers that | ||
65 | -- does not take operator precedence into account but allows grouping | ||
66 | -- via parenthesis. We have incorporated some error recovery startegies | ||
67 | -- to our grammar so that it may resume parsing even after encountering | ||
68 | -- an error, which allows us to report more errors. | ||
69 | local g = re.compile([[ | ||
70 | S <- (Exp / ErrNoExp) (!. / ErrExtra) | ||
71 | Exp <- {| Term (op Operand)* |} -> compute | ||
72 | -- If we encounter a missing term/operand, we return a dummy instead. | ||
73 | Operand <- Term / ErrExpTerm /{ExpTerm} dummy | ||
74 | Term <- num / Group | ||
75 | -- If we encounter a missing closing parenthesis, we ignore it. | ||
76 | Group <- "(" InnerExp (")" / ErrMisClose /{MisClose} "") | ||
77 | -- If we encounter a missing inner expression, we skip to the next | ||
78 | -- closing parenthesis, and return a dummy in its place. | ||
79 | InnerExp <- Exp / ErrExpExp /{ExpExp} [^)]* dummy | ||
80 | |||
81 | op <- {[-+*/]} | ||
82 | num <- [0-9]+ -> tonumber | ||
83 | |||
84 | -- Before throwing an error, we make sure to record it first. | ||
85 | ErrNoExp <- ("" -> "NoExp" => recorderror) %{NoExp} | ||
86 | ErrExtra <- ("" -> "Extra" => recorderror) %{Extra} | ||
87 | ErrExpTerm <- ("" -> "ExpTerm" => recorderror) %{ExpTerm} | ||
88 | ErrExpExp <- ("" -> "ExpExp" => recorderror) %{ExpExp} | ||
89 | ErrMisClose <- ("" -> "MisClose" => recorderror) %{MisClose} | ||
90 | |||
91 | dummy <- "" -> "0" -> tonumber | ||
92 | ]], { | ||
93 | compute = compute; | ||
94 | recorderror = recorderror; | ||
95 | tonumber = tonumber; | ||
96 | }) | ||
97 | |||
98 | -- The `eval` function takes an input string to match against the grammar | ||
99 | -- we've just defined. If the input string matches, then the result of the | ||
100 | -- computation is returned, otherwise we return the error messages and | ||
101 | -- positions of all the failures encountered. | ||
102 | local function eval(input) | ||
103 | local result, label, suffix = g:match(input) | ||
104 | if #errors == 0 then | ||
105 | return result | ||
106 | else | ||
107 | local out = {} | ||
108 | for i, err in ipairs(errors) do | ||
109 | local pos = err[2] | ||
110 | local msg = errmsgs[err[1]] | ||
111 | local line, col = re.calcline(input, pos) | ||
112 | table.insert(out, "syntax error: " .. msg .. " (line " .. line .. ", col " .. col .. ")") | ||
113 | end | ||
114 | errors = {} | ||
115 | return nil, table.concat(out, "\n") | ||
116 | end | ||
117 | end | ||
118 | |||
119 | print(eval "98-76*(54/32)") | ||
120 | --> 37.125 | ||
121 | |||
122 | print(eval "(1+1-1*2/2") | ||
123 | --> syntax error: missing a closing ')' after the expression (line 1, col 10) | ||
124 | |||
125 | print(eval "(1+)-1*(2/2)") | ||
126 | --> syntax error: expected a term after the operator (line 1, col 4) | ||
127 | |||
128 | print(eval "(1+1)-1*(/2)") | ||
129 | --> syntax error: expected an expression after the parenthesis (line 1, col 10) | ||
130 | |||
131 | print(eval "1+(1-(1*2))/2x") | ||
132 | --> syntax error: extra characters found after the expression (line 1, col 14) | ||
133 | |||
134 | print(eval "-1+(1-(1*2))/2") | ||
135 | --> syntax error: no expression found (line 1, col 1) | ||
136 | |||
137 | print(eval "(1+1-1*(2/2+)-():") | ||
138 | --> syntax error: expected a term after the operator (line 1, col 13) | ||
139 | --> syntax error: expected an expression after the parenthesis (line 1, col 16) | ||
140 | --> syntax error: missing a closing ')' after the expression (line 1, col 17) | ||
141 | --> syntax error: extra characters found after the expression (line 1, col 17) | ||
diff --git a/lpeglabel.html b/lpeglabel.html deleted file mode 100644 index 59e744a..0000000 --- a/lpeglabel.html +++ /dev/null | |||
@@ -1,587 +0,0 @@ | |||
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" | ||
2 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
3 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | ||
4 | <head> | ||
5 | <title>LPegLabLabel - Parsing Expression Grammars For Lua</title> | ||
6 | <link rel="stylesheet" | ||
7 | href="http://www.inf.puc-rio.br/~roberto/lpeg/doc.css" | ||
8 | type="text/css"/> | ||
9 | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> | ||
10 | </head> | ||
11 | <body> | ||
12 | |||
13 | <div id="container"> | ||
14 | |||
15 | <div id="product"> | ||
16 | <div id="product_logo"> | ||
17 | <a href="https://github.com/sqmedeiros/lpeglabel"> | ||
18 | <img alt="LPegLabel logo" src="lpeglabel-logo.gif" width="128"/></a> | ||
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> | ||
53 | library that provides an implementation of Parsing Expression | ||
54 | Grammars (PEGs) with labeled failures. Labels can be | ||
55 | used to signal different kinds of erros and to | ||
56 | specify which alternative in a labeled ordered choice | ||
57 | should handle a given label. Labels can also be combined | ||
58 | with the standard patterns of LPeg. | ||
59 | </p> | ||
60 | |||
61 | <p> | ||
62 | This document describes the new functions available | ||
63 | in LpegLabel and presents some examples of usage. | ||
64 | In LPegLabel, the result of an unsuccessful matching | ||
65 | is a triple <code>nil, lab, sfail</code>, where <code>lab</code> | ||
66 | is the label associated with the failure, and | ||
67 | <code>sfail</code> is the suffix input where | ||
68 | the label was thrown. | ||
69 | </p> | ||
70 | |||
71 | <p> | ||
72 | Below there is a brief summary of the new functions | ||
73 | provided by LpegLabel: | ||
74 | </p> | ||
75 | <table border="1"> | ||
76 | <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> | ||
78 | <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> | ||
80 | <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> | ||
82 | </td></tr> | ||
83 | <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> | ||
85 | </td></tr> | ||
86 | <tr><td><a href="#re-lc"><code>p1 /{l<sub>1</sub>, ..., l<sub>n</sub>} 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> | ||
88 | </td></tr> | ||
89 | <tr><td><a href="#re-setl"><code>relabel.setlabels (tlabel)</code></a></td> | ||
90 | <td>Allows to specicify a table with mnemonic labels. | ||
91 | </td></tr> | ||
92 | </tbody></table> | ||
93 | |||
94 | <p> | ||
95 | For a more detailed and formal discussion about | ||
96 | PEGs with labels please see | ||
97 | <a href="http://www.inf.puc-rio.br/~roberto/docs/sblp2013-1.pdf"> | ||
98 | Exception Handling for Error Reporting in Parsing Expression Grammars</a>, | ||
99 | <a href="http://arxiv.org/abs/1405.6646">Error Reporting in Parsing Expression Grammars</a>, | ||
100 | and <a href="http://dx.doi.org/10.1145/2851613.2851750"> | ||
101 | A parsing machine for parsing expression grammars with labeled failures</a>. | ||
102 | </p> | ||
103 | |||
104 | <!-- | ||
105 | <p> | ||
106 | In case of an unsucessful matching, the <em>match</em> function returns | ||
107 | <code>nil</code> plus a list of labels. These labels may be used to build | ||
108 | a good error message. | ||
109 | </p> | ||
110 | --> | ||
111 | |||
112 | <h2><a name="func">Functions</a></h2> | ||
113 | |||
114 | |||
115 | <h3><a name="f-t"></a><code>lpeglabel.T(l)</code></h3> | ||
116 | <p> | ||
117 | Returns a pattern that throws the label <code>l</code>. | ||
118 | A label must be an integer between <code>0</code> and <code>63</code>. | ||
119 | |||
120 | The 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> | ||
125 | Returns a pattern equivalent to a <em>labeled ordered choice</em>. | ||
126 | If the matching of <code>p1</code> gives one of the labels <code>l<sub>1</sub>, ..., l<sub>n</sub></code>, | ||
127 | then the matching of <code>p2</code> is tried from the same position. Otherwise, | ||
128 | the result of the matching of <code>p1</code> is the pattern's result. | ||
129 | </p> | ||
130 | |||
131 | <p> | ||
132 | The labeled ordered choice <code>lpeg.Lc(p1, p2, 0)</code> is equivalent to the | ||
133 | regular ordered choice <code>p1 / p2</code>. | ||
134 | </p> | ||
135 | |||
136 | <p> | ||
137 | Although PEG's ordered choice is associative, the labeled ordered choice is not. | ||
138 | When using this function, the user should take care to build a left-associative | ||
139 | labeled ordered choice pattern. | ||
140 | </p> | ||
141 | |||
142 | |||
143 | <h3><a name="re-t"></a><code>%{l}</code></h3> | ||
144 | <p> | ||
145 | Syntax of <em>relabel</em> module. Equivalent to <code>lpeg.T(l)</code>. | ||
146 | </p> | ||
147 | |||
148 | |||
149 | <h3><a name="re-lc"></a><code>p1 /{l<sub>1</sub>, ..., l<sub>n</sub>} p2</code></h3> | ||
150 | <p> | ||
151 | Syntax of <em>relabel</em> module. Equivalent to <code>lpeg.Lc(p1, p2, l<sub>1</sub>, ..., l<sub>n</sub>)</code>. | ||
152 | </p> | ||
153 | |||
154 | <p> | ||
155 | The <code>/{}</code> operator is left-associative. | ||
156 | </p> | ||
157 | |||
158 | <p> | ||
159 | A grammar can use both choice operators (<code>/</code> and <code>/{}</code>), | ||
160 | but a single choice can not mix them. That is, the parser | ||
161 | of <code>relabel</code> module will not recognize a pattern as | ||
162 | <code>p1 / p2 /{l<sub>1</sub>} p3</code>. | ||
163 | </p> | ||
164 | |||
165 | |||
166 | <h3><a name="re-setl"></a><code>relabel.setlabels (tlabel)</code></h3> | ||
167 | |||
168 | <p>Allows to specicify a table with labels. They keys of | ||
169 | <code>tlabel</code> must be integers between <code>0</code> and <code>63</code>, | ||
170 | and the associated values should be strings. | ||
171 | </p> | ||
172 | |||
173 | |||
174 | |||
175 | <h2><a name="ex">Some Examples</a></h2> | ||
176 | |||
177 | <h3>Throwing a label</h3> | ||
178 | <p> | ||
179 | The following example defines a grammar that matches | ||
180 | a list of identifiers separated by commas. A label | ||
181 | is thrown when there is an error matching an identifier | ||
182 | or a comma: | ||
183 | </p> | ||
184 | <pre class="example"> | ||
185 | local m = require'lpeglabel' | ||
186 | |||
187 | local g = m.P{ | ||
188 | "S", | ||
189 | S = m.V"Id" * m.V"List", | ||
190 | List = -m.P(1) + ("," + m.T(2)) * m.V"Id" * m.V"List", | ||
191 | Id = m.R'az'^1 + m.T(1), | ||
192 | } | ||
193 | |||
194 | function mymatch (g, s) | ||
195 | local r, e = g:match(s) | ||
196 | if not r then | ||
197 | if e == 1 then | ||
198 | return "Error: expecting an identifier" | ||
199 | elseif e == 2 then | ||
200 | return "Error: expecting ','" | ||
201 | else | ||
202 | return "Error" | ||
203 | end | ||
204 | end | ||
205 | return r | ||
206 | end | ||
207 | |||
208 | print(mymatch(g, "a,b")) | ||
209 | print(mymatch(g, "a b")) | ||
210 | print(mymatch(g, ", b")) | ||
211 | </pre> | ||
212 | <p> | ||
213 | In this example we could think about writing rule <em>List</em> as follows: | ||
214 | <pre class="example"> | ||
215 | List = m.P(("," + m.T(2)) * m.V"Id")^0 | ||
216 | </pre> | ||
217 | but this would give us an expression that when matching | ||
218 | the end of input would result in a failure whose associated | ||
219 | label would be <em>2</em>. | ||
220 | </p> | ||
221 | |||
222 | <p> | ||
223 | In the previous example we could have also created a table | ||
224 | with the error messages to improve the readbility of the PEG. | ||
225 | Below we rewrite the grammar following this approach: | ||
226 | </p> | ||
227 | |||
228 | <pre class="example"> | ||
229 | local m = require'lpeglabel' | ||
230 | |||
231 | local errUndef = 0 | ||
232 | local errId = 1 | ||
233 | local errComma = 2 | ||
234 | |||
235 | local terror = { | ||
236 | [errUndef] = "Error", | ||
237 | [errId] = "Error: expecting an identifier", | ||
238 | [errComma] = "Error: expecting ','", | ||
239 | } | ||
240 | |||
241 | local g = m.P{ | ||
242 | "S", | ||
243 | S = m.V"Id" * m.V"List", | ||
244 | List = -m.P(1) + ("," + m.T(errComma)) * m.V"Id" * m.V"List", | ||
245 | Id = m.R'az'^1 + m.T(errId), | ||
246 | } | ||
247 | |||
248 | function mymatch (g, s) | ||
249 | local r, e = g:match(s) | ||
250 | if not r then | ||
251 | return terror[e] | ||
252 | end | ||
253 | return r | ||
254 | end | ||
255 | |||
256 | print(mymatch(g, "a,b")) | ||
257 | print(mymatch(g, "a b")) | ||
258 | print(mymatch(g, ", b")) | ||
259 | </pre> | ||
260 | |||
261 | <h3>Throwing a label using the <em>relabel</em> module</h3> | ||
262 | |||
263 | <p> | ||
264 | We can also rewrite the previous example using the <em>relabel</em> module | ||
265 | as follows: | ||
266 | </p> | ||
267 | <pre class="example"> | ||
268 | local re = require 'relabel' | ||
269 | |||
270 | local g = re.compile[[ | ||
271 | S <- Id List | ||
272 | List <- !. / (',' / %{2}) Id List | ||
273 | Id <- [a-z] / %{1} | ||
274 | ]] | ||
275 | |||
276 | function mymatch (g, s) | ||
277 | local r, e = g:match(s) | ||
278 | if not r then | ||
279 | if e == 1 then | ||
280 | return "Error: expecting an identifier" | ||
281 | elseif e == 2 then | ||
282 | return "Error: expecting ','" | ||
283 | else | ||
284 | return "Error" | ||
285 | end | ||
286 | end | ||
287 | return r | ||
288 | end | ||
289 | |||
290 | print(mymatch(g, "a,b")) | ||
291 | print(mymatch(g, "a b")) | ||
292 | print(mymatch(g, ", b")) | ||
293 | </pre> | ||
294 | |||
295 | <p> | ||
296 | Another way to describe the previous example using the <em>relabel</em> module | ||
297 | is by using a table with the description of the errors (<em>terror</em>) and | ||
298 | another table that associates a name to a given label (<em>tlabels</em>): | ||
299 | </p> | ||
300 | <pre class="example"> | ||
301 | local re = require 'relabel' | ||
302 | |||
303 | local errUndef, errId, errComma = 0, 1, 2 | ||
304 | |||
305 | local terror = { | ||
306 | [errUndef] = "Error", | ||
307 | [errId] = "Error: expecting an identifier", | ||
308 | [errComma] = "Error: expecting ','", | ||
309 | } | ||
310 | |||
311 | local tlabels = { ["errUndef"] = errUndef, | ||
312 | ["errId"] = errId, | ||
313 | ["errComma"] = errComma } | ||
314 | |||
315 | re.setlabels(tlabels) | ||
316 | |||
317 | local g = re.compile[[ | ||
318 | S <- Id List | ||
319 | List <- !. / (',' / %{errComma}) Id List | ||
320 | Id <- [a-z] / %{errId} | ||
321 | ]] | ||
322 | |||
323 | function mymatch (g, s) | ||
324 | local r, e = g:match(s) | ||
325 | if not r then | ||
326 | return terror[e] | ||
327 | end | ||
328 | return r | ||
329 | end | ||
330 | |||
331 | print(mymatch(g, "a,b")) | ||
332 | print(mymatch(g, "a b")) | ||
333 | print(mymatch(g, ", b")) | ||
334 | </pre> | ||
335 | |||
336 | |||
337 | |||
338 | <h3>Throwing and catching a label</h3> | ||
339 | |||
340 | <p> | ||
341 | When a label is thrown, the grammar itself can handle this label | ||
342 | by using the labeled ordered choice. Below we rewrite the example | ||
343 | of the list of identifiers to show this feature: | ||
344 | </p> | ||
345 | <pre class="example"> | ||
346 | local m = require'lpeglabel' | ||
347 | |||
348 | local errUndef, errId, errComma = 0, 1, 2 | ||
349 | |||
350 | local terror = { | ||
351 | [errUndef] = "Error", | ||
352 | [errId] = "Error: expecting an identifier", | ||
353 | [errComma] = "Error: expecting ','", | ||
354 | } | ||
355 | |||
356 | g = m.P{ | ||
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 | |||
367 | print(g:match("a,b")) | ||
368 | print(g:match("a b")) | ||
369 | print(g:match(",b")) | ||
370 | </pre> | ||
371 | |||
372 | <p> | ||
373 | As was pointed out <a href="#f-lc">before</a>, the labeled ordered | ||
374 | choice is not associative, so we should impose a left-associative | ||
375 | order when using function <code>Lc</code>. | ||
376 | </p> | ||
377 | <p> | ||
378 | Below we use the <em>re</em> module to throw and catch labels. | ||
379 | As was pointed out <a href="#re-lc">before</a>, the <code>/{}</code> | ||
380 | operator is left-associative, so we do not need to manually impose | ||
381 | a left-associative order as we did in the previous example that | ||
382 | used <code>Lc</code>: | ||
383 | </p> | ||
384 | <pre class="example"> | ||
385 | local re = require'relabel' | ||
386 | |||
387 | local terror = {} | ||
388 | |||
389 | local function newError(l, msg) | ||
390 | table.insert(terror, { l = l, msg = msg } ) | ||
391 | end | ||
392 | |||
393 | newError("errId", "Error: expecting an identifier") | ||
394 | newError("errComma", "Error: expecting ','") | ||
395 | |||
396 | local labelCode = {} | ||
397 | local labelMsg = {} | ||
398 | for k, v in ipairs(terror) do | ||
399 | labelCode[v.l] = k | ||
400 | labelMsg[v.l] = v.msg | ||
401 | end | ||
402 | |||
403 | re.setlabels(labelCode) | ||
404 | |||
405 | local 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 | |||
414 | print(p:match("a,b")) | ||
415 | print(p:match("a b")) | ||
416 | print(p:match(",b")) | ||
417 | </pre> | ||
418 | |||
419 | |||
420 | <h3>Tiny Language</h3> | ||
421 | <p> | ||
422 | As a more complex example, below we have the grammar | ||
423 | for the Tiny language, as described in | ||
424 | <a href="http://arxiv.org/abs/1405.6646">this</a> paper. | ||
425 | The example below can also show the line where the syntactic | ||
426 | error probably happened. | ||
427 | </p> | ||
428 | <pre class="example"> | ||
429 | local re = require 'relabel' | ||
430 | |||
431 | local terror = {} | ||
432 | |||
433 | local function newError(l, msg) | ||
434 | table.insert(terror, { l = l, msg = msg} ) | ||
435 | end | ||
436 | |||
437 | newError("errSemi", "Error: missing ';'") | ||
438 | newError("errExpIf", "Error: expected expression after 'if'") | ||
439 | newError("errThen", "Error: expected 'then' keyword") | ||
440 | newError("errCmdSeq1", "Error: expected at least a command after 'then'") | ||
441 | newError("errCmdSeq2", "Error: expected at least a command after 'else'") | ||
442 | newError("errEnd", "Error: expected 'end' keyword") | ||
443 | newError("errCmdSeqRep", "Error: expected at least a command after 'repeat'") | ||
444 | newError("errUntil", "Error: expected 'until' keyword") | ||
445 | newError("errExpRep", "Error: expected expression after 'until'") | ||
446 | newError("errAssignOp", "Error: expected ':=' in assigment") | ||
447 | newError("errExpAssign", "Error: expected expression after ':='") | ||
448 | newError("errReadName", "Error: expected an identifier after 'read'") | ||
449 | newError("errWriteExp", "Error: expected expression after 'write'") | ||
450 | newError("errSimpExp", "Error: expected '(', ID, or number after '<' or '='") | ||
451 | newError("errTerm", "Error: expected '(', ID, or number after '+' or '-'") | ||
452 | newError("errFactor", "Error: expected '(', ID, or number after '*' or '/'") | ||
453 | newError("errExpFac", "Error: expected expression after '('") | ||
454 | newError("errClosePar", "Error: expected ')' after expression") | ||
455 | |||
456 | local line | ||
457 | |||
458 | local function incLine() | ||
459 | line = line + 1 | ||
460 | return true | ||
461 | end | ||
462 | |||
463 | local function countLine(s, i) | ||
464 | line = 1 | ||
465 | local p = re.compile([[ | ||
466 | S <- (%nl -> incLine / .)* | ||
467 | ]], { incLine = incLine}) | ||
468 | p:match(s:sub(1, i)) | ||
469 | return true | ||
470 | end | ||
471 | |||
472 | local labelCode = {} | ||
473 | for k, v in ipairs(terror) do | ||
474 | labelCode[v.l] = k | ||
475 | end | ||
476 | |||
477 | re.setlabels(labelCode) | ||
478 | |||
479 | local g = re.compile([[ | ||
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> | ||
546 | The MIT License (MIT) | ||
547 | </p> | ||
548 | <p> | ||
549 | Copyright (c) 2014-2015 Sérgio Medeiros | ||
550 | </p> | ||
551 | <p> | ||
552 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
553 | of this software and associated documentation files (the "Software"), to deal | ||
554 | in the Software without restriction, including without limitation the rights | ||
555 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
556 | copies of the Software, and to permit persons to whom the Software is | ||
557 | furnished to do so, subject to the following conditions: | ||
558 | </p> | ||
559 | <p> | ||
560 | The above copyright notice and this permission notice shall be included in all | ||
561 | copies or substantial portions of the Software. | ||
562 | </p> | ||
563 | <p> | ||
564 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
565 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
566 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
567 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
568 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
569 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
570 | SOFTWARE. | ||
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> | ||