add postprocessors to grammar.ne for cleaning tree of empty nodes

This commit is contained in:
Sorrel Bri 2020-04-14 22:03:56 -07:00
parent aa19d42a11
commit 432630e600
3 changed files with 39 additions and 38 deletions

View file

@ -5,47 +5,46 @@ function id(x) { return x[0]; }
const { lexer } = require('./lexer.js'); const { lexer } = require('./lexer.js');
const getTerminal = d => d ? d[0] : null; const getTerminal = d => d ? d[0] : null;
const getAll = d => d.map((item, i) => ({[i]: item})); const getAll = d => d.map((item, i) => ({ [i]: item }));
const flag = token => d => d.map(item => ({[token]: item})) const flag = token => d => d.map(item => ({ [token]: item }))
const clearNull = d => d.filter(t => !!t); const clearNull = d => d.filter(t => !!t);
const flagIndex = d => d.map((item, i) => ({[i]: item})) const flagIndex = d => d.map((item, i) => ({[i]: item}))
const remove = _ => null; const remove = _ => null;
const append = d => d.join(''); const append = d => d.join('');
const constructSet = d => d.reduce((acc, t) => { const constructSet = d => d.reduce((acc, t) => {
if (t && t.type === 'setIdentifier') acc.push({set: t}) if (t && t.type === 'setIdentifier') acc.push({set: t});
if (t && t.length) acc[acc.length - 1].phones = t; if (t && t.length) acc[acc.length - 1].phones = t;
return acc; return acc;
}, []); }, []);
const compose = (...funcs) => d => funcs.reduce((acc, func) => func(acc), d) const pipe = (...funcs) => d => funcs.reduce((acc, func) => func(acc), d);
var grammar = { var grammar = {
Lexer: lexer, Lexer: lexer,
ParserRules: [ ParserRules: [
{"name": "main$ebnf$1", "symbols": []}, {"name": "main$ebnf$1", "symbols": []},
{"name": "main$ebnf$1$subexpression$1", "symbols": ["statement"]}, {"name": "main$ebnf$1$subexpression$1", "symbols": ["_", "statement", "_"]},
{"name": "main$ebnf$1", "symbols": ["main$ebnf$1", "main$ebnf$1$subexpression$1"], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}}, {"name": "main$ebnf$1", "symbols": ["main$ebnf$1", "main$ebnf$1$subexpression$1"], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}},
{"name": "main", "symbols": ["main$ebnf$1"], "postprocess": compose(flag('main'), getTerminal)}, {"name": "main", "symbols": ["main$ebnf$1"], "postprocess": pipe(clearNull, flag('main'), getTerminal)},
{"name": "_$ebnf$1$subexpression$1", "symbols": [(lexer.has("whiteSpace") ? {type: "whiteSpace"} : whiteSpace)]}, {"name": "_$ebnf$1$subexpression$1", "symbols": [(lexer.has("whiteSpace") ? {type: "whiteSpace"} : whiteSpace)]},
{"name": "_$ebnf$1", "symbols": ["_$ebnf$1$subexpression$1"], "postprocess": id}, {"name": "_$ebnf$1", "symbols": ["_$ebnf$1$subexpression$1"], "postprocess": id},
{"name": "_$ebnf$1", "symbols": [], "postprocess": function(d) {return null;}}, {"name": "_$ebnf$1", "symbols": [], "postprocess": function(d) {return null;}},
{"name": "_", "symbols": ["_$ebnf$1"], "postprocess": remove}, {"name": "_", "symbols": ["_$ebnf$1"], "postprocess": remove},
{"name": "__", "symbols": [(lexer.has("whiteSpace") ? {type: "whiteSpace"} : whiteSpace)], "postprocess": remove}, {"name": "__", "symbols": [(lexer.has("whiteSpace") ? {type: "whiteSpace"} : whiteSpace)], "postprocess": remove},
{"name": "statement", "symbols": ["comment"]}, {"name": "statement", "symbols": ["comment"]},
{"name": "statement", "symbols": ["definition"], "postprocess": compose(clearNull, getTerminal)}, {"name": "statement", "symbols": ["definition"], "postprocess": getTerminal, clearNull},
{"name": "comment", "symbols": [(lexer.has("comment") ? {type: "comment"} : comment)], "postprocess": compose(remove, getTerminal)}, {"name": "comment", "symbols": [(lexer.has("comment") ? {type: "comment"} : comment)], "postprocess": pipe(getTerminal, remove)},
{"name": "definition", "symbols": [(lexer.has("kwSet") ? {type: "kwSet"} : kwSet), "__", "setDefinition"], "postprocess": d => ({token: 'setDefinition', sets: d[2]})}, {"name": "definition", "symbols": [(lexer.has("kwSet") ? {type: "kwSet"} : kwSet), "__", "setDefinition"], "postprocess": d => ({token: d[0].type, [d[0].value]: d[2]})},
{"name": "setDefinition$ebnf$1", "symbols": []}, {"name": "setDefinition$ebnf$1", "symbols": []},
{"name": "setDefinition$ebnf$1$subexpression$1", "symbols": [(lexer.has("setIdentifier") ? {type: "setIdentifier"} : setIdentifier), "__", (lexer.has("equal") ? {type: "equal"} : equal), "__", "setExpression", (lexer.has("comma") ? {type: "comma"} : comma), "__"]}, {"name": "setDefinition$ebnf$1$subexpression$1", "symbols": [(lexer.has("setIdentifier") ? {type: "setIdentifier"} : setIdentifier), "__", (lexer.has("equal") ? {type: "equal"} : equal), "__", "setExpression", (lexer.has("comma") ? {type: "comma"} : comma), "__"]},
{"name": "setDefinition$ebnf$1", "symbols": ["setDefinition$ebnf$1", "setDefinition$ebnf$1$subexpression$1"], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}}, {"name": "setDefinition$ebnf$1", "symbols": ["setDefinition$ebnf$1", "setDefinition$ebnf$1$subexpression$1"], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}},
{"name": "setDefinition", "symbols": ["setDefinition$ebnf$1", (lexer.has("setIdentifier") ? {type: "setIdentifier"} : setIdentifier), "__", (lexer.has("equal") ? {type: "equal"} : equal), "__", "setExpression"], "postprocess": constructSet}, {"name": "setDefinition", "symbols": ["setDefinition$ebnf$1", (lexer.has("setIdentifier") ? {type: "setIdentifier"} : setIdentifier), "__", (lexer.has("equal") ? {type: "equal"} : equal), "__", "setExpression"]},
{"name": "setExpression", "symbols": [(lexer.has("openSquareBracket") ? {type: "openSquareBracket"} : openSquareBracket), "_", "phoneList", "_", (lexer.has("closeSquareBracket") ? {type: "closeSquareBracket"} : closeSquareBracket)], "postprocess": d => d.filter(t => t && t.length)}, {"name": "setExpression", "symbols": [(lexer.has("openSquareBracket") ? {type: "openSquareBracket"} : openSquareBracket), "_", "phoneList", "_", (lexer.has("closeSquareBracket") ? {type: "closeSquareBracket"} : closeSquareBracket)], "postprocess": d => d.filter(t => t && t.length)},
{"name": "phoneList$ebnf$1", "symbols": []}, {"name": "phoneList$ebnf$1", "symbols": []},
{"name": "phoneList$ebnf$1$subexpression$1", "symbols": [(lexer.has("phone") ? {type: "phone"} : phone), (lexer.has("comma") ? {type: "comma"} : comma), "_"]}, {"name": "phoneList$ebnf$1$subexpression$1", "symbols": [(lexer.has("phone") ? {type: "phone"} : phone), (lexer.has("comma") ? {type: "comma"} : comma), "_"]},
{"name": "phoneList$ebnf$1", "symbols": ["phoneList$ebnf$1", "phoneList$ebnf$1$subexpression$1"], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}}, {"name": "phoneList$ebnf$1", "symbols": ["phoneList$ebnf$1", "phoneList$ebnf$1$subexpression$1"], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}},
{"name": "phoneList", "symbols": ["phoneList$ebnf$1", (lexer.has("phone") ? {type: "phone"} : phone)], "postprocess": d => d.filter(t => t && (t.type === 'phone' || t.length) ) {"name": "phoneList", "symbols": ["phoneList$ebnf$1", (lexer.has("phone") ? {type: "phone"} : phone)], "postprocess": d => d.filter(t => t && (t.type === 'phone' || t[0]) )
.map(t => { .flatMap(t => {
if (!t.length) return t; if (!t.length) return t;
t.filter(st => st && st.type === 'phone') return t[0].filter(st => st && st.type === 'phone')
return t;
}) } }) }
] ]
, ParserStart: "main" , ParserStart: "main"

