diff --git a/parser-gen.lua b/parser-gen.lua index 972e728..7d45b70 100644 --- a/parser-gen.lua +++ b/parser-gen.lua @@ -53,7 +53,6 @@ local function resetOptions() return opts end - -- check values and fill default to the opts local function mergeOptions(options) @@ -81,10 +80,8 @@ local function mergeOptions(options) -- Lua 5.1 compatibility: local unpack = unpack or table.unpack - local Predef = { nl = m.P"\n", cr = m.P"\r", tab = m.P"\t" } - local function updatelocale() m.locale(Predef) local any = m.P(1) @@ -112,20 +109,14 @@ local function mergeOptions(options) updatelocale() - - local function defaultsync(patt) return (m.P(1)^-1) * (-patt * m.P(1))^0 end - - - local function sync (patt) return patt --(-patt * m.P(1))^0 * patt^0 -- skip until we find the pattern and consume it(if we do) end - local function pattspaces (patt) if opts.skipspaces then return patt * opts.specialrules.SKIP ^0 @@ -150,7 +141,6 @@ local function mergeOptions(options) local bg = {} -- local variable to keep global function buildgrammar - local function addspaces (caps) local hastoken = tokenstack:pop() if hastoken == 1 then @@ -274,7 +264,6 @@ local function mergeOptions(options) end end - local function applygrammar(gram, opts) if opts.trace then return m.P(pegdebugtrace(gram, opts.traceoptions)) @@ -307,7 +296,6 @@ local function mergeOptions(options) ret1 = traverse(op1, tokenrule) ret2 = traverse(op2, tokenrule) - return applyaction(act, ret1, ret2, tokenrule) elseif isgrammar(ast) then @@ -405,7 +393,6 @@ local function mergeOptions(options) end end - function bg.buildgrammar (ast) local builder = {} @@ -450,9 +437,6 @@ local function mergeOptions(options) return builder end - - - local function build(ast, opts) if isgrammar(ast) then @@ -508,7 +492,7 @@ local function mergeOptions(options) if action == "or" then return '(' .. op1 ..' / '.. op2 ..')' elseif action == "and" then - return op1 ..' ' .. op2 + return '('..op1..' '..op2..')' --25/04/24 DCN: added brackets elseif action == "&" then return '&'..op1 elseif action == "!" then @@ -526,7 +510,7 @@ local function mergeOptions(options) -- if not lab then -- error("Label '"..op2.."' unspecified using setlabels()") -- end - return op1 .. '^'..op2 + return op1..'^'..op2 elseif action == "->" then if op1 == "''" then -- spacial case for empty capture return op1 ..' -> '.. op2 @@ -727,7 +711,6 @@ local function mergeOptions(options) builder["SYNC"] = opts.specialrules.SYNC_re end - end local function recordAstToPEG(label) diff --git a/parser-gen_tests2.lua b/parser-gen_tests2.lua index 0092f04..5960da6 100644 --- a/parser-gen_tests2.lua +++ b/parser-gen_tests2.lua @@ -25,16 +25,20 @@ local function cleanGram(s) -- remove leading and ending spaces and tabs return (s:gsub("^%s*(.-)%s*$", "%1"):gsub("[\9 ]+", " ")) end - -local function tst_ast(str, gram, options) +local ast_test_number = 0 +local function tst_ast(str, gram, options, test_name) -- rule round trip - options = options or {} - + options = options or {} + ast_test_number = ast_test_number + 1 + test_name = test_name or 'anon#'..ast_test_number + print('\nast_tst ('..test_name..') start...') + local ast = peg.pegToAST(gram, options['definitions']) local gram2 = pg.astToPEG(ast, {recovery=false, skipspaces=false, nocaptures=true, re_useext=true}) - print(serpent.block(cleanGram(gram))) - print(serpent.block(cleanGram(gram2))) + print('Given grammar: '..serpent.block(cleanGram(gram))) + print('New grammar: '..serpent.block(cleanGram(gram2))) + print('Given str: '..serpent.block(str)) if not options['not_exact_grammar'] then -- assert(cleanGram(gram) == cleanGram(gram2)) @@ -42,6 +46,11 @@ local function tst_ast(str, gram, options) local c1,e1 = pg.parse(str, gram, options) local c2,e2 = pg.parse(str, gram2, options) + print('Given grammar captures: '..serpent.block(c1)) + print('New grammar captures: '..serpent.block(c2)) + print('Given grammar errors: '..serpent.block(e1)) + print('New grammar errors: '..serpent.block(e2)) + assert(equals(c1, c2)) -- same captures assert(equals(e1, e2)) -- same error @@ -55,6 +64,7 @@ local function tst_ast(str, gram, options) local m3, me3 = p3:match(str) assert( equals(m1, m3)) end + print('ast_tst ('..test_name..') ...finish\n') return m1, me1 end @@ -93,7 +103,7 @@ SKIP <- DOT DOT <- '.' ]] str = "a...b" ---tst_ast(str, rule) +tst_ast(str, rule) --was disabled -- non terminals -- space allowed @@ -125,7 +135,7 @@ tst_ast(str, rule) rule = [[ R <- 'a' ( 'b' / 'c' )]] str = 'ab' ---res = tst_ast(str, rule, {not_exact_grammar = true}) +res = tst_ast(str, rule, {not_exact_grammar = true}) --was disabled -- testing ranges rule = [[ r <- {[a1b]* } ]] str = "a1b" @@ -302,16 +312,31 @@ A <- 'a' B <- 'b' ]] + -- SELF-DESCRIPTION --local res1, errs = tst_ast(peg.gram, peg.gram, {nocaptures=true, labels=peg.errinfo, definitions = {foldtable = peg.foldtable, concat = peg.concat}, not_exact_grammar = true}) local options ={nocaptures=true, labels=peg.errinfo, definitions = {foldtable = peg.foldtable, concat = peg.concat}} - -local str = peg.gram + local ast = peg.pegToAST(peg.gram, {foldtable = peg.foldtable, concat = peg.concat}) + +print( "=====================") +print( "peg.gram:\n") +print( peg.gram) +print( "=====================") + +print( "=====================") +print("ast:\n") +print(serpent.block(ast)) +print( "=====================") + local gram3 = pg.astToPEG(ast, {recovery=false, skipspaces=false, nocaptures=true, re_useext=true}) -print(gram3) +print( "=====================") +print( "gram3:\n") +print( gram3) +print( "=====================") + local optionstrace = tableMerge({trace=true}, options) local c1,e1 = pg.parse(rule, peg.gram, options) diff --git a/peg-parser-tests.lua b/peg-parser-tests.lua index ecad014..926033a 100644 --- a/peg-parser-tests.lua +++ b/peg-parser-tests.lua @@ -4,6 +4,7 @@ local f = peg.pegToAST local eq = require "equals" local equals = eq.equals +local serpent = pcall(require, "serpent") and require "serpent" or function(...) print(...) end -- self-description of peg-parser: @@ -12,7 +13,6 @@ local equals = eq.equals -- ( p ) grouping e = f("('a')") res = {t="a"} - assert(equals(e,res)) -- 'string' literal string @@ -24,95 +24,93 @@ assert(equals(e,res)) -- "string" literal string e = f('"string"') res = {t="string"} - assert(equals(e,res)) + --[class] character class e = f("[^a-zA-Z01]") res = { - action = "invert", + action = "class", op1 = { - action = "or", + action = "invert", op1 = { - action = "or", + action = "classset", op1 = { - action = "or", + action = "classset", op1 = { - action = "range", + action = "classset", op1 = { - s = "az" + action = "range", + op1 = { + name = "az" + } + }, + op2 = { + action = "range", + op1 = { + name = "AZ" + } } }, op2 = { - action = "range", - op1 = { - s = "AZ" - } + tchar = "0" } }, op2 = { - t = "0" + tchar = "1" } - }, - op2 = { - t = "1" } } -} +} +print('\nast:\n') +print(serpent.block(e)) +print('') assert(equals(e,res)) --. any character e = f(".") res = {action="anychar"} - assert(equals(e,res)) + --%name pattern defs[name] or a pre-defined pattern e = f("%name") -res = {action="%", op1={s="name"}} - +res = {action="%", op1={name="name"}} assert(equals(e,res)) + --name non terminal e = f("name") res = {nt="name"} - assert(equals(e,res)) -- non terminal e = f("") res = {nt="name"} - assert(equals(e,res)) --{} position capture e = f("{}") - res = {action="poscap"} - assert(equals(e,res)) --{ p } simple capture e = f("{name}") res = {action="scap", op1= {nt="name"}} - assert(equals(e,res)) --{: p :} anonymous group capture e = f("{:name:}") res = {action="gcap", op1= {nt="name"}} - assert(equals(e,res)) --{:name: p :} named group capture e = f("{:g: name:}") -res = {action="gcap", op1= {nt="name"} , op2={s="g"}} - +res = {action="gcap", op1= {nt="name"} , op2={name="g"}} assert(equals(e,res)) + --{~ p ~} substitution capture e = f("{~ name ~}") - res = {action="subcap", op1= {nt="name"}} - assert(equals(e,res)) --{| p |} table capture @@ -122,7 +120,7 @@ assert(equals(e,res)) --=name back reference e = f("=name") -res = {action="bref", op1= {s="name"}} +res = {action="bref", op1= {name="name"}} assert(equals(e,res)) --p ? optional match @@ -157,7 +155,7 @@ assert(equals(e,res)) --p^LABEL error label e = f("name^err") -res = {action = "^LABEL", op1= {nt="name"}, op2 = {s="err"}} +res = {action = "^LABEL", op1= {nt="name"}, op2 = {name="err"}} assert(equals(e,res)) --p -> 'string' string capture @@ -171,70 +169,46 @@ res = {action="->", op1= {nt="name"}, op2 = {s="a"}} assert(equals(e,res)) --p -> num numbered capture - e = f('name -> 3') - res = {action="->", op1= {nt="name"}, op2 = {num="3"}} - assert(equals(e,res)) --p -> name function/query/string capture equivalent to p / defs[name] - e = f('name -> func') res = {action="->", op1= {nt="name"}, op2 = {func="func"}} - assert(equals(e,res)) - - --p => name match-time capture equivalent to lpeg.Cmt(p, defs[name]) - e = f('name => func') res = {action="=>", op1= {nt="name"}, op2 = {func="func"}} - assert(equals(e,res)) - ---& p and predicate - +--& p and predicate e = f('&name') res = {action="&", op1= {nt="name"}} - assert(equals(e,res)) - --! p not predicate - - e = f('!name') res = {action="!", op1= {nt="name"}} - assert(equals(e,res)) - --p1 p2 p3 concatenation with left association - e = f('name name2 name3') res = {action="and", op1= {action = "and", op1={nt="name"}, op2={nt="name2"}}, op2={nt="name3"}} - assert(equals(e,res)) --p1 / p2 / p3 ordered choice with left association - e = f('name / name2 / name3') res = {action="or", op1= {action = "or", op1={nt="name"}, op2={nt="name2"}}, op2={nt="name3"}} - assert(equals(e,res)) - --(name <- p)+ grammar - e = f('a <- b b <- c') res = { {rulename = "a", rule = {nt="b"}}, {rulename = "b", rule = {nt="c"}} } - assert(equals(e,res)) -- error labels @@ -242,19 +216,14 @@ assert(equals(e,res)) --peg.setlabels({errName=1}) e = f('%{errName}') - -res = {action="label", op1={s="errName"}} - +res = {action="label", op1={name="errName"}} assert(equals(e,res)) -- a //{errName,errName2} b --peg.setlabels({errName=1, errName2=2}) e = f('a ^errName') - -res = {action="^LABEL", op2={s="errName"}, op1={nt="a"}} - - +res = {action="^LABEL", op2={name="errName"}, op1={nt="a"}} assert(equals(e,res)) -print("all tests succesful") \ No newline at end of file +print("all tests succesful")