diff options
| -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> | ||
