From 2f1e173c3d7670f802a087cd2c80afc5b0ed23c3 Mon Sep 17 00:00:00 2001 From: sergio Date: Fri, 9 Dec 2016 10:50:34 -0300 Subject: Updating documentantion and polishing examples --- README.md | 542 +++++++++++++++++++++++++++-------------------- examples/expRec.lua | 50 ++--- examples/expRecAut.lua | 52 ++--- examples/listId2Rec2.lua | 2 +- examples/listIdRe2.lua | 31 ++- 5 files changed, 376 insertions(+), 301 deletions(-) diff --git a/README.md b/README.md index 5e27796..9bc162d 100644 --- a/README.md +++ b/README.md @@ -37,13 +37,13 @@ Below there is a brief summary of the new functions provided by LpegLabel: FunctionDescription lpeglabelrec.T (l) Throws a label l to signal an error -lpeglabelrec.Rec (p1, p2, l1, [l2, ..., ln]) +lpeglabelrec.Rec (p1, p2, l1 [, l2, ..., ln]) Specifies a recovery pattern p2 for p1, when the matching of p1 gives one of the labels l1, ..., ln. %{l} Syntax of relabelrec module. Equivalent to lpeglabelrec.T(l) -p1 //{l1, ..., ln} p2 +p1 //{l1 [, l2, ..., ln} p2 Syntax of relabelrec module. Equivalent to lpeglabelrec.Rec(p1, p2, l1, ..., ln) relabelrec.calcline(subject, i) @@ -173,41 +173,38 @@ By using the `Rec` function we can specify a recovery pattern that should be matched when a label is thrown. After matching the recovery pattern, and possibly recording the error, the parser will resume the regular matching. For example, in the example below -we expect to match rule `A`, but in case label 42 is thrown -then we will try to match `recp`: +we expect to match rule `A`, but when a failure occur the label 42 +is thrown and then we will try to match the recovery pattern `recp`: ```lua local m = require'lpeglabelrec' local recp = m.P"oast" local g = m.P{ - "S", - S = m.Rec(m.V"A", recp, 42) * ".", - A = m.P"t" * (m.P("est") + m.T(42)) + "S", + S = m.Rec(m.V"A", recp, 42) * ".", + A = m.P"t" * (m.P"est" + m.T(42)) } print(g:match("test.")) --> 6 - print(g:match("toast.")) --> 7 - -print(g:match("oast.")) --> nil 0 oast. - -print(g:match("toward.")) --> nil 0 ward. +print(g:match("oast.")) --> nil 0 oast. +print(g:match("toward.")) --> nil 0 ward. ``` -When trying to match 'toast.', in rule `A` the first -'t' is matched, and then label 42 is thrown, with the associated -inpux suffix 'oast.'. In rule `S` this label is caught -and the recovery pattern matches 'oast', so pattern `'.'` -matches the rest of the input. +When trying to match subject 'toast.', in rule `A` the first +'t' is matched, then the matching of `m.P"est"` fails and label 42 +is thrown, with the associated inpux suffix 'oast.'. In rule +`S` label 42 is caught and the recovery pattern matches 'oast', +so pattern `'.'` matches the rest of the input. -When matching 'oast.', pattern `m.P"t"` fails, and -the result of the matching is nil, 0, oast.. +When matching subject 'oast.', pattern `m.P"t"` fails, and +the result of the matching is nil, 0, oast.. -When matching 'toward.', label 42 is throw, with the associated -input suffix 'oward.'. The matching of the recovery pattern fails to, -so the result of the matching is nil, 0, ward.. +When matching 'toward.', label 42 is thrown after matching 't', +with the associated input suffix 'oward.'. As the matching of the +recovery pattern fails, the result is nil, 0, ward.. -Usually, the recovery pattern is an expression that never fails. +Usually, the recovery pattern is an expression that does not fail. In the previous example, we could have used `(m.P(1) - m.P".")^0` as the recovery pattern. @@ -216,11 +213,14 @@ to use a recovery strategy. Grammar `g` remains the same, but we add a recovery grammar `grec` that handles the labels thrown by `g`. In grammar `grec` we use functions `record` and `sync`. -Function `record` gives us a pattern that captures two -values: the current subject position (where a label was thrown) -and the label itself. These values will be used to record -all the errors found. Function `sync` give us synchronization -pattern, that macthes the input +Function `record`, plus function `recorderror`, will help +us to save the input position where a label was thrown, +while function `sync` will give us a synchronization pattern, +that consumes the input while is not possible to match a given +pattern `p`. + +When the matching of an identifier fails, a defaul value ('NONE') +is provided. ```lua local m = require'lpeglabelrec' @@ -266,8 +266,8 @@ end local grec = m.P{ "S", S = m.Rec(m.Rec(g, m.V"ErrComma", errComma), m.V"ErrId", errId), - ErrComma = record(errComma) * sync(-m.P(1) + id), - ErrId = record(errId) * sync(-m.P(1) + ",") + ErrComma = record(errComma) * sync(id), + ErrId = record(errId) * sync(m.P",") } @@ -281,58 +281,41 @@ function mymatch (g, s) local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg table.insert(out, msg) end - return nil, table.concat(out, "\n") + return nil, table.concat(out, "\n") .. "\n" end return r end print(mymatch(grec, "one,two")) +-- Captures (separated by ';'): one; two; +-- Syntactic errors found: 0 + print(mymatch(grec, "one two three")) +-- Captures (separated by ';'): one; two; three; +-- Syntactic errors found: 2 +-- Error at line 1 (col 4): expecting ',' +-- Error at line 1 (col 8): expecting ',' + print(mymatch(grec, "1,\n two, \n3,")) +-- Captures (separated by ';'): NONE; two; NONE; NONE; +-- Syntactic errors found: 3 +-- Error at line 1 (col 1): expecting an identifier +-- Error at line 2 (col 6): expecting an identifier +-- Error at line 3 (col 2): expecting an identifier + print(mymatch(grec, "one\n two123, \nthree,")) +-- Captures (separated by ';'): one; two; three; NONE; +-- Syntactic errors found: 3 +-- Error at line 2 (col 1): expecting ',' +-- Error at line 2 (col 5): expecting ',' +-- Error at line 3 (col 6): expecting an identifier ``` - - ##### *relabelrec* syntax -Now we rewrite the previous example using the syntax -supported by *relabelrec*: - -```lua -local re = require 'relabelrec' - -local g = re.compile[[ - S <- Id List - List <- !. / (',' / %{2}) (Id / %{1}) List - Id <- Sp [a-z]+ - Comma <- Sp ',' - Sp <- %s* -]] - -function mymatch (g, s) - local r, e, sfail = g:match(s) - if not r then - local line, col = re.calcline(s, #s - #sfail) - local msg = "Error at line " .. line .. " (col " .. col .. ")" - if e == 1 then - return r, msg .. ": expecting an identifier before '" .. sfail .. "'" - elseif e == 2 then - return r, msg .. ": expecting ',' before '" .. sfail .. "'" - else - return r, msg - end - end - return r -end - -print(mymatch(g, "one,two")) --> 8 -print(mymatch(g, "one two")) --> nil Error at line 1 (col 3): expecting ',' before ' two' -print(mymatch(g, "one,\n two,\nthree,")) --> nil Error at line 3 (col 6): expecting an identifier before '' -``` - -With the help of function *setlabels* we can also rewrite the previous example to use -mnemonic labels instead of plain numbers: +Below we describe again a grammar that matches a list of identifiers, +now using the syntax supported by *relabelrec*, where `//{}` is the +recovery operator, and `%{}` is the throw operator: ```lua local re = require 'relabelrec' @@ -355,64 +338,124 @@ re.setlabels(labels) local g = re.compile[[ S <- Id List - List <- !. / (',' / %{errComma}) (Id / %{errId}) List - Id <- Sp [a-z]+ - Comma <- Sp ',' + List <- !. / Comma Id List + Id <- Sp {[a-z]+} / %{errId} + Comma <- Sp ',' / %{errComma} Sp <- %s* ]] +local errors + +function recorderror (subject, pos, label) + local line, col = re.calcline(subject, pos) + table.insert(errors, { line = line, col = col, msg = errmsgs[labels[label]] }) + return true +end + +function sync (p) + return '( !(' .. p .. ') .)*' +end + +local grec = re.compile( + "S <- %g //{errComma} ErrComma //{errId} ErrId" .. "\n" .. + "ErrComma <- ('' -> 'errComma' => recorderror) " .. sync('[a-z]+') .. "\n" .. + "ErrId <- ('' -> 'errId' => recorderror) " .. sync('","') .. "-> default" + , {g = g, recorderror = recorderror, default = "NONE"} +) + function mymatch (g, s) - local r, e, sfail = g:match(s) - if not r then - local line, col = re.calcline(s, #s - #sfail) - local msg = "Error at line " .. line .. " (col " .. col .. "): " - return r, msg .. errmsgs[e] .. " before '" .. sfail .. "'" + errors = {} + subject = s + io.write("Input: ", s, "\n") + local r = { g:match(s) } + io.write("Captures (separated by ';'): ") + for k, v in pairs(r) do + io.write(v .. "; ") + end + io.write("\nSyntactic errors found: " .. #errors) + if #errors > 0 then + io.write("\n") + local out = {} + for i, err in ipairs(errors) do + local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg + table.insert(out, msg) + end + io.write(table.concat(out, "\n")) end + print("\n") return r end -print(mymatch(g, "one,two")) --> 8 -print(mymatch(g, "one two")) --> nil Error at line 1 (col 3): expecting ',' before ' two' -print(mymatch(g, "one,\n two,\nthree,")) --> nil Error at line 3 (col 6): expecting an identifier before '' -``` - +print(mymatch(grec, "one,two")) +-- Captures (separated by ';'): one; two; +-- Syntactic errors found: 0 +print(mymatch(grec, "one two three")) +-- Captures (separated by ';'): one; two; three; +-- Syntactic errors found: 2 +-- Error at line 1 (col 4): expecting ',' +-- Error at line 1 (col 8): expecting ',' +print(mymatch(grec, "1,\n two, \n3,")) +-- Captures (separated by ';'): NONE; two; NONE; NONE; +-- Syntactic errors found: 3 +-- Error at line 1 (col 1): expecting an identifier +-- Error at line 2 (col 6): expecting an identifier +-- Error at line 3 (col 2): expecting an identifier +print(mymatch(grec, "one\n two123, \nthree,")) +-- Captures (separated by ';'): one; two; three; NONE; +-- Syntactic errors found: 3 +-- Error at line 2 (col 1): expecting ',' +-- Error at line 2 (col 5): expecting ',' +-- Error at line 3 (col 6): expecting an identifier +``` #### Arithmetic Expressions -Here's an example of an LPegLabel grammar that make its own function called -'expect', which takes a pattern and a label as parameters and throws the label -if the pattern fails to be matched. This function can be extended later on to -record all errors encountered once error recovery is implemented. +Here's an example of an LPegLabel grammar that matches an expression. +We have used a function `expect`, that takes a pattern `patt` and a label as +parameters and builds a new pattern that throws this label when `patt` +fails. -```lua -local lpeg = require"lpeglabel" +When a subexpression is syntactically invalid, a default value of 1000 +is provided by the recovery pattern, so the evaluation of an expression +should always produce a numeric value. -local R, S, P, V, C, Ct, T = lpeg.R, lpeg.S, lpeg.P, lpeg.V, lpeg.C, lpeg.Ct, lpeg.T +In this example, we can see that it may be a tedious and error prone +task to build manually the recovery grammar `grec`. In the next example +we will show how to build the recovery grammar in a more automatic way. + +```lua +local m = require"lpeglabelrec" +local re = require"relabelrec" local labels = { - {"NoExp", "no expression found"}, - {"Extra", "extra characters found after the expression"}, - {"ExpTerm", "expected a term after the operator"}, - {"ExpExp", "expected an expression after the parenthesis"}, + {"ExpTermFirst", "expected an expression"}, + {"ExpTermOp", "expected a term after the operator"}, {"MisClose", "missing a closing ')' after the expression"}, } -local function expect(patt, labname) +local function labelindex(labname) for i, elem in ipairs(labels) do if elem[1] == labname then - return patt + T(i) + return i end end - error("could not find label: " .. labname) end -local num = R("09")^1 / tonumber -local op = S("+-*/") +local errors, subject + +local function expect(patt, labname) + local i = labelindex(labname) + return patt + m.T(i) +end + + +local num = m.R("09")^1 / tonumber +local op = m.S("+-") local function compute(tokens) local result = tokens[1] @@ -421,10 +464,6 @@ local function compute(tokens) result = result + tokens[i+1] elseif tokens[i] == '-' then result = result - tokens[i+1] - elseif tokens[i] == '*' then - result = result * tokens[i+1] - elseif tokens[i] == '/' then - result = result / tokens[i+1] else error('unknown operation: ' .. tokens[i]) end @@ -432,128 +471,128 @@ local function compute(tokens) return result end -local g = P { +local g = m.P { "Exp", - Exp = Ct(V"Term" * (C(op) * expect(V"Term", "ExpTerm"))^0) / compute; - Term = num + V"Group"; - Group = "(" * expect(V"Exp", "ExpExp") * expect(")", "MisClose"); + Exp = m.Ct(m.V"OperandFirst" * (m.C(op) * m.V"Operand")^0) / compute, + OperandFirst = expect(m.V"Term", "ExpTermFirst"), + Operand = expect(m.V"Term", "ExpTermOp"), + Term = num + m.V"Group", + Group = "(" * m.V"Exp" * expect(")", "MisClose"), } -g = expect(g, "NoExp") * expect(-P(1), "Extra") - -local function eval(input) - local result, label, suffix = g:match(input) - if result ~= nil then - return result - else - local pos = input:len() - suffix:len() + 1 - local msg = labels[label][2] - return nil, "syntax error: " .. msg .. " (at index " .. pos .. ")" - end +function recorderror(pos, lab) + local line, col = re.calcline(subject, pos) + table.insert(errors, { line = line, col = col, msg = labels[lab][2] }) end -print(eval "98-76*(54/32)") ---> 37.125 - -print(eval "(1+1-1*2/2") ---> syntax error: missing a closing ')' after the expression (at index 11) - -print(eval "(1+)-1*(2/2)") ---> syntax error: expected a term after the operator (at index 4) - -print(eval "(1+1)-1*(/2)") ---> syntax error: expected an expression after the parenthesis (at index 10) - -print(eval "1+(1-(1*2))/2x") ---> syntax error: extra chracters found after the expression (at index 14) - -print(eval "-1+(1-(1*2))/2") ---> syntax error: no expression found (at index 1) -``` - -#### Catching labels - -When a label is thrown, the grammar itself can handle this label -by using the labeled ordered choice. Below we rewrite the example -of the list of identifiers to show this feature: - - -```lua -local m = require'lpeglabel' - -local terror = {} +function record (labname) + return (m.Cp() * m.Cc(labelindex(labname))) / recorderror +end -local function newError(s) - table.insert(terror, s) - return #terror +function sync (p) + return (-p * m.P(1))^0 end -local errUndef = newError("undefined") -local errId = newError("expecting an identifier") -local errComma = newError("expecting ','") +function defaultValue (p) + return p or m.Cc(1000) +end -local g = m.P{ +local grec = m.P { "S", - S = m.Lc(m.Lc(m.V"Id" * m.V"List", m.V"ErrId", errId), - m.V"ErrComma", errComma), - List = -m.P(1) + (m.V"Comma" + m.T(errComma)) * (m.V"Id" + m.T(errId)) * m.V"List", - Id = m.V"Sp" * m.R'az'^1, - Comma = m.V"Sp" * ",", - Sp = m.S" \n\t"^0, - ErrId = m.Cc(errId) / terror, - ErrComma = m.Cc(errComma) / terror + S = m.Rec(m.V"A", m.V"ErrExpTermFirst", labelindex("ExpTermFirst")), + A = m.Rec(m.V"Sg", m.V"ErrExpTermOp", labelindex("ExpTermOp")), + Sg = m.Rec(g, m.V"ErrMisClose", labelindex("MisClose")), + ErrExpTermFirst = record("ExpTermFirst") * sync(op + ")") * defaultValue(), + ErrExpTermOp = record("ExpTermOp") * sync(op + ")") * defaultValue(), + ErrMisClose = record("MisClose") * sync(m.P")") * defaultValue(m.P""), } + +local function eval(input) + errors = {} + io.write("Input: ", input, "\n") + subject = input + local result, label, suffix = grec:match(input) + io.write("Syntactic errors found: " .. #errors, "\n") + if #errors > 0 then + local out = {} + for i, err in ipairs(errors) do + local pos = err.col + local msg = err.msg + table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") + end + print(table.concat(out, "\n")) + end + io.write("Result = ") + return result +end -print(m.match(g, "one,two")) --> 8 -print(m.match(g, "one two")) --> expecting ',' -print(m.match(g, "one,\n two,\nthree,")) --> expecting an identifier +print(eval "90-70-(5)+3") +-- Syntactic errors found: 0 +-- Result = 18 + +print(eval "15+") +-- Syntactic errors found: 1 +-- syntax error: expected a term after the operator (at index 3) +-- Result = 1015 + +print(eval "-2") +-- Syntactic errors found: 1 +-- syntax error: expected an expression (at index 1) +-- Result = 998 + +print(eval "1+()+") +-- Syntactic errors found: 2 +-- syntax error: expected an expression (at index 4) +-- syntax error: expected a term after the operator (at index 5) +-- Result = 2001 + +print(eval "1+(") +-- Syntactic errors found: 2 +-- syntax error: expected an expression (at index 3) +-- syntax error: missing a closing ')' after the expression (at index 3) +-- Result = 1001 + +print(eval "3)") +-- Syntactic errors found: 0 +-- Result = 3 ``` -#### Error Recovery +#### Automatically Building the Recovery Grammar -By using labeled ordered choice or the recovery operator, when a label -is thrown, the parser may record the error and still continue parsing -to find more errors. We can even record the error right away without -actually throwing a label (relying on the regular PEG failure instead). -Below we rewrite the arithmetic expression example and modify -the `expect` function to use the recovery operator for error recovery: +Below we rewrite the previous example to automatically +build the recovery grammar based on information provided +by the user for each label (error message, recovery pattern, etc). +In the example below we also throw an error when the grammar +does not match the whole subject. ```lua -local lpeg = require"lpeglabel" +local m = require"lpeglabelrec" +local re = require"relabelrec" -local R, S, P, V = lpeg.R, lpeg.S, lpeg.P, lpeg.V -local C, Cc, Ct, Cmt, Carg = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cmt, lpeg.Carg -local T, Lc, Rec = lpeg.T, lpeg.Lc, lpeg.Rec +local num = m.R("09")^1 / tonumber +local op = m.S("+-") -local labels = { - {"NoExp", "no expression found"}, - {"Extra", "extra characters found after the expression"}, - {"ExpTerm", "expected a term after the operator"}, - {"ExpExp", "expected an expression after the parenthesis"}, - {"MisClose", "missing a closing ')' after the expression"}, -} +local labels = {} +local nlabels = 0 -local function labelindex(labname) - for i, elem in ipairs(labels) do - if elem[1] == labname then - return i - end - end - error("could not find label: " .. labname) +local function newError(lab, msg, psync, pcap) + nlabels = nlabels + 1 + psync = psync or m.P(-1) + pcap = pcap or m.P"" + labels[lab] = { id = nlabels, msg = msg, psync = psync, pcap = pcap } end -local function expect(patt, labname, recpatt) - local i = labelindex(labname) - local function recorderror(input, pos, errors) - table.insert(errors, {i, pos}) - return true - end - if not recpatt then recpatt = P"" end - return Rec(patt, Cmt(Carg(1), recorderror) * recpatt) -end +newError("ExpTermFirst", "expected an expression", op + ")", m.Cc(1000)) +newError("ExpTermOp", "expected a term after the operator", op + ")", m.Cc(1000)) +newError("MisClose", "missing a closing ')' after the expression", m.P")") +newError("Extra", "extra characters found after the expression") -local num = R("09")^1 / tonumber -local op = S("+-*/") +local errors, subject + +local function expect(patt, labname) + local i = labels[labname].id + return patt + m.T(i) +end local function compute(tokens) local result = tokens[1] @@ -562,10 +601,6 @@ local function compute(tokens) result = result + tokens[i+1] elseif tokens[i] == '-' then result = result - tokens[i+1] - elseif tokens[i] == '*' then - result = result * tokens[i+1] - elseif tokens[i] == '/' then - result = result / tokens[i+1] else error('unknown operation: ' .. tokens[i]) end @@ -573,43 +608,84 @@ local function compute(tokens) return result end - -local g = P { +local g = m.P { "Exp", - Exp = Ct(V"Term" * (C(op) * V"Operand")^0) / compute; - Operand = expect(V"Term", "ExpTerm", Cc(0)); - Term = num + V"Group"; - Group = "(" * V"InnerExp" * expect(")", "MisClose"); - InnerExp = expect(V"Exp", "ExpExp", (P(1) - ")")^0 * Cc(0)); + Exp = m.Ct(m.V"OperandFirst" * (m.C(op) * m.V"Operand")^0) / compute, + OperandFirst = expect(m.V"Term", "ExpTermFirst"), + Operand = expect(m.V"Term", "ExpTermOp"), + Term = num + m.V"Group", + Group = "(" * m.V"Exp" * expect(")", "MisClose"), } -g = expect(g, "NoExp", P(1)^0) * expect(-P(1), "Extra") +function recorderror(pos, lab) + local line, col = re.calcline(subject, pos) + table.insert(errors, { line = line, col = col, msg = labels[lab].msg }) +end + +function record (labname) + return (m.Cp() * m.Cc(labname)) / recorderror +end + +function sync (p) + return (-p * m.P(1))^0 +end + +function defaultValue (p) + return p or m.Cc(1000) +end + +local grec = g * expect(m.P(-1), "Extra") +for k, v in pairs(labels) do + grec = m.Rec(grec, record(k) * sync(v.psync) * v.pcap, v.id) +end local function eval(input) - local errors = {} - local result, label, suffix = g:match(input, 1, errors) - if #errors == 0 then - return result - else + errors = {} + io.write("Input: ", input, "\n") + subject = input + local result, label, suffix = grec:match(input) + io.write("Syntactic errors found: " .. #errors, "\n") + if #errors > 0 then local out = {} for i, err in ipairs(errors) do - local pos = err[2] - local msg = labels[err[1]][2] + local pos = err.col + local msg = err.msg table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") end - return nil, table.concat(out, "\n") + print(table.concat(out, "\n")) end + io.write("Result = ") + return result end -print(eval "98-76*(54/32)") ---> 37.125 - -print(eval "-1+(1-(1*2))/2") ---> syntax error: no expression found (at index 1) - -print(eval "(1+1-1*(2/2+)-():") ---> syntax error: expected a term after the operator (at index 13) ---> syntax error: expected an expression after the parenthesis (at index 16) ---> syntax error: missing a closing ')' after the expression (at index 17) ---> syntax error: extra characters found after the expression (at index 17) +print(eval "90-70-(5)+3") +-- Syntactic errors found: 0 +-- Result = 18 + +print(eval "15+") +-- Syntactic errors found: 1 +-- syntax error: expected a term after the operator (at index 3) +-- Result = 1015 + +print(eval "-2") +-- Syntactic errors found: 1 +-- syntax error: expected an expression (at index 1) +-- Result = 998 + +print(eval "1+()+") +-- Syntactic errors found: 2 +-- syntax error: expected an expression (at index 4) +-- syntax error: expected a term after the operator (at index 5) +-- Result = 2001 + +print(eval "1+(") +-- Syntactic errors found: 2 +-- syntax error: expected an expression (at index 3) +-- syntax error: missing a closing ')' after the expression (at index 3) +-- Result = 1001 + +print(eval "3)") +-- Syntactic errors found: 1 +-- syntax error: extra characters found after the expression (at index 2) +-- Result = 3 ``` diff --git a/examples/expRec.lua b/examples/expRec.lua index c5cbcca..5c5fd7d 100644 --- a/examples/expRec.lua +++ b/examples/expRec.lua @@ -1,10 +1,6 @@ local m = require"lpeglabelrec" local re = require"relabelrec" -local R, S, P, V = m.R, m.S, m.P, m.V -local C, Cc, Ct, Cmt = m.C, m.Cc, m.Ct, m.Cmt -local T, Rec = m.T, m.Rec - local labels = { {"ExpTermFirst", "expected an expression"}, {"ExpTermOp", "expected a term after the operator"}, @@ -22,14 +18,14 @@ end local errors, subject -local function expect(patt, labname, recpatt) +local function expect(patt, labname) local i = labelindex(labname) - return patt + T(i) + return patt + m.T(i) end -local num = R("09")^1 / tonumber -local op = S("+-") +local num = m.R("09")^1 / tonumber +local op = m.S("+-") local function compute(tokens) local result = tokens[1] @@ -45,13 +41,13 @@ local function compute(tokens) return result end -local g = P { +local g = m.P { "Exp", - Exp = Ct(V"OperandFirst" * (C(op) * V"Operand")^0) / compute, - OperandFirst = expect(V"Term", "ExpTermFirst"), - Operand = expect(V"Term", "ExpTermOp"), - Term = num + V"Group", - Group = "(" * V"Exp" * expect(")", "MisClose"), + Exp = m.Ct(m.V"OperandFirst" * (m.C(op) * m.V"Operand")^0) / compute, + OperandFirst = expect(m.V"Term", "ExpTermFirst"), + Operand = expect(m.V"Term", "ExpTermOp"), + Term = num + m.V"Group", + Group = "(" * m.V"Exp" * expect(")", "MisClose"), } function recorderror(pos, lab) @@ -71,22 +67,23 @@ function defaultValue (p) return p or m.Cc(1000) end -local recg = P { +local grec = m.P { "S", - S = Rec(V"A", V"ErrExpTermFirst", labelindex("ExpTermFirst")), -- default value is 0 - A = Rec(V"Sg", V"ErrExpTermOp", labelindex("ExpTermOp")), - Sg = Rec(g, V"ErrMisClose", labelindex("MisClose")), + S = m.Rec(m.V"A", m.V"ErrExpTermFirst", labelindex("ExpTermFirst")), -- default value is 0 + A = m.Rec(m.V"Sg", m.V"ErrExpTermOp", labelindex("ExpTermOp")), + Sg = m.Rec(g, m.V"ErrMisClose", labelindex("MisClose")), ErrExpTermFirst = record("ExpTermFirst") * sync(op + ")") * defaultValue(), ErrExpTermOp = record("ExpTermOp") * sync(op + ")") * defaultValue(), - ErrMisClose = record("MisClose") * sync(P")") * defaultValue(m.P""), + ErrMisClose = record("MisClose") * sync(m.P")") * defaultValue(m.P""), } - - + local function eval(input) errors = {} + io.write("Input: ", input, "\n") subject = input - local result, label, suffix = recg:match(input) - if #errors > 0 then + local result, label, suffix = grec:match(input) + io.write("Syntactic errors found: " .. #errors, "\n") + if #errors > 0 then local out = {} for i, err in ipairs(errors) do local pos = err.col @@ -95,13 +92,14 @@ local function eval(input) end print(table.concat(out, "\n")) end + io.write("Result = ") return result end -print(eval "90-70*5") +print(eval "90-70-(5)+3") --> 20 -print(eval "2+") +print(eval "15+") --> 2 + 0 print(eval "-2") @@ -126,3 +124,5 @@ print(eval "1+(") print(eval "3)") +print(eval "11+())3") + diff --git a/examples/expRecAut.lua b/examples/expRecAut.lua index e098078..f870d73 100644 --- a/examples/expRecAut.lua +++ b/examples/expRecAut.lua @@ -1,12 +1,8 @@ local m = require"lpeglabelrec" local re = require"relabelrec" -local R, S, P, V = m.R, m.S, m.P, m.V -local C, Cc, Ct, Cmt = m.C, m.Cc, m.Ct, m.Cmt -local T, Rec = m.T, m.Rec - -local num = R("09")^1 / tonumber -local op = S("+-") +local num = m.R("09")^1 / tonumber +local op = m.S("+-") local labels = {} local nlabels = 0 @@ -27,7 +23,7 @@ local errors, subject local function expect(patt, labname) local i = labels[labname].id - return patt + T(i) + return patt + m.T(i) end local function compute(tokens) @@ -44,13 +40,13 @@ local function compute(tokens) return result end -local g = P { +local g = m.P { "Exp", - Exp = Ct(V"OperandFirst" * (C(op) * V"Operand")^0) / compute, - OperandFirst = expect(V"Term", "ExpTermFirst"), - Operand = expect(V"Term", "ExpTermOp"), - Term = num + V"Group", - Group = "(" * V"Exp" * expect(")", "MisClose"), + Exp = m.Ct(m.V"OperandFirst" * (m.C(op) * m.V"Operand")^0) / compute, + OperandFirst = expect(m.V"Term", "ExpTermFirst"), + Operand = expect(m.V"Term", "ExpTermOp"), + Term = num + m.V"Group", + Group = "(" * m.V"Exp" * expect(")", "MisClose"), } function recorderror(pos, lab) @@ -70,27 +66,18 @@ function defaultValue (p) return p or m.Cc(1000) end -local recg2 = g +local grec = g * expect(m.P(-1), "Extra") for k, v in pairs(labels) do - recg2 = Rec(recg2, record(k) * sync(v.psync) * v.pcap, v.id) + grec = m.Rec(grec, record(k) * sync(v.psync) * v.pcap, v.id) end -local recg = P { - "S", - S = Rec(V"A", V"ErrExpTermFirst", labels["ExpTermFirst"].id), -- default value is 0 - A = Rec(V"Sg", V"ErrExpTermOp", labels["ExpTermOp"].id), - Sg = Rec(g, V"ErrMisClose", labels["MisClose"].id), - ErrExpTermFirst = record("ExpTermFirst") * sync(op + ")") * defaultValue(), - ErrExpTermOp = record("ExpTermOp") * sync(op + ")") * defaultValue(), - ErrMisClose = record("MisClose") * sync(P")") * defaultValue(m.P""), -} - - local function eval(input) errors = {} + io.write("Input: ", input, "\n") subject = input - local result, label, suffix = recg2:match(input) - if #errors > 0 then + local result, label, suffix = grec:match(input) + io.write("Syntactic errors found: " .. #errors, "\n") + if #errors > 0 then local out = {} for i, err in ipairs(errors) do local pos = err.col @@ -99,13 +86,14 @@ local function eval(input) end print(table.concat(out, "\n")) end + io.write("Result = ") return result end -print(eval "90-70*5") ---> 20 +print(eval "90-70-(5)+3") +--> 18 -print(eval "2+") +print(eval "15+") --> 2 + 0 print(eval "-2") @@ -130,3 +118,5 @@ print(eval "1+(") print(eval "3)") +print(eval "11+()3") +--> 1 + ([0]) [+] 3 + [0] diff --git a/examples/listId2Rec2.lua b/examples/listId2Rec2.lua index ab8f1dd..c6705dd 100644 --- a/examples/listId2Rec2.lua +++ b/examples/listId2Rec2.lua @@ -56,7 +56,7 @@ function mymatch (g, s) local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg table.insert(out, msg) end - return nil, table.concat(out, "\n") + return nil, table.concat(out, "\n") .. "\n" end return r end diff --git a/examples/listIdRe2.lua b/examples/listIdRe2.lua index 070bcdb..6bab6ba 100644 --- a/examples/listIdRe2.lua +++ b/examples/listIdRe2.lua @@ -19,7 +19,7 @@ re.setlabels(labels) local g = re.compile[[ S <- Id List List <- !. / Comma Id List - Id <- Sp [a-z]+ / %{errId} + Id <- Sp {[a-z]+} / %{errId} Comma <- Sp ',' / %{errComma} Sp <- %s* ]] @@ -38,25 +38,34 @@ end local grec = re.compile( "S <- %g //{errComma} ErrComma //{errId} ErrId" .. "\n" .. - "ErrComma <- ('' -> 'errComma' => recorderror) " .. sync('!. / [a-z]+') .. "\n" .. - "ErrId <- ('' -> 'errId' => recorderror) (!(!. / ',') .)*" - , {g = g, recorderror = recorderror}) + "ErrComma <- ('' -> 'errComma' => recorderror) " .. sync('[a-z]+') .. "\n" .. + "ErrId <- ('' -> 'errId' => recorderror) " .. sync('","') .. "-> default" + , {g = g, recorderror = recorderror, default = "NONE"}) function mymatch (g, s) errors = {} - local r, e, sfail = g:match(s) + subject = s + io.write("Input: ", s, "\n") + local r = { g:match(s) } + io.write("Captures (separated by ';'): ") + for k, v in pairs(r) do + io.write(v .. "; ") + end + io.write("\nSyntactic errors found: " .. #errors) if #errors > 0 then + io.write("\n") local out = {} for i, err in ipairs(errors) do local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg table.insert(out, msg) end - return nil, table.concat(out, "\n") + io.write(table.concat(out, "\n")) end - return r + print("\n") + return r end -print(mymatch(grec, "one,two")) -print(mymatch(grec, "one two three")) -print(mymatch(grec, "1,\n two, \n3,")) -print(mymatch(grec, "one\n two123, \nthree,")) +mymatch(grec, "one,two") +mymatch(grec, "one two three") +mymatch(grec, "1,\n two, \n3,") +mymatch(grec, "one\n two123, \nthree,") -- cgit v1.2.3-55-g6feb