aboutsummaryrefslogtreecommitdiff
path: root/examples/recoveryRe.lua
diff options
context:
space:
mode:
Diffstat (limited to 'examples/recoveryRe.lua')
-rw-r--r--examples/recoveryRe.lua34
1 files changed, 33 insertions, 1 deletions
diff --git a/examples/recoveryRe.lua b/examples/recoveryRe.lua
index 50ec53a..795e01d 100644
--- a/examples/recoveryRe.lua
+++ b/examples/recoveryRe.lua
@@ -1,5 +1,8 @@
1local re = require"relabel" 1local re = require"relabel"
2 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.
3local errinfo = { 6local errinfo = {
4 {"NoExp", "no expression found"}, 7 {"NoExp", "no expression found"},
5 {"Extra", "extra characters found after the expression"}, 8 {"Extra", "extra characters found after the expression"},
@@ -8,6 +11,10 @@ local errinfo = {
8 {"MisClose", "missing a closing ')' after the expression"}, 11 {"MisClose", "missing a closing ')' after the expression"},
9} 12}
10 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.
11local labels = {} 18local labels = {}
12local errmsgs = {} 19local errmsgs = {}
13 20
@@ -16,15 +23,26 @@ for i, err in ipairs(errinfo) do
16 errmsgs[err[1]] = err[2] 23 errmsgs[err[1]] = err[2]
17end 24end
18 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.
19re.setlabels(labels) 29re.setlabels(labels)
20 30
31-- The `errors` table will hold the list of errors recorded during parsing
21local errors = {} 32local errors = {}
22 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.
23function recordError(input, pos, label) 38function recordError(input, pos, label)
24 table.insert(errors, {label, pos}) 39 table.insert(errors, {label, pos})
25 return true 40 return true
26end 41end
27 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).
28local function compute(tokens) 46local function compute(tokens)
29 local result = tokens[1] 47 local result = tokens[1]
30 for i = 2, #tokens, 2 do 48 for i = 2, #tokens, 2 do
@@ -43,17 +61,27 @@ local function compute(tokens)
43 return result 61 return result
44end 62end
45 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.
46local g = re.compile([[ 69local g = re.compile([[
47 S <- (Exp / ErrNoExp) (!. / ErrExtra) 70 S <- (Exp / ErrNoExp) (!. / ErrExtra)
48 Exp <- {| Term (op Operand)* |} -> compute 71 Exp <- {| Term (op Operand)* |} -> compute
72 -- If we encounter a missing term/operand, we return a dummy instead.
49 Operand <- Term / ErrExpTerm /{ExpTerm} dummy 73 Operand <- Term / ErrExpTerm /{ExpTerm} dummy
50 Term <- num / Group 74 Term <- num / Group
75 -- If we encounter a missing closing parenthesis, we ignore it.
51 Group <- "(" InnerExp (")" / ErrMisClose /{MisClose} "") 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.
52 InnerExp <- Exp / ErrExpExp /{ExpExp} [^)]* dummy 79 InnerExp <- Exp / ErrExpExp /{ExpExp} [^)]* dummy
53 80
54 op <- {[-+*/]} 81 op <- {[-+*/]}
55 num <- [0-9]+ -> tonumber 82 num <- [0-9]+ -> tonumber
56 83
84 -- Before throwing an error, we make sure to record it first.
57 ErrNoExp <- ("" -> "NoExp" => recordError) %{NoExp} 85 ErrNoExp <- ("" -> "NoExp" => recordError) %{NoExp}
58 ErrExtra <- ("" -> "Extra" => recordError) %{Extra} 86 ErrExtra <- ("" -> "Extra" => recordError) %{Extra}
59 ErrExpTerm <- ("" -> "ExpTerm" => recordError) %{ExpTerm} 87 ErrExpTerm <- ("" -> "ExpTerm" => recordError) %{ExpTerm}
@@ -67,6 +95,10 @@ local g = re.compile([[
67 tonumber = tonumber; 95 tonumber = tonumber;
68}) 96})
69 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.
70local function eval(input) 102local function eval(input)
71 local result, label, suffix = g:match(input) 103 local result, label, suffix = g:match(input)
72 if #errors == 0 then 104 if #errors == 0 then