diff options
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/expRec.lua | 128 | ||||
| -rw-r--r-- | examples/expRecAut.lua | 122 | ||||
| -rw-r--r-- | examples/expect.lua | 98 | ||||
| -rw-r--r-- | examples/listId1.lua | 12 | ||||
| -rw-r--r-- | examples/listId2.lua | 13 | ||||
| -rw-r--r-- | examples/listId2Rec2.lua | 67 | ||||
| -rw-r--r-- | examples/listId2Rec2Cap.lua | 79 | ||||
| -rw-r--r-- | examples/listIdCatch.lua | 28 | ||||
| -rw-r--r-- | examples/listIdCatchRe.lua | 34 | ||||
| -rw-r--r-- | examples/listIdRe1.lua | 8 | ||||
| -rw-r--r-- | examples/listIdRe2.lua | 61 | ||||
| -rw-r--r-- | examples/recovery.lua | 134 | ||||
| -rw-r--r-- | examples/recoveryOpFail.lua | 105 | ||||
| -rw-r--r-- | examples/recoveryOpLab.lua | 107 | ||||
| -rw-r--r-- | examples/tiny.lua | 84 | ||||
| -rwxr-xr-x | examples/typedlua/test.lua | 9 | ||||
| -rw-r--r-- | examples/typedlua/tllexer.lua | 11 | ||||
| -rw-r--r-- | examples/typedlua/tlparser.lua | 2 |
18 files changed, 509 insertions, 593 deletions
diff --git a/examples/expRec.lua b/examples/expRec.lua new file mode 100644 index 0000000..5c5fd7d --- /dev/null +++ b/examples/expRec.lua | |||
| @@ -0,0 +1,128 @@ | |||
| 1 | local m = require"lpeglabelrec" | ||
| 2 | local re = require"relabelrec" | ||
| 3 | |||
| 4 | local labels = { | ||
| 5 | {"ExpTermFirst", "expected an expression"}, | ||
| 6 | {"ExpTermOp", "expected a term after the operator"}, | ||
| 7 | {"MisClose", "missing a closing ')' after the expression"}, | ||
| 8 | } | ||
| 9 | |||
| 10 | local function labelindex(labname) | ||
| 11 | for i, elem in ipairs(labels) do | ||
| 12 | if elem[1] == labname then | ||
| 13 | return i | ||
| 14 | end | ||
| 15 | end | ||
| 16 | error("could not find label: " .. labname) | ||
| 17 | end | ||
| 18 | |||
| 19 | local errors, subject | ||
| 20 | |||
| 21 | local function expect(patt, labname) | ||
| 22 | local i = labelindex(labname) | ||
| 23 | return patt + m.T(i) | ||
| 24 | end | ||
| 25 | |||
| 26 | |||
| 27 | local num = m.R("09")^1 / tonumber | ||
| 28 | local op = m.S("+-") | ||
| 29 | |||
| 30 | local function compute(tokens) | ||
| 31 | local result = tokens[1] | ||
| 32 | for i = 2, #tokens, 2 do | ||
| 33 | if tokens[i] == '+' then | ||
| 34 | result = result + tokens[i+1] | ||
| 35 | elseif tokens[i] == '-' then | ||
| 36 | result = result - tokens[i+1] | ||
| 37 | else | ||
| 38 | error('unknown operation: ' .. tokens[i]) | ||
| 39 | end | ||
| 40 | end | ||
| 41 | return result | ||
| 42 | end | ||
| 43 | |||
| 44 | local g = m.P { | ||
| 45 | "Exp", | ||
| 46 | Exp = m.Ct(m.V"OperandFirst" * (m.C(op) * m.V"Operand")^0) / compute, | ||
| 47 | OperandFirst = expect(m.V"Term", "ExpTermFirst"), | ||
| 48 | Operand = expect(m.V"Term", "ExpTermOp"), | ||
| 49 | Term = num + m.V"Group", | ||
| 50 | Group = "(" * m.V"Exp" * expect(")", "MisClose"), | ||
| 51 | } | ||
| 52 | |||
| 53 | function recorderror(pos, lab) | ||
| 54 | local line, col = re.calcline(subject, pos) | ||
| 55 | table.insert(errors, { line = line, col = col, msg = labels[lab][2] }) | ||
| 56 | end | ||
| 57 | |||
| 58 | function record (labname) | ||
| 59 | return (m.Cp() * m.Cc(labelindex(labname))) / recorderror | ||
| 60 | end | ||
| 61 | |||
| 62 | function sync (p) | ||
| 63 | return (-p * m.P(1))^0 | ||
| 64 | end | ||
| 65 | |||
| 66 | function defaultValue (p) | ||
| 67 | return p or m.Cc(1000) | ||
| 68 | end | ||
| 69 | |||
| 70 | local grec = m.P { | ||
| 71 | "S", | ||
| 72 | S = m.Rec(m.V"A", m.V"ErrExpTermFirst", labelindex("ExpTermFirst")), -- default value is 0 | ||
| 73 | A = m.Rec(m.V"Sg", m.V"ErrExpTermOp", labelindex("ExpTermOp")), | ||
| 74 | Sg = m.Rec(g, m.V"ErrMisClose", labelindex("MisClose")), | ||
| 75 | ErrExpTermFirst = record("ExpTermFirst") * sync(op + ")") * defaultValue(), | ||
| 76 | ErrExpTermOp = record("ExpTermOp") * sync(op + ")") * defaultValue(), | ||
| 77 | ErrMisClose = record("MisClose") * sync(m.P")") * defaultValue(m.P""), | ||
| 78 | } | ||
| 79 | |||
| 80 | local function eval(input) | ||
| 81 | errors = {} | ||
| 82 | io.write("Input: ", input, "\n") | ||
| 83 | subject = input | ||
| 84 | local result, label, suffix = grec:match(input) | ||
| 85 | io.write("Syntactic errors found: " .. #errors, "\n") | ||
| 86 | if #errors > 0 then | ||
| 87 | local out = {} | ||
| 88 | for i, err in ipairs(errors) do | ||
| 89 | local pos = err.col | ||
| 90 | local msg = err.msg | ||
| 91 | table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") | ||
| 92 | end | ||
| 93 | print(table.concat(out, "\n")) | ||
| 94 | end | ||
| 95 | io.write("Result = ") | ||
| 96 | return result | ||
| 97 | end | ||
| 98 | |||
| 99 | print(eval "90-70-(5)+3") | ||
| 100 | --> 20 | ||
| 101 | |||
| 102 | print(eval "15+") | ||
| 103 | --> 2 + 0 | ||
| 104 | |||
| 105 | print(eval "-2") | ||
| 106 | --> 0 - 2 | ||
| 107 | |||
| 108 | print(eval "1+3+-9") | ||
| 109 | --> 1 + 3 + [0] - 9 | ||
| 110 | |||
| 111 | print(eval "1+()3+") | ||
| 112 | --> 1 + ([0]) [3 +] [0] | ||
| 113 | |||
| 114 | print(eval "8-(2+)-5") | ||
| 115 | --> 8 - (2 + [0]) - 5 | ||
| 116 | |||
| 117 | print(eval "()") | ||
| 118 | |||
| 119 | print(eval "") | ||
| 120 | |||
| 121 | print(eval "1+()+") | ||
| 122 | |||
| 123 | print(eval "1+(") | ||
| 124 | |||
| 125 | print(eval "3)") | ||
| 126 | |||
| 127 | print(eval "11+())3") | ||
| 128 | |||
diff --git a/examples/expRecAut.lua b/examples/expRecAut.lua new file mode 100644 index 0000000..f870d73 --- /dev/null +++ b/examples/expRecAut.lua | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | local m = require"lpeglabelrec" | ||
| 2 | local re = require"relabelrec" | ||
| 3 | |||
| 4 | local num = m.R("09")^1 / tonumber | ||
| 5 | local op = m.S("+-") | ||
| 6 | |||
| 7 | local labels = {} | ||
| 8 | local nlabels = 0 | ||
| 9 | |||
| 10 | local function newError(lab, msg, psync, pcap) | ||
| 11 | nlabels = nlabels + 1 | ||
| 12 | psync = psync or m.P(-1) | ||
| 13 | pcap = pcap or m.P"" | ||
| 14 | labels[lab] = { id = nlabels, msg = msg, psync = psync, pcap = pcap } | ||
| 15 | end | ||
| 16 | |||
| 17 | newError("ExpTermFirst", "expected an expression", op + ")", m.Cc(1000)) | ||
| 18 | newError("ExpTermOp", "expected a term after the operator", op + ")", m.Cc(1000)) | ||
| 19 | newError("MisClose", "missing a closing ')' after the expression", m.P")") | ||
| 20 | newError("Extra", "extra characters found after the expression") | ||
| 21 | |||
| 22 | local errors, subject | ||
| 23 | |||
| 24 | local function expect(patt, labname) | ||
| 25 | local i = labels[labname].id | ||
| 26 | return patt + m.T(i) | ||
| 27 | end | ||
| 28 | |||
| 29 | local function compute(tokens) | ||
| 30 | local result = tokens[1] | ||
| 31 | for i = 2, #tokens, 2 do | ||
| 32 | if tokens[i] == '+' then | ||
| 33 | result = result + tokens[i+1] | ||
| 34 | elseif tokens[i] == '-' then | ||
| 35 | result = result - tokens[i+1] | ||
| 36 | else | ||
| 37 | error('unknown operation: ' .. tokens[i]) | ||
| 38 | end | ||
| 39 | end | ||
| 40 | return result | ||
| 41 | end | ||
| 42 | |||
| 43 | local g = m.P { | ||
| 44 | "Exp", | ||
| 45 | Exp = m.Ct(m.V"OperandFirst" * (m.C(op) * m.V"Operand")^0) / compute, | ||
| 46 | OperandFirst = expect(m.V"Term", "ExpTermFirst"), | ||
| 47 | Operand = expect(m.V"Term", "ExpTermOp"), | ||
| 48 | Term = num + m.V"Group", | ||
| 49 | Group = "(" * m.V"Exp" * expect(")", "MisClose"), | ||
| 50 | } | ||
| 51 | |||
| 52 | function recorderror(pos, lab) | ||
| 53 | local line, col = re.calcline(subject, pos) | ||
| 54 | table.insert(errors, { line = line, col = col, msg = labels[lab].msg }) | ||
| 55 | end | ||
| 56 | |||
| 57 | function record (labname) | ||
| 58 | return (m.Cp() * m.Cc(labname)) / recorderror | ||
| 59 | end | ||
| 60 | |||
| 61 | function sync (p) | ||
| 62 | return (-p * m.P(1))^0 | ||
| 63 | end | ||
| 64 | |||
| 65 | function defaultValue (p) | ||
| 66 | return p or m.Cc(1000) | ||
| 67 | end | ||
| 68 | |||
| 69 | local grec = g * expect(m.P(-1), "Extra") | ||
| 70 | for k, v in pairs(labels) do | ||
| 71 | grec = m.Rec(grec, record(k) * sync(v.psync) * v.pcap, v.id) | ||
| 72 | end | ||
| 73 | |||
| 74 | local function eval(input) | ||
| 75 | errors = {} | ||
| 76 | io.write("Input: ", input, "\n") | ||
| 77 | subject = input | ||
| 78 | local result, label, suffix = grec:match(input) | ||
| 79 | io.write("Syntactic errors found: " .. #errors, "\n") | ||
| 80 | if #errors > 0 then | ||
| 81 | local out = {} | ||
| 82 | for i, err in ipairs(errors) do | ||
| 83 | local pos = err.col | ||
| 84 | local msg = err.msg | ||
| 85 | table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") | ||
| 86 | end | ||
| 87 | print(table.concat(out, "\n")) | ||
| 88 | end | ||
| 89 | io.write("Result = ") | ||
| 90 | return result | ||
| 91 | end | ||
| 92 | |||
| 93 | print(eval "90-70-(5)+3") | ||
| 94 | --> 18 | ||
| 95 | |||
| 96 | print(eval "15+") | ||
| 97 | --> 2 + 0 | ||
| 98 | |||
| 99 | print(eval "-2") | ||
| 100 | --> 0 - 2 | ||
| 101 | |||
| 102 | print(eval "1+3+-9") | ||
| 103 | --> 1 + 3 + [0] - 9 | ||
| 104 | |||
| 105 | print(eval "1+()3+") | ||
| 106 | --> 1 + ([0]) [+] 3 + [0] | ||
| 107 | |||
| 108 | print(eval "8-(2+)-5") | ||
| 109 | --> 8 - (2 + [0]) - 5 | ||
| 110 | |||
| 111 | print(eval "()") | ||
| 112 | |||
| 113 | print(eval "") | ||
| 114 | |||
| 115 | print(eval "1+()+") | ||
| 116 | |||
| 117 | print(eval "1+(") | ||
| 118 | |||
| 119 | print(eval "3)") | ||
| 120 | |||
| 121 | print(eval "11+()3") | ||
| 122 | --> 1 + ([0]) [+] 3 + [0] | ||
diff --git a/examples/expect.lua b/examples/expect.lua deleted file mode 100644 index cb68d38..0000000 --- a/examples/expect.lua +++ /dev/null | |||
| @@ -1,98 +0,0 @@ | |||
| 1 | local lpeg = require"lpeglabel" | ||
| 2 | |||
| 3 | local R, S, P, V, C, Ct, T = lpeg.R, lpeg.S, lpeg.P, lpeg.V, lpeg.C, lpeg.Ct, lpeg.T | ||
| 4 | |||
| 5 | -- The `labels` table contains the list of labels that we will be using | ||
| 6 | -- as well as the corresponding error message for each label, which will | ||
| 7 | -- be used in our error reporting later on. | ||
| 8 | local labels = { | ||
| 9 | {"NoExp", "no expression found"}, | ||
| 10 | {"Extra", "extra characters found after the expression"}, | ||
| 11 | {"ExpTerm", "expected a term after the operator"}, | ||
| 12 | {"ExpExp", "expected an expression after the parenthesis"}, | ||
| 13 | {"MisClose", "missing a closing ')' after the expression"}, | ||
| 14 | } | ||
| 15 | |||
| 16 | -- The `expect` function takes a pattern and a label defined in | ||
| 17 | -- the `labels` table and returns a pattern that throws the specified | ||
| 18 | -- label if the original pattern fails to match. | ||
| 19 | -- Note: LPegLabel requires us to use integers for the labels, so we | ||
| 20 | -- use the index of the label in the `labels` table to represent it. | ||
| 21 | local function expect(patt, labname) | ||
| 22 | for i, elem in ipairs(labels) do | ||
| 23 | if elem[1] == labname then | ||
| 24 | return patt + T(i) | ||
| 25 | end | ||
| 26 | end | ||
| 27 | |||
| 28 | error("could not find label: " .. labname) | ||
| 29 | end | ||
| 30 | |||
| 31 | local num = R("09")^1 / tonumber | ||
| 32 | local op = S("+-*/") | ||
| 33 | |||
| 34 | -- The `compute` function takes an alternating list of numbers and | ||
| 35 | -- operators and computes the result of applying the operations | ||
| 36 | -- to the numbers in a left to right order (no operator precedence). | ||
| 37 | local function compute(tokens) | ||
| 38 | local result = tokens[1] | ||
| 39 | for i = 2, #tokens, 2 do | ||
| 40 | if tokens[i] == '+' then | ||
| 41 | result = result + tokens[i+1] | ||
| 42 | elseif tokens[i] == '-' then | ||
| 43 | result = result - tokens[i+1] | ||
| 44 | elseif tokens[i] == '*' then | ||
| 45 | result = result * tokens[i+1] | ||
| 46 | elseif tokens[i] == '/' then | ||
| 47 | result = result / tokens[i+1] | ||
| 48 | else | ||
| 49 | error('unknown operation: ' .. tokens[i]) | ||
| 50 | end | ||
| 51 | end | ||
| 52 | return result | ||
| 53 | end | ||
| 54 | |||
| 55 | -- Our grammar is a simple arithmetic expression of integers that | ||
| 56 | -- does not take operator precedence into account but allows grouping | ||
| 57 | -- via parenthesis. | ||
| 58 | local g = P { | ||
| 59 | "Exp", | ||
| 60 | Exp = Ct(V"Term" * (C(op) * expect(V"Term", "ExpTerm"))^0) / compute; | ||
| 61 | Term = num + V"Group"; | ||
| 62 | Group = "(" * expect(V"Exp", "ExpExp") * expect(")", "MisClose"); | ||
| 63 | } | ||
| 64 | |||
| 65 | g = expect(g, "NoExp") * expect(-P(1), "Extra") | ||
| 66 | |||
| 67 | -- The `eval` function takes an input string to match against the grammar | ||
| 68 | -- we've just defined. If the input string matches, then the result of the | ||
| 69 | -- computation is returned, otherwise we return the error message and | ||
| 70 | -- position of the first failure encountered. | ||
| 71 | local function eval(input) | ||
| 72 | local result, label, suffix = g:match(input) | ||
| 73 | if result ~= nil then | ||
| 74 | return result | ||
| 75 | else | ||
| 76 | local pos = input:len() - suffix:len() + 1 | ||
| 77 | local msg = labels[label][2] | ||
| 78 | return nil, "syntax error: " .. msg .. " (at index " .. pos .. ")" | ||
| 79 | end | ||
| 80 | end | ||
| 81 | |||
| 82 | print(eval "98-76*(54/32)") | ||
| 83 | --> 37.125 | ||
| 84 | |||
| 85 | print(eval "(1+1-1*2/2") | ||
| 86 | --> syntax error: missing a closing ')' after the expression (at index 11) | ||
| 87 | |||
| 88 | print(eval "(1+)-1*(2/2)") | ||
| 89 | --> syntax error: expected a term after the operator (at index 4) | ||
| 90 | |||
| 91 | print(eval "(1+1)-1*(/2)") | ||
| 92 | --> syntax error: expected an expression after the parenthesis (at index 10) | ||
| 93 | |||
| 94 | print(eval "1+(1-(1*2))/2x") | ||
| 95 | --> syntax error: extra chracters found after the expression (at index 14) | ||
| 96 | |||
| 97 | print(eval "-1+(1-(1*2))/2") | ||
| 98 | --> syntax error: no expression found (at index 1) | ||
diff --git a/examples/listId1.lua b/examples/listId1.lua index 8976f5f..dee46e9 100644 --- a/examples/listId1.lua +++ b/examples/listId1.lua | |||
| @@ -1,12 +1,14 @@ | |||
| 1 | local m = require'lpeglabel' | 1 | local m = require'lpeglabelrec' |
| 2 | local re = require'relabel' | 2 | local re = require'relabelrec' |
| 3 | |||
| 4 | local id = m.R'az'^1 | ||
| 3 | 5 | ||
| 4 | local g = m.P{ | 6 | local g = m.P{ |
| 5 | "S", | 7 | "S", |
| 6 | S = m.V"Id" * m.V"List", | 8 | S = m.V"Id" * m.V"List", |
| 7 | List = -m.P(1) + (m.V"Comma" + m.T(2)) * (m.V"Id" + m.T(1)) * m.V"List", | 9 | List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List", |
| 8 | Id = m.V"Sp" * m.R'az'^1, | 10 | Id = m.V"Sp" * id + m.T(1), |
| 9 | Comma = m.V"Sp" * ",", | 11 | Comma = m.V"Sp" * "," + m.T(2), |
| 10 | Sp = m.S" \n\t"^0, | 12 | Sp = m.S" \n\t"^0, |
| 11 | } | 13 | } |
| 12 | 14 | ||
diff --git a/examples/listId2.lua b/examples/listId2.lua index 509fda4..46f0063 100644 --- a/examples/listId2.lua +++ b/examples/listId2.lua | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | local m = require'lpeglabel' | 1 | local m = require'lpeglabelrec' |
| 2 | local re = require'relabel' | 2 | local re = require'relabelrec' |
| 3 | 3 | ||
| 4 | local terror = {} | 4 | local terror = {} |
| 5 | 5 | ||
| @@ -12,15 +12,18 @@ local errUndef = newError("undefined") | |||
| 12 | local errId = newError("expecting an identifier") | 12 | local errId = newError("expecting an identifier") |
| 13 | local errComma = newError("expecting ','") | 13 | local errComma = newError("expecting ','") |
| 14 | 14 | ||
| 15 | local id = m.R'az'^1 | ||
| 16 | |||
| 15 | local g = m.P{ | 17 | local g = m.P{ |
| 16 | "S", | 18 | "S", |
| 17 | S = m.V"Id" * m.V"List", | 19 | S = m.V"Id" * m.V"List", |
| 18 | List = -m.P(1) + (m.V"Comma" + m.T(errComma)) * (m.V"Id" + m.T(errId)) * m.V"List", | 20 | List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List", |
| 19 | Id = m.V"Sp" * m.R'az'^1, | 21 | Id = m.V"Sp" * id + m.T(errId), |
| 20 | Comma = m.V"Sp" * ",", | 22 | Comma = m.V"Sp" * "," + m.T(errComma), |
| 21 | Sp = m.S" \n\t"^0, | 23 | Sp = m.S" \n\t"^0, |
| 22 | } | 24 | } |
| 23 | 25 | ||
| 26 | |||
| 24 | function mymatch (g, s) | 27 | function mymatch (g, s) |
| 25 | local r, e, sfail = g:match(s) | 28 | local r, e, sfail = g:match(s) |
| 26 | if not r then | 29 | if not r then |
diff --git a/examples/listId2Rec2.lua b/examples/listId2Rec2.lua new file mode 100644 index 0000000..c6705dd --- /dev/null +++ b/examples/listId2Rec2.lua | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | local m = require'lpeglabelrec' | ||
| 2 | local re = require'relabelrec' | ||
| 3 | |||
| 4 | local terror = {} | ||
| 5 | |||
| 6 | local function newError(s) | ||
| 7 | table.insert(terror, s) | ||
| 8 | return #terror | ||
| 9 | end | ||
| 10 | |||
| 11 | local errUndef = newError("undefined") | ||
| 12 | local errId = newError("expecting an identifier") | ||
| 13 | local errComma = newError("expecting ','") | ||
| 14 | |||
| 15 | local id = m.R'az'^1 | ||
| 16 | |||
| 17 | local g = m.P{ | ||
| 18 | "S", | ||
| 19 | S = m.V"Id" * m.V"List", | ||
| 20 | List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List", | ||
| 21 | Id = m.V"Sp" * id + m.T(errId), | ||
| 22 | Comma = m.V"Sp" * "," + m.T(errComma), | ||
| 23 | Sp = m.S" \n\t"^0, | ||
| 24 | } | ||
| 25 | |||
| 26 | local subject, errors | ||
| 27 | |||
| 28 | function recorderror(pos, lab) | ||
| 29 | local line, col = re.calcline(subject, pos) | ||
| 30 | table.insert(errors, { line = line, col = col, msg = terror[lab] }) | ||
| 31 | end | ||
| 32 | |||
| 33 | function record (lab) | ||
| 34 | return (m.Cp() * m.Cc(lab)) / recorderror | ||
| 35 | end | ||
| 36 | |||
| 37 | function sync (p) | ||
| 38 | return (-p * m.P(1))^0 | ||
| 39 | end | ||
| 40 | |||
| 41 | local grec = m.P{ | ||
| 42 | "S", | ||
| 43 | S = m.Rec(m.Rec(g, m.V"ErrComma", errComma), m.V"ErrId", errId), | ||
| 44 | ErrComma = record(errComma) * sync(id), | ||
| 45 | ErrId = record(errId) * sync(m.P",") | ||
| 46 | } | ||
| 47 | |||
| 48 | |||
| 49 | function mymatch (g, s) | ||
| 50 | errors = {} | ||
| 51 | subject = s | ||
| 52 | local r, e, sfail = g:match(s) | ||
| 53 | if #errors > 0 then | ||
| 54 | local out = {} | ||
| 55 | for i, err in ipairs(errors) do | ||
| 56 | local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg | ||
| 57 | table.insert(out, msg) | ||
| 58 | end | ||
| 59 | return nil, table.concat(out, "\n") .. "\n" | ||
| 60 | end | ||
| 61 | return r | ||
| 62 | end | ||
| 63 | |||
| 64 | print(mymatch(grec, "one,two")) | ||
| 65 | print(mymatch(grec, "one two three")) | ||
| 66 | print(mymatch(grec, "1,\n two, \n3,")) | ||
| 67 | print(mymatch(grec, "one\n two123, \nthree,")) | ||
diff --git a/examples/listId2Rec2Cap.lua b/examples/listId2Rec2Cap.lua new file mode 100644 index 0000000..1c22c88 --- /dev/null +++ b/examples/listId2Rec2Cap.lua | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | local m = require'lpeglabelrec' | ||
| 2 | local re = require'relabelrec' | ||
| 3 | |||
| 4 | local terror = {} | ||
| 5 | |||
| 6 | local function newError(s) | ||
| 7 | table.insert(terror, s) | ||
| 8 | return #terror | ||
| 9 | end | ||
| 10 | |||
| 11 | local errUndef = newError("undefined") | ||
| 12 | local errId = newError("expecting an identifier") | ||
| 13 | local errComma = newError("expecting ','") | ||
| 14 | |||
| 15 | local id = m.R'az'^1 | ||
| 16 | |||
| 17 | local g = m.P{ | ||
| 18 | "S", | ||
| 19 | S = m.V"Id" * m.V"List", | ||
| 20 | List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List", | ||
| 21 | Id = m.V"Sp" * m.C(id) + m.T(errId), | ||
| 22 | Comma = m.V"Sp" * "," + m.T(errComma), | ||
| 23 | Sp = m.S" \n\t"^0, | ||
| 24 | } | ||
| 25 | |||
| 26 | local subject, errors | ||
| 27 | |||
| 28 | function recorderror(pos, lab) | ||
| 29 | local line, col = re.calcline(subject, pos) | ||
| 30 | table.insert(errors, { line = line, col = col, msg = terror[lab] }) | ||
| 31 | end | ||
| 32 | |||
| 33 | function record (lab) | ||
| 34 | return (m.Cp() * m.Cc(lab)) / recorderror | ||
| 35 | end | ||
| 36 | |||
| 37 | function sync (p) | ||
| 38 | return (-p * m.P(1))^0 | ||
| 39 | end | ||
| 40 | |||
| 41 | function defaultValue () | ||
| 42 | return m.Cc"NONE" | ||
| 43 | end | ||
| 44 | |||
| 45 | local grec = m.P{ | ||
| 46 | "S", | ||
| 47 | S = m.Rec(m.Rec(g, m.V"ErrComma", errComma), m.V"ErrId", errId), | ||
| 48 | ErrComma = record(errComma) * sync(id), | ||
| 49 | ErrId = record(errId) * sync(m.P",") * defaultValue(), | ||
| 50 | } | ||
| 51 | |||
| 52 | |||
| 53 | function mymatch (g, s) | ||
| 54 | errors = {} | ||
| 55 | subject = s | ||
| 56 | io.write("Input: ", s, "\n") | ||
| 57 | local r = { g:match(s) } | ||
| 58 | io.write("Captures (separated by ';'): ") | ||
| 59 | for k, v in pairs(r) do | ||
| 60 | io.write(v .. "; ") | ||
| 61 | end | ||
| 62 | io.write("\nSyntactic errors found: " .. #errors) | ||
| 63 | if #errors > 0 then | ||
| 64 | io.write("\n") | ||
| 65 | local out = {} | ||
| 66 | for i, err in ipairs(errors) do | ||
| 67 | local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg | ||
| 68 | table.insert(out, msg) | ||
| 69 | end | ||
| 70 | io.write(table.concat(out, "\n")) | ||
| 71 | end | ||
| 72 | print("\n") | ||
| 73 | return r | ||
| 74 | end | ||
| 75 | |||
| 76 | mymatch(grec, "one,two") | ||
| 77 | mymatch(grec, "one two three") | ||
| 78 | mymatch(grec, "1,\n two, \n3,") | ||
| 79 | mymatch(grec, "one\n two123, \nthree,") | ||
diff --git a/examples/listIdCatch.lua b/examples/listIdCatch.lua deleted file mode 100644 index 5ad6f2d..0000000 --- a/examples/listIdCatch.lua +++ /dev/null | |||
| @@ -1,28 +0,0 @@ | |||
| 1 | local m = require'lpeglabel' | ||
| 2 | |||
| 3 | local terror = {} | ||
| 4 | |||
| 5 | local function newError(s) | ||
| 6 | table.insert(terror, s) | ||
| 7 | return #terror | ||
| 8 | end | ||
| 9 | |||
| 10 | local errUndef = newError("undefined") | ||
| 11 | local errId = newError("expecting an identifier") | ||
| 12 | local errComma = newError("expecting ','") | ||
| 13 | |||
| 14 | local g = m.P{ | ||
| 15 | "S", | ||
| 16 | S = m.Lc(m.Lc(m.V"Id" * m.V"List", m.V"ErrId", errId), | ||
| 17 | m.V"ErrComma", errComma), | ||
| 18 | List = -m.P(1) + (m.V"Comma" + m.T(errComma)) * (m.V"Id" + m.T(errId)) * m.V"List", | ||
| 19 | Id = m.V"Sp" * m.R'az'^1, | ||
| 20 | Comma = m.V"Sp" * ",", | ||
| 21 | Sp = m.S" \n\t"^0, | ||
| 22 | ErrId = m.Cc(errId) / terror, | ||
| 23 | ErrComma = m.Cc(errComma) / terror | ||
| 24 | } | ||
| 25 | |||
| 26 | print(m.match(g, "one,two")) | ||
| 27 | print(m.match(g, "one two")) | ||
| 28 | print(m.match(g, "one,\n two,\nthree,")) | ||
diff --git a/examples/listIdCatchRe.lua b/examples/listIdCatchRe.lua deleted file mode 100644 index 8971191..0000000 --- a/examples/listIdCatchRe.lua +++ /dev/null | |||
| @@ -1,34 +0,0 @@ | |||
| 1 | local re = require'relabel' | ||
| 2 | |||
| 3 | local terror = {} | ||
| 4 | |||
| 5 | local function newError(l, msg) | ||
| 6 | table.insert(terror, { l = l, msg = msg } ) | ||
| 7 | end | ||
| 8 | |||
| 9 | newError("errId", "Error: expecting an identifier") | ||
| 10 | newError("errComma", "Error: expecting ','") | ||
| 11 | |||
| 12 | local labelCode = {} | ||
| 13 | local labelMsg = {} | ||
| 14 | for k, v in ipairs(terror) do | ||
| 15 | labelCode[v.l] = k | ||
| 16 | labelMsg[v.l] = v.msg | ||
| 17 | end | ||
| 18 | |||
| 19 | re.setlabels(labelCode) | ||
| 20 | |||
| 21 | local p = re.compile([[ | ||
| 22 | S <- Id List /{errId} ErrId /{errComma} ErrComma | ||
| 23 | List <- !. / Comma Id List | ||
| 24 | Id <- [a-z]+ / %{errId} | ||
| 25 | Comma <- ',' / %{errComma} | ||
| 26 | ErrId <- '' -> errId | ||
| 27 | ErrComma <- '' -> errComma | ||
| 28 | ]], labelMsg) | ||
| 29 | |||
| 30 | print(p:match("a,b")) | ||
| 31 | print(p:match("a b")) | ||
| 32 | print(p:match(",b")) | ||
| 33 | |||
| 34 | |||
diff --git a/examples/listIdRe1.lua b/examples/listIdRe1.lua index d092566..3988a3b 100644 --- a/examples/listIdRe1.lua +++ b/examples/listIdRe1.lua | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | local re = require 'relabel' | 1 | local re = require 'relabelrec' |
| 2 | 2 | ||
| 3 | local g = re.compile[[ | 3 | local g = re.compile[[ |
| 4 | S <- Id List | 4 | S <- Id List |
| 5 | List <- !. / (',' / %{2}) (Id / %{1}) List | 5 | List <- !. / Comma Id List |
| 6 | Id <- Sp [a-z]+ | 6 | Id <- Sp [a-z]+ / %{2} |
| 7 | Comma <- Sp ',' | 7 | Comma <- Sp ',' / %{3} |
| 8 | Sp <- %s* | 8 | Sp <- %s* |
| 9 | ]] | 9 | ]] |
| 10 | 10 | ||
diff --git a/examples/listIdRe2.lua b/examples/listIdRe2.lua index fe30535..6bab6ba 100644 --- a/examples/listIdRe2.lua +++ b/examples/listIdRe2.lua | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | local re = require 'relabel' | 1 | local re = require 'relabelrec' |
| 2 | 2 | ||
| 3 | local errinfo = { | 3 | local errinfo = { |
| 4 | {"errUndef", "undefined"}, | 4 | {"errUndef", "undefined"}, |
| @@ -18,23 +18,54 @@ re.setlabels(labels) | |||
| 18 | 18 | ||
| 19 | local g = re.compile[[ | 19 | local g = re.compile[[ |
| 20 | S <- Id List | 20 | S <- Id List |
| 21 | List <- !. / (',' / %{errComma}) (Id / %{errId}) List | 21 | List <- !. / Comma Id List |
| 22 | Id <- Sp [a-z]+ | 22 | Id <- Sp {[a-z]+} / %{errId} |
| 23 | Comma <- Sp ',' | 23 | Comma <- Sp ',' / %{errComma} |
| 24 | Sp <- %s* | 24 | Sp <- %s* |
| 25 | ]] | 25 | ]] |
| 26 | 26 | ||
| 27 | function mymatch (g, s) | 27 | local errors |
| 28 | local r, e, sfail = g:match(s) | 28 | |
| 29 | if not r then | 29 | function recorderror (subject, pos, label) |
| 30 | local line, col = re.calcline(s, #s - #sfail) | 30 | local line, col = re.calcline(subject, pos) |
| 31 | local msg = "Error at line " .. line .. " (col " .. col .. "): " | 31 | table.insert(errors, { line = line, col = col, msg = errmsgs[labels[label]] }) |
| 32 | return r, msg .. errmsgs[e] .. " before '" .. sfail .. "'" | 32 | return true |
| 33 | end | 33 | end |
| 34 | return r | 34 | |
| 35 | function sync (p) | ||
| 36 | return '( !(' .. p .. ') .)*' | ||
| 35 | end | 37 | end |
| 36 | 38 | ||
| 37 | print(mymatch(g, "one,two")) | 39 | local grec = re.compile( |
| 38 | print(mymatch(g, "one two")) | 40 | "S <- %g //{errComma} ErrComma //{errId} ErrId" .. "\n" .. |
| 39 | print(mymatch(g, "one,\n two,\nthree,")) | 41 | "ErrComma <- ('' -> 'errComma' => recorderror) " .. sync('[a-z]+') .. "\n" .. |
| 42 | "ErrId <- ('' -> 'errId' => recorderror) " .. sync('","') .. "-> default" | ||
| 43 | , {g = g, recorderror = recorderror, default = "NONE"}) | ||
| 40 | 44 | ||
| 45 | function mymatch (g, s) | ||
| 46 | errors = {} | ||
| 47 | subject = s | ||
| 48 | io.write("Input: ", s, "\n") | ||
| 49 | local r = { g:match(s) } | ||
| 50 | io.write("Captures (separated by ';'): ") | ||
| 51 | for k, v in pairs(r) do | ||
| 52 | io.write(v .. "; ") | ||
| 53 | end | ||
| 54 | io.write("\nSyntactic errors found: " .. #errors) | ||
| 55 | if #errors > 0 then | ||
| 56 | io.write("\n") | ||
| 57 | local out = {} | ||
| 58 | for i, err in ipairs(errors) do | ||
| 59 | local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg | ||
| 60 | table.insert(out, msg) | ||
| 61 | end | ||
| 62 | io.write(table.concat(out, "\n")) | ||
| 63 | end | ||
| 64 | print("\n") | ||
| 65 | return r | ||
| 66 | end | ||
| 67 | |||
| 68 | mymatch(grec, "one,two") | ||
| 69 | mymatch(grec, "one two three") | ||
| 70 | mymatch(grec, "1,\n two, \n3,") | ||
| 71 | mymatch(grec, "one\n two123, \nthree,") | ||
diff --git a/examples/recovery.lua b/examples/recovery.lua deleted file mode 100644 index 3272ae7..0000000 --- a/examples/recovery.lua +++ /dev/null | |||
| @@ -1,134 +0,0 @@ | |||
| 1 | local lpeg = require"lpeglabel" | ||
| 2 | |||
| 3 | local R, S, P, V = lpeg.R, lpeg.S, lpeg.P, lpeg.V | ||
| 4 | local C, Cc, Ct, Cmt = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cmt | ||
| 5 | local T, Lc = lpeg.T, lpeg.Lc | ||
| 6 | |||
| 7 | -- The `labels` table contains the list of labels that we will be using | ||
| 8 | -- as well as the corresponding error message for each label, which will | ||
| 9 | -- be used in our error reporting later on. | ||
| 10 | local labels = { | ||
| 11 | {"NoExp", "no expression found"}, | ||
| 12 | {"Extra", "extra characters found after the expression"}, | ||
| 13 | {"ExpTerm", "expected a term after the operator"}, | ||
| 14 | {"ExpExp", "expected an expression after the parenthesis"}, | ||
| 15 | {"MisClose", "missing a closing ')' after the expression"}, | ||
| 16 | } | ||
| 17 | |||
| 18 | -- The `labelindex` function gives us the index of a label in the | ||
| 19 | -- `labels` table, which serves as the integer representation of the label. | ||
| 20 | -- We need this because LPegLabel requires us to use integers for the labels. | ||
| 21 | local function labelindex(labname) | ||
| 22 | for i, elem in ipairs(labels) do | ||
| 23 | if elem[1] == labname then | ||
| 24 | return i | ||
| 25 | end | ||
| 26 | end | ||
| 27 | error("could not find label: " .. labname) | ||
| 28 | end | ||
| 29 | |||
| 30 | -- The `errors` table will hold the list of errors recorded during parsing | ||
| 31 | local errors = {} | ||
| 32 | |||
| 33 | -- The `expect` function takes a pattern and a label and returns a pattern | ||
| 34 | -- that throws the specified label if the original pattern fails to match. | ||
| 35 | -- Before throwing the label, it records the label to be thrown along with | ||
| 36 | -- the position of the failure (index in input string) into the `errors` table. | ||
| 37 | local function expect(patt, labname) | ||
| 38 | local i = labelindex(labname) | ||
| 39 | function recorderror(input, pos) | ||
| 40 | table.insert(errors, {i, pos}) | ||
| 41 | return true | ||
| 42 | end | ||
| 43 | return patt + Cmt("", recorderror) * T(i) | ||
| 44 | end | ||
| 45 | |||
| 46 | local num = R("09")^1 / tonumber | ||
| 47 | local op = S("+-*/") | ||
| 48 | |||
| 49 | -- The `compute` function takes an alternating list of numbers and | ||
| 50 | -- operators and computes the result of applying the operations | ||
| 51 | -- to the numbers in a left to right order (no operator precedence). | ||
| 52 | local function compute(tokens) | ||
| 53 | local result = tokens[1] | ||
| 54 | for i = 2, #tokens, 2 do | ||
| 55 | if tokens[i] == '+' then | ||
| 56 | result = result + tokens[i+1] | ||
| 57 | elseif tokens[i] == '-' then | ||
| 58 | result = result - tokens[i+1] | ||
| 59 | elseif tokens[i] == '*' then | ||
| 60 | result = result * tokens[i+1] | ||
| 61 | elseif tokens[i] == '/' then | ||
| 62 | result = result / tokens[i+1] | ||
| 63 | else | ||
| 64 | error('unknown operation: ' .. tokens[i]) | ||
| 65 | end | ||
| 66 | end | ||
| 67 | return result | ||
| 68 | end | ||
| 69 | |||
| 70 | -- Our grammar is a simple arithmetic expression of integers that | ||
| 71 | -- does not take operator precedence into account but allows grouping | ||
| 72 | -- via parenthesis. We have incorporated some error recovery startegies | ||
| 73 | -- to our grammar so that it may resume parsing even after encountering | ||
| 74 | -- an error, which allows us to report more errors. | ||
| 75 | local g = P { | ||
| 76 | "Exp", | ||
| 77 | Exp = Ct(V"Term" * (C(op) * V"OpRecov")^0) / compute; | ||
| 78 | -- `OpRecov` handles missing terms/operands by returning a dummy (zero). | ||
| 79 | OpRecov = Lc(V"Operand", Cc(0), labelindex("ExpTerm")); | ||
| 80 | Operand = expect(V"Term", "ExpTerm"); | ||
| 81 | Term = num + V"Group"; | ||
| 82 | -- `Group` handles missing closing parenthesis by simply ignoring it. | ||
| 83 | -- Like all the others, the error is still recorded of course. | ||
| 84 | Group = "(" * V"InnerExp" * Lc(expect(")", "MisClose"), P"", labelindex("MisClose")); | ||
| 85 | -- `InnerExp` handles missing expressions by skipping to the next closing | ||
| 86 | -- parenthesis. A dummy (zero) is returned in place of the expression. | ||
| 87 | InnerExp = Lc(expect(V"Exp", "ExpExp"), (P(1) - ")")^0 * Cc(0), labelindex("ExpExp")); | ||
| 88 | } | ||
| 89 | |||
| 90 | g = expect(g, "NoExp") * expect(-P(1), "Extra") | ||
| 91 | |||
| 92 | -- The `eval` function takes an input string to match against the grammar | ||
| 93 | -- we've just defined. If the input string matches, then the result of the | ||
| 94 | -- computation is returned, otherwise we return the error messages and | ||
| 95 | -- positions of all the failures encountered. | ||
| 96 | local function eval(input) | ||
| 97 | local result, label, suffix = g:match(input) | ||
| 98 | if #errors == 0 then | ||
| 99 | return result | ||
| 100 | else | ||
| 101 | local out = {} | ||
| 102 | for i, err in ipairs(errors) do | ||
| 103 | local pos = err[2] | ||
| 104 | local msg = labels[err[1]][2] | ||
| 105 | table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") | ||
| 106 | end | ||
| 107 | errors = {} | ||
| 108 | return nil, table.concat(out, "\n") | ||
| 109 | end | ||
| 110 | end | ||
| 111 | |||
| 112 | print(eval "98-76*(54/32)") | ||
| 113 | --> 37.125 | ||
| 114 | |||
| 115 | print(eval "(1+1-1*2/2") | ||
| 116 | --> syntax error: missing a closing ')' after the expression (at index 11) | ||
| 117 | |||
| 118 | print(eval "(1+)-1*(2/2)") | ||
| 119 | --> syntax error: expected a term after the operator (at index 4) | ||
| 120 | |||
| 121 | print(eval "(1+1)-1*(/2)") | ||
| 122 | --> syntax error: expected an expression after the parenthesis (at index 10) | ||
| 123 | |||
| 124 | print(eval "1+(1-(1*2))/2x") | ||
| 125 | --> syntax error: extra chracters found after the expression (at index 14) | ||
| 126 | |||
| 127 | print(eval "-1+(1-(1*2))/2") | ||
| 128 | --> syntax error: no expression found (at index 1) | ||
| 129 | |||
| 130 | print(eval "(1+1-1*(2/2+)-():") | ||
| 131 | --> syntax error: expected a term after the operator (at index 13) | ||
| 132 | --> syntax error: expected an expression after the parenthesis (at index 16) | ||
| 133 | --> syntax error: missing a closing ')' after the expression (at index 17) | ||
| 134 | --> syntax error: extra characters found after the expression (at index 17) | ||
diff --git a/examples/recoveryOpFail.lua b/examples/recoveryOpFail.lua deleted file mode 100644 index 6ddc6a2..0000000 --- a/examples/recoveryOpFail.lua +++ /dev/null | |||
| @@ -1,105 +0,0 @@ | |||
| 1 | local lpeg = require"lpeglabel" | ||
| 2 | |||
| 3 | local R, S, P, V = lpeg.R, lpeg.S, lpeg.P, lpeg.V | ||
| 4 | local C, Cc, Ct, Cmt, Carg = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cmt, lpeg.Carg | ||
| 5 | local T, Lc, Rec = lpeg.T, lpeg.Lc, lpeg.Rec | ||
| 6 | |||
| 7 | local labels = { | ||
| 8 | {"NoExp", "no expression found"}, | ||
| 9 | {"Extra", "extra characters found after the expression"}, | ||
| 10 | {"ExpTerm", "expected a term after the operator"}, | ||
| 11 | {"ExpExp", "expected an expression after the parenthesis"}, | ||
| 12 | {"MisClose", "missing a closing ')' after the expression"}, | ||
| 13 | } | ||
| 14 | |||
| 15 | local function labelindex(labname) | ||
| 16 | for i, elem in ipairs(labels) do | ||
| 17 | if elem[1] == labname then | ||
| 18 | return i | ||
| 19 | end | ||
| 20 | end | ||
| 21 | error("could not find label: " .. labname) | ||
| 22 | end | ||
| 23 | |||
| 24 | local function expect(patt, labname, recpatt) | ||
| 25 | local i = labelindex(labname) | ||
| 26 | local function recorderror(input, pos, errors) | ||
| 27 | table.insert(errors, {i, pos}) | ||
| 28 | return true | ||
| 29 | end | ||
| 30 | if not recpatt then recpatt = P"" end | ||
| 31 | return Rec(patt, Cmt(Carg(1), recorderror) * recpatt) | ||
| 32 | end | ||
| 33 | |||
| 34 | local num = R("09")^1 / tonumber | ||
| 35 | local op = S("+-*/") | ||
| 36 | |||
| 37 | local function compute(tokens) | ||
| 38 | local result = tokens[1] | ||
| 39 | for i = 2, #tokens, 2 do | ||
| 40 | if tokens[i] == '+' then | ||
| 41 | result = result + tokens[i+1] | ||
| 42 | elseif tokens[i] == '-' then | ||
| 43 | result = result - tokens[i+1] | ||
| 44 | elseif tokens[i] == '*' then | ||
| 45 | result = result * tokens[i+1] | ||
| 46 | elseif tokens[i] == '/' then | ||
| 47 | result = result / tokens[i+1] | ||
| 48 | else | ||
| 49 | error('unknown operation: ' .. tokens[i]) | ||
| 50 | end | ||
| 51 | end | ||
| 52 | return result | ||
| 53 | end | ||
| 54 | |||
| 55 | |||
| 56 | local g = P { | ||
| 57 | "Exp", | ||
| 58 | Exp = Ct(V"Term" * (C(op) * V"Operand")^0) / compute; | ||
| 59 | Operand = expect(V"Term", "ExpTerm", Cc(0)); | ||
| 60 | Term = num + V"Group"; | ||
| 61 | Group = "(" * V"InnerExp" * expect(")", "MisClose"); | ||
| 62 | InnerExp = expect(V"Exp", "ExpExp", (P(1) - ")")^0 * Cc(0)); | ||
| 63 | } | ||
| 64 | |||
| 65 | g = expect(g, "NoExp", P(1)^0) * expect(-P(1), "Extra") | ||
| 66 | |||
| 67 | local function eval(input) | ||
| 68 | local errors = {} | ||
| 69 | local result, label, suffix = g:match(input, 1, errors) | ||
| 70 | if #errors == 0 then | ||
| 71 | return result | ||
| 72 | else | ||
| 73 | local out = {} | ||
| 74 | for i, err in ipairs(errors) do | ||
| 75 | local pos = err[2] | ||
| 76 | local msg = labels[err[1]][2] | ||
| 77 | table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") | ||
| 78 | end | ||
| 79 | return nil, table.concat(out, "\n") | ||
| 80 | end | ||
| 81 | end | ||
| 82 | |||
| 83 | print(eval "98-76*(54/32)") | ||
| 84 | --> 37.125 | ||
| 85 | |||
| 86 | print(eval "(1+1-1*2/2") | ||
| 87 | --> syntax error: missing a closing ')' after the expression (at index 11) | ||
| 88 | |||
| 89 | print(eval "(1+)-1*(2/2)") | ||
| 90 | --> syntax error: expected a term after the operator (at index 4) | ||
| 91 | |||
| 92 | print(eval "(1+1)-1*(/2)") | ||
| 93 | --> syntax error: expected an expression after the parenthesis (at index 10) | ||
| 94 | |||
| 95 | print(eval "1+(1-(1*2))/2x") | ||
| 96 | --> syntax error: extra chracters found after the expression (at index 14) | ||
| 97 | |||
| 98 | print(eval "-1+(1-(1*2))/2") | ||
| 99 | --> syntax error: no expression found (at index 1) | ||
| 100 | |||
| 101 | print(eval "(1+1-1*(2/2+)-():") | ||
| 102 | --> syntax error: expected a term after the operator (at index 13) | ||
| 103 | --> syntax error: expected an expression after the parenthesis (at index 16) | ||
| 104 | --> syntax error: missing a closing ')' after the expression (at index 17) | ||
| 105 | --> syntax error: extra characters found after the expression (at index 17) | ||
diff --git a/examples/recoveryOpLab.lua b/examples/recoveryOpLab.lua deleted file mode 100644 index 6697f8b..0000000 --- a/examples/recoveryOpLab.lua +++ /dev/null | |||
| @@ -1,107 +0,0 @@ | |||
| 1 | local lpeg = require"lpeglabel" | ||
| 2 | |||
| 3 | local R, S, P, V = lpeg.R, lpeg.S, lpeg.P, lpeg.V | ||
| 4 | local C, Cc, Ct, Cmt = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cmt | ||
| 5 | local T, Lc, Rec = lpeg.T, lpeg.Lc, lpeg.Rec | ||
| 6 | |||
| 7 | local labels = { | ||
| 8 | {"NoExp", "no expression found"}, | ||
| 9 | {"Extra", "extra characters found after the expression"}, | ||
| 10 | {"ExpTerm", "expected a term after the operator"}, | ||
| 11 | {"ExpExp", "expected an expression after the parenthesis"}, | ||
| 12 | {"MisClose", "missing a closing ')' after the expression"}, | ||
| 13 | } | ||
| 14 | |||
| 15 | local function labelindex(labname) | ||
| 16 | for i, elem in ipairs(labels) do | ||
| 17 | if elem[1] == labname then | ||
| 18 | return i | ||
| 19 | end | ||
| 20 | end | ||
| 21 | error("could not find label: " .. labname) | ||
| 22 | end | ||
| 23 | |||
| 24 | local errors = {} | ||
| 25 | |||
| 26 | local function expect(patt, labname) | ||
| 27 | local i = labelindex(labname) | ||
| 28 | function recorderror(input, pos) | ||
| 29 | table.insert(errors, {i, pos}) | ||
| 30 | return true | ||
| 31 | end | ||
| 32 | return patt + Cmt("", recorderror) * T(i) | ||
| 33 | end | ||
| 34 | |||
| 35 | local num = R("09")^1 / tonumber | ||
| 36 | local op = S("+-*/") | ||
| 37 | |||
| 38 | local function compute(tokens) | ||
| 39 | local result = tokens[1] | ||
| 40 | for i = 2, #tokens, 2 do | ||
| 41 | if tokens[i] == '+' then | ||
| 42 | result = result + tokens[i+1] | ||
| 43 | elseif tokens[i] == '-' then | ||
| 44 | result = result - tokens[i+1] | ||
| 45 | elseif tokens[i] == '*' then | ||
| 46 | result = result * tokens[i+1] | ||
| 47 | elseif tokens[i] == '/' then | ||
| 48 | result = result / tokens[i+1] | ||
| 49 | else | ||
| 50 | error('unknown operation: ' .. tokens[i]) | ||
| 51 | end | ||
| 52 | end | ||
| 53 | return result | ||
| 54 | end | ||
| 55 | |||
| 56 | |||
| 57 | local g = P { | ||
| 58 | "Exp", | ||
| 59 | Exp = Ct(V"Term" * (C(op) * V"OpRecov")^0) / compute; | ||
| 60 | OpRecov = Rec(V"Operand", Cc(0), labelindex("ExpTerm")); | ||
| 61 | Operand = expect(V"Term", "ExpTerm"); | ||
| 62 | Term = num + Rec(V"Group", P"", labelindex("MisClose")); | ||
| 63 | Group = "(" * Rec(V"InnerExp", (P(1) - ")")^0 * Cc(0), labelindex("ExpExp")) * expect(")", "MisClose"); | ||
| 64 | InnerExp = expect(V"Exp", "ExpExp"); | ||
| 65 | } | ||
| 66 | |||
| 67 | g = expect(g, "NoExp") * expect(-P(1), "Extra") | ||
| 68 | |||
| 69 | local function eval(input) | ||
| 70 | local result, label, suffix = g:match(input) | ||
| 71 | if #errors == 0 then | ||
| 72 | return result | ||
| 73 | else | ||
| 74 | local out = {} | ||
| 75 | for i, err in ipairs(errors) do | ||
| 76 | local pos = err[2] | ||
| 77 | local msg = labels[err[1]][2] | ||
| 78 | table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") | ||
| 79 | end | ||
| 80 | errors = {} | ||
| 81 | return nil, table.concat(out, "\n") | ||
| 82 | end | ||
| 83 | end | ||
| 84 | |||
| 85 | print(eval "98-76*(54/32)") | ||
| 86 | --> 37.125 | ||
| 87 | |||
| 88 | print(eval "(1+1-1*2/2") | ||
| 89 | --> syntax error: missing a closing ')' after the expression (at index 11) | ||
| 90 | |||
| 91 | print(eval "(1+)-1*(2/2)") | ||
| 92 | --> syntax error: expected a term after the operator (at index 4) | ||
| 93 | |||
| 94 | print(eval "(1+1)-1*(/2)") | ||
| 95 | --> syntax error: expected an expression after the parenthesis (at index 10) | ||
| 96 | |||
| 97 | print(eval "1+(1-(1*2))/2x") | ||
| 98 | --> syntax error: extra chracters found after the expression (at index 14) | ||
| 99 | |||
| 100 | print(eval "-1+(1-(1*2))/2") | ||
| 101 | --> syntax error: no expression found (at index 1) | ||
| 102 | |||
| 103 | print(eval "(1+1-1*(2/2+)-():") | ||
| 104 | --> syntax error: expected a term after the operator (at index 13) | ||
| 105 | --> syntax error: expected an expression after the parenthesis (at index 16) | ||
| 106 | --> syntax error: missing a closing ')' after the expression (at index 17) | ||
| 107 | --> syntax error: extra characters found after the expression (at index 17) | ||
diff --git a/examples/tiny.lua b/examples/tiny.lua index 99c3144..784e031 100644 --- a/examples/tiny.lua +++ b/examples/tiny.lua | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | local re = require 'relabel' | 1 | local re = require 'relabelrec' |
| 2 | 2 | ||
| 3 | local terror = {} | 3 | local terror = {} |
| 4 | 4 | ||
| @@ -25,21 +25,6 @@ newError("errFactor", "Error: expected '(', ID, or number after '*' or '/'") | |||
| 25 | newError("errExpFac", "Error: expected expression after '('") | 25 | newError("errExpFac", "Error: expected expression after '('") |
| 26 | newError("errClosePar", "Error: expected ')' after expression") | 26 | newError("errClosePar", "Error: expected ')' after expression") |
| 27 | 27 | ||
| 28 | local line | ||
| 29 | |||
| 30 | local function incLine() | ||
| 31 | line = line + 1 | ||
| 32 | return true | ||
| 33 | end | ||
| 34 | |||
| 35 | local function countLine(s, i) | ||
| 36 | line = 1 | ||
| 37 | local p = re.compile([[ | ||
| 38 | S <- (%nl -> incLine / .)* | ||
| 39 | ]], { incLine = incLine}) | ||
| 40 | p:match(s:sub(1, i)) | ||
| 41 | return true | ||
| 42 | end | ||
| 43 | 28 | ||
| 44 | local labelCode = {} | 29 | local labelCode = {} |
| 45 | for k, v in ipairs(terror) do | 30 | for k, v in ipairs(terror) do |
| @@ -48,7 +33,7 @@ end | |||
| 48 | 33 | ||
| 49 | re.setlabels(labelCode) | 34 | re.setlabels(labelCode) |
| 50 | 35 | ||
| 51 | local g = re.compile([[ | 36 | local g = re.compile[[ |
| 52 | Tiny <- CmdSeq | 37 | Tiny <- CmdSeq |
| 53 | CmdSeq <- (Cmd (SEMICOLON / ErrSemi)) (Cmd (SEMICOLON / ErrSemi))* | 38 | CmdSeq <- (Cmd (SEMICOLON / ErrSemi)) (Cmd (SEMICOLON / ErrSemi))* |
| 54 | Cmd <- IfCmd / RepeatCmd / ReadCmd / WriteCmd / AssignCmd | 39 | Cmd <- IfCmd / RepeatCmd / ReadCmd / WriteCmd / AssignCmd |
| @@ -61,25 +46,24 @@ local g = re.compile([[ | |||
| 61 | SimpleExp <- Term ((ADD / SUB) (Term / ErrTerm))* | 46 | SimpleExp <- Term ((ADD / SUB) (Term / ErrTerm))* |
| 62 | Term <- Factor ((MUL / DIV) (Factor / ErrFactor))* | 47 | Term <- Factor ((MUL / DIV) (Factor / ErrFactor))* |
| 63 | Factor <- OPENPAR (Exp / ErrExpFac) (CLOSEPAR / ErrClosePar) / NUMBER / NAME | 48 | Factor <- OPENPAR (Exp / ErrExpFac) (CLOSEPAR / ErrClosePar) / NUMBER / NAME |
| 64 | ErrSemi <- ErrCount %{errSemi} | 49 | ErrSemi <- %{errSemi} |
| 65 | ErrExpIf <- ErrCount %{errExpIf} | 50 | ErrExpIf <- %{errExpIf} |
| 66 | ErrThen <- ErrCount %{errThen} | 51 | ErrThen <- %{errThen} |
| 67 | ErrCmdSeq1 <- ErrCount %{errCmdSeq1} | 52 | ErrCmdSeq1 <- %{errCmdSeq1} |
| 68 | ErrCmdSeq2 <- ErrCount %{errCmdSeq2} | 53 | ErrCmdSeq2 <- %{errCmdSeq2} |
| 69 | ErrEnd <- ErrCount %{errEnd} | 54 | ErrEnd <- %{errEnd} |
| 70 | ErrCmdSeqRep <- ErrCount %{errCmdSeqRep} | 55 | ErrCmdSeqRep <- %{errCmdSeqRep} |
| 71 | ErrUntil <- ErrCount %{errUntil} | 56 | ErrUntil <- %{errUntil} |
| 72 | ErrExpRep <- ErrCount %{errExpRep} | 57 | ErrExpRep <- %{errExpRep} |
| 73 | ErrAssignOp <- ErrCount %{errAssignOp} | 58 | ErrAssignOp <- %{errAssignOp} |
| 74 | ErrExpAssign <- ErrCount %{errExpAssign} | 59 | ErrExpAssign <- %{errExpAssign} |
| 75 | ErrReadName <- ErrCount %{errReadName} | 60 | ErrReadName <- %{errReadName} |
| 76 | ErrWriteExp <- ErrCount %{errWriteExp} | 61 | ErrWriteExp <- %{errWriteExp} |
| 77 | ErrSimpExp <- ErrCount %{errSimpExp} | 62 | ErrSimpExp <- %{errSimpExp} |
| 78 | ErrTerm <- ErrCount %{errTerm} | 63 | ErrTerm <- %{errTerm} |
| 79 | ErrFactor <- ErrCount %{errFactor} | 64 | ErrFactor <- %{errFactor} |
| 80 | ErrExpFac <- ErrCount %{errExpFac} | 65 | ErrExpFac <- %{errExpFac} |
| 81 | ErrClosePar <- ErrCount %{errClosePar} | 66 | ErrClosePar <- %{errClosePar} |
| 82 | ErrCount <- '' => countLine | ||
| 83 | ADD <- Sp '+' | 67 | ADD <- Sp '+' |
| 84 | ASSIGNMENT <- Sp ':=' | 68 | ASSIGNMENT <- Sp ':=' |
| 85 | CLOSEPAR <- Sp ')' | 69 | CLOSEPAR <- Sp ')' |
| @@ -102,12 +86,17 @@ local g = re.compile([[ | |||
| 102 | WRITE <- Sp 'write' | 86 | WRITE <- Sp 'write' |
| 103 | RESERVED <- (IF / ELSE / END / READ / REPEAT / THEN / UNTIL / WRITE) ![a-z]+ | 87 | RESERVED <- (IF / ELSE / END / READ / REPEAT / THEN / UNTIL / WRITE) ![a-z]+ |
| 104 | Sp <- %s* | 88 | Sp <- %s* |
| 105 | ]], { countLine = countLine }) | 89 | ]] |
| 106 | 90 | ||
| 107 | 91 | ||
| 108 | local function printError(n, e) | 92 | local function mymatch(g, s) |
| 109 | assert(n == nil) | 93 | local r, e, sfail = g:match(s) |
| 110 | print("Line " .. line .. ": " .. terror[e].msg) | 94 | if not r then |
| 95 | local line, col = re.calcline(s, #s - #sfail) | ||
| 96 | local msg = "Error at line " .. line .. " (col " .. col .. "): " | ||
| 97 | return r, msg .. terror[e].msg | ||
| 98 | end | ||
| 99 | return r | ||
| 111 | end | 100 | end |
| 112 | 101 | ||
| 113 | local s = [[ | 102 | local s = [[ |
| @@ -118,7 +107,7 @@ repeat | |||
| 118 | n := n - 1 | 107 | n := n - 1 |
| 119 | until (n < 1); | 108 | until (n < 1); |
| 120 | write f;]] | 109 | write f;]] |
| 121 | printError(g:match(s)) | 110 | print(mymatch(g, s)) |
| 122 | 111 | ||
| 123 | s = [[ | 112 | s = [[ |
| 124 | n := 5; | 113 | n := 5; |
| @@ -128,14 +117,14 @@ repeat | |||
| 128 | n := n - 1; | 117 | n := n - 1; |
| 129 | until (n < 1); | 118 | until (n < 1); |
| 130 | read ;]] | 119 | read ;]] |
| 131 | printError(g:match(s)) | 120 | print(mymatch(g, s)) |
| 132 | 121 | ||
| 133 | s = [[ | 122 | s = [[ |
| 134 | if a < 1 then | 123 | if a < 1 then |
| 135 | b := 2; | 124 | b := 2; |
| 136 | else | 125 | else |
| 137 | b := 3;]] | 126 | b := 3;]] |
| 138 | printError(g:match(s)) | 127 | print(mymatch(g, s)) |
| 139 | 128 | ||
| 140 | s = [[ | 129 | s = [[ |
| 141 | n := 5; | 130 | n := 5; |
| @@ -145,7 +134,7 @@ repeat | |||
| 145 | n := n - 1; | 134 | n := n - 1; |
| 146 | untill (n < 1); | 135 | untill (n < 1); |
| 147 | ]] | 136 | ]] |
| 148 | printError(g:match(s)) | 137 | print(mymatch(g, s)) |
| 149 | 138 | ||
| 150 | s = [[ | 139 | s = [[ |
| 151 | n := 5; | 140 | n := 5; |
| @@ -155,9 +144,8 @@ repeat | |||
| 155 | n := n - 1; | 144 | n := n - 1; |
| 156 | 3 (n < 1); | 145 | 3 (n < 1); |
| 157 | ]] | 146 | ]] |
| 158 | printError(g:match(s)) | 147 | print(mymatch(g, s)) |
| 159 | |||
| 160 | printError(g:match("a : 2")) | ||
| 161 | printError(g:match("a := (2")) | ||
| 162 | 148 | ||
| 149 | print(mymatch(g, "a : 2")) | ||
| 150 | print(mymatch(g, "a := (2")) | ||
| 163 | 151 | ||
diff --git a/examples/typedlua/test.lua b/examples/typedlua/test.lua index ed4e7a1..95474ba 100755 --- a/examples/typedlua/test.lua +++ b/examples/typedlua/test.lua | |||
| @@ -401,15 +401,18 @@ assert(m == e) | |||
| 401 | -- unfinished comments | 401 | -- unfinished comments |
| 402 | 402 | ||
| 403 | s = [=[ | 403 | s = [=[ |
| 404 | --[[ testing | 404 | --[[ |
| 405 | |||
| 406 | testing | ||
| 405 | unfinished | 407 | unfinished |
| 408 | |||
| 406 | comment | 409 | comment |
| 407 | ]=] | 410 | ]=] |
| 408 | --[=[ | 411 | --[=[ |
| 409 | test.lua:3:1: syntax error, unexpected 'comment', expecting '=', ',', 'String', '{', '(', ':', '[', '.' | 412 | test.lua:3:1: syntax error, unexpected 'comment', expecting '=', ',', 'String', '{', '(', ':', '[', '.' |
| 410 | ]=] | 413 | ]=] |
| 411 | e = [=[ | 414 | e = [=[ |
| 412 | test.lua:1:1: unfinished long comment | 415 | test.lua:1:2: unfinished long comment |
| 413 | ]=] | 416 | ]=] |
| 414 | 417 | ||
| 415 | r, m = parse(s) | 418 | r, m = parse(s) |
diff --git a/examples/typedlua/tllexer.lua b/examples/typedlua/tllexer.lua index 6517ba5..d6033ec 100644 --- a/examples/typedlua/tllexer.lua +++ b/examples/typedlua/tllexer.lua | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | local tllexer = {} | 1 | local tllexer = {} |
| 2 | 2 | ||
| 3 | local lpeg = require "lpeglabel" | 3 | local lpeg = require "lpeglabelrec" |
| 4 | lpeg.locale(lpeg) | 4 | lpeg.locale(lpeg) |
| 5 | 5 | ||
| 6 | local tlerror = require "tlerror" | 6 | local tlerror = require "tlerror" |
| @@ -9,10 +9,6 @@ function tllexer.try (pat, label) | |||
| 9 | return pat + lpeg.T(tlerror.labels[label]) | 9 | return pat + lpeg.T(tlerror.labels[label]) |
| 10 | end | 10 | end |
| 11 | 11 | ||
| 12 | function tllexer.catch (pat, label) | ||
| 13 | return lpeg.Lc(pat, lpeg.P(false), tlerror.labels[label]) | ||
| 14 | end | ||
| 15 | |||
| 16 | local function setffp (s, i, t, n) | 12 | local function setffp (s, i, t, n) |
| 17 | if not t.ffp or i > t.ffp then | 13 | if not t.ffp or i > t.ffp then |
| 18 | t.ffp = i | 14 | t.ffp = i |
| @@ -45,7 +41,10 @@ local CloseEQ = lpeg.Cmt(Close * lpeg.Cb("init"), | |||
| 45 | local LongString = Open * (lpeg.P(1) - CloseEQ)^0 * tllexer.try(Close, "LongString") / | 41 | local LongString = Open * (lpeg.P(1) - CloseEQ)^0 * tllexer.try(Close, "LongString") / |
| 46 | function (s, o) return s end | 42 | function (s, o) return s end |
| 47 | 43 | ||
| 48 | local Comment = lpeg.Lc(lpeg.P("--") * LongString / function () return end, | 44 | local LongStringCm1 = Open * (lpeg.P(1) - CloseEQ)^0 * Close / |
| 45 | function (s, o) return s end | ||
| 46 | |||
| 47 | local Comment = lpeg.Rec(lpeg.P"--" * #Open * (LongStringCm1 / function() return end + lpeg.T(tlerror.labels["LongString"])), | ||
| 49 | lpeg.T(tlerror.labels["LongComment"]), tlerror.labels["LongString"]) + | 48 | lpeg.T(tlerror.labels["LongComment"]), tlerror.labels["LongString"]) + |
| 50 | lpeg.P("--") * (lpeg.P(1) - lpeg.P("\n"))^0 | 49 | lpeg.P("--") * (lpeg.P(1) - lpeg.P("\n"))^0 |
| 51 | 50 | ||
diff --git a/examples/typedlua/tlparser.lua b/examples/typedlua/tlparser.lua index a301fa6..fe4fd5e 100644 --- a/examples/typedlua/tlparser.lua +++ b/examples/typedlua/tlparser.lua | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | local tlparser = {} | 1 | local tlparser = {} |
| 2 | 2 | ||
| 3 | local lpeg = require "lpeglabel" | 3 | local lpeg = require "lpeglabelrec" |
| 4 | lpeg.locale(lpeg) | 4 | lpeg.locale(lpeg) |
| 5 | 5 | ||
| 6 | local tllexer = require "tllexer" | 6 | local tllexer = require "tllexer" |