View file

@ -1,24 +1,24 @@
@{% @{%
const { lexer } = require('./lexer.js'); const { lexer } = require('./lexer.js');
const getTerminal = d => d ? d[0] : null; const getTerminal = d => d ? d[0] : null;
const getAll = d => d.map((item, i) => ({[i]: item})); const getAll = d => d.map((item, i) => ({ [i]: item }));
const flag = token => d => d.map(item => ({[token]: item})) const flag = token => d => d.map(item => ({ [token]: item }))
const clearNull = d => d.filter(t => !!t); const clearNull = d => d.filter(t => !!t);
const flagIndex = d => d.map((item, i) => ({[i]: item})) const flagIndex = d => d.map((item, i) => ({[i]: item}))
const remove = _ => null; const remove = _ => null;
const append = d => d.join(''); const append = d => d.join('');
const constructSet = d => d.reduce((acc, t) => { const constructSet = d => d.reduce((acc, t) => {
if (t && t.type === 'setIdentifier') acc.push({set: t}) if (t && t.type === 'setIdentifier') acc.push({set: t});
if (t && t.length) acc[acc.length - 1].phones = t; if (t && t.length) acc[acc.length - 1].phones = t;
return acc; return acc;
}, []); }, []);
const compose = (...funcs) => d => funcs.reduce((acc, func) => func(acc), d) const pipe = (...funcs) => d => funcs.reduce((acc, func) => func(acc), d);
%} %}
@lexer lexer @lexer lexer
main -> (statement):* main -> (_ statement _):*
{% compose(flag('main'), getTerminal) %} {% pipe(clearNull, flag('main'), getTerminal) %}
_ -> (%whiteSpace):? _ -> (%whiteSpace):?
{% remove %} {% remove %}
@ -27,23 +27,26 @@ __ -> %whiteSpace
{% remove %} {% remove %}
statement -> comment | definition statement -> comment | definition
{% compose(clearNull, getTerminal) %} {% getTerminal, clearNull %}
comment -> %comment comment -> %comment
{% compose(remove, getTerminal) %} {% pipe(getTerminal, remove) %}
# SETS # SETS
definition -> %kwSet __ setDefinition {% d => ({token: 'setDefinition', sets: d[2]}) %} definition -> %kwSet __ setDefinition
{% d => ({token: d[0].type, [d[0].value]: d[2]}) %}
setDefinition -> (%setIdentifier __ %equal __ setExpression %comma __):* %setIdentifier __ %equal __ setExpression setDefinition -> (%setIdentifier __ %equal __ setExpression %comma __):* %setIdentifier __ %equal __ setExpression
{% constructSet %} # {% pipe(
# //constructSet,
# getTerminal) %}
setExpression -> %openSquareBracket _ phoneList _ %closeSquareBracket setExpression -> %openSquareBracket _ phoneList _ %closeSquareBracket
{% d => d.filter(t => t && t.length) %} {% d => d.filter(t => t && t.length) %}
phoneList -> (%phone %comma _):* %phone phoneList -> (%phone %comma _):* %phone
{% d => d.filter(t => t && (t.type === 'phone' || t.length) ) # {% clearNull %}
.map(t => { {% d => d.filter(t => t && (t.type === 'phone' || t[0]) )
.flatMap(t => {
if (!t.length) return t; if (!t.length) return t;
t.filter(st => st && st.type === 'phone') return t[0].filter(st => st && st.type === 'phone')
return t;
}) %} }) %}

View file

@ -7,15 +7,14 @@ describe('parser', () => {
const { latl } = assertionData.simpleComment; const { latl } = assertionData.simpleComment;
const AST = parser().feed(latl).results; const AST = parser().feed(latl).results;
expect(AST.length).toBe(1); expect(AST.length).toBe(1);
console.log(AST[0]) expect(AST[0]).toStrictEqual({ main: [ null ]})
// expect(AST[0]).toStrictEqual()
}) })
// it('parses multiple set definitions with comma operator', () => { it('parses multiple set definitions with comma operator', () => {
// const { latl } = assertionData.commaSetDefinition; const { latl } = assertionData.commaSetDefinition;
// const AST = parser().feed(latl) const AST = parser().feed(latl).results;
// console.log(AST) console.log(AST[0])
// }); });
// it('lexes set definition with alias', () => { // it('lexes set definition with alias', () => {
// const { latl, tokens } = assertionData.setAliasDefinition; // const { latl, tokens } = assertionData.setAliasDefinition;