Add compiler optimization: Remove proxy rules
This shouldn't have measurable effect on the benchmarks as there are no proxy rules in the grammars the benchamrk uses. However the effect on generated parsers' speed should be positive generally.
This commit is contained in:
parent
33a1a7c1e9
commit
90ed4712e9
|
@ -372,6 +372,82 @@ PEG.Compiler = {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Optimalization passes made on the grammar AST before compilation. Each pass
|
||||||
|
* is a function that is passed the AST and start rule and returns a new AST
|
||||||
|
* and start rule. The AST can be modified in-place by the pass. The passes
|
||||||
|
* are run in sequence in order of their definition.
|
||||||
|
*/
|
||||||
|
_passes: [
|
||||||
|
/*
|
||||||
|
* Removes proxy rules -- that is, rules that only delegate to other rule.
|
||||||
|
*/
|
||||||
|
function(ast, startRule) {
|
||||||
|
function isProxyRule(node) {
|
||||||
|
return node.type === "rule" && node.expression.type === "rule_ref";
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceRuleRefs(ast, from, to) {
|
||||||
|
function nop() {}
|
||||||
|
|
||||||
|
function replaceInExpression(node, from, to) {
|
||||||
|
replace(node.expression, from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceInSubnodes(propertyName) {
|
||||||
|
return function(node, from, to) {
|
||||||
|
PEG.ArrayUtils.each(node[propertyName], function(node) {
|
||||||
|
replace(node, from, to);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var replaceFunctions = {
|
||||||
|
rule: replaceInExpression,
|
||||||
|
choice: replaceInSubnodes("alternatives"),
|
||||||
|
sequence: replaceInSubnodes("elements"),
|
||||||
|
and_predicate: replaceInExpression,
|
||||||
|
not_predicate: replaceInExpression,
|
||||||
|
optional: replaceInExpression,
|
||||||
|
zero_or_more: replaceInExpression,
|
||||||
|
one_or_more: replaceInExpression,
|
||||||
|
action: replaceInExpression,
|
||||||
|
|
||||||
|
rule_ref:
|
||||||
|
function(node, from, to) {
|
||||||
|
if (node.name === from) {
|
||||||
|
node.name = to;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
literal: nop,
|
||||||
|
any: nop,
|
||||||
|
"class": nop
|
||||||
|
};
|
||||||
|
|
||||||
|
function replace(node, from, to) {
|
||||||
|
replaceFunctions[node.type](node, from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var rule in ast) {
|
||||||
|
replace(ast[rule], from, to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var rule in ast) {
|
||||||
|
if (isProxyRule(ast[rule])) {
|
||||||
|
replaceRuleRefs(ast, ast[rule].name, ast[rule].expression.name);
|
||||||
|
if (rule === startRule) {
|
||||||
|
startRule = ast[rule].expression.name;
|
||||||
|
}
|
||||||
|
delete ast[rule];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ast, startRule];
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
_compileFunctions: {
|
_compileFunctions: {
|
||||||
rule: function(node) {
|
rule: function(node) {
|
||||||
var resultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
var resultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
||||||
|
@ -787,6 +863,12 @@ PEG.Compiler = {
|
||||||
this._checks[i](ast, startRule);
|
this._checks[i](ast, startRule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < this._passes.length; i++) {
|
||||||
|
var newAstNadStartRule = this._passes[i](ast, startRule);
|
||||||
|
ast = newAstNadStartRule[0];
|
||||||
|
startRule = newAstNadStartRule[1];
|
||||||
|
}
|
||||||
|
|
||||||
var parseFunctionDefinitions = [];
|
var parseFunctionDefinitions = [];
|
||||||
for (var rule in ast) {
|
for (var rule in ast) {
|
||||||
parseFunctionDefinitions.push(this.compileNode(ast[rule]));
|
parseFunctionDefinitions.push(this.compileNode(ast[rule]));
|
||||||
|
|
2224
lib/metagrammar.js
2224
lib/metagrammar.js
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue