AST refactoring 3/6: Rewrite checks to not extend the AST nodes

redux
David Majda 14 years ago
parent c59516541f
commit 5885a34dde

@ -2,8 +2,6 @@
(function() {
function nop() {}
/* ===== PEG ===== */
/* no var */ PEG = {};
@ -142,31 +140,7 @@ PEG.RegExpUtils = {
/* Namespace with grammar AST nodes. */
PEG.Grammar = {
/*
* Extends specified AST node classes with a function named |name|. The
* definition of the function is different for each node class and it is
* specified in the |functions| object, which contains the functions keyed by
* unqualified AST node names.
*
* Example:
*
* PEG.Grammar.extendNodes("foo", {
* Literal: function() { return 1; },
* Class: function() { return 2; }
* });
*
* This is is equivalent to:
*
* PEG.Grammar.Literal.prototype.foo = function() { return 1; };
* PEG.Grammar.Class.prototype.foo = function() { return 2; };
*/
extendNodes: function(name, functions) {
for (var nodeName in functions) {
PEG.Grammar[nodeName].prototype[name] = functions[nodeName];
}
}
};
PEG.Grammar = {};
/* ===== PEG.Grammar.GrammarError ===== */
@ -248,126 +222,6 @@ PEG.Grammar.Class = function(characters) {
this.characters = characters;
};
/* ===== Referenced Rule Existence Checks ===== */
PEG.Grammar.extendNodes("checkReferencedRulesExist", {
Rule:
function(grammar) {
this.expression.checkReferencedRulesExist(grammar);
},
Choice:
function(grammar) {
PEG.ArrayUtils.each(this.alternatives, function(alternative) {
alternative.checkReferencedRulesExist(grammar);
});
},
Sequence:
function(grammar) {
PEG.ArrayUtils.each(this.elements, function(element) {
element.checkReferencedRulesExist(grammar);
});
},
AndPredicate:
function(grammar) { this.expression.checkReferencedRulesExist(grammar); },
NotPredicate:
function(grammar) { this.expression.checkReferencedRulesExist(grammar); },
Optional:
function(grammar) { this.expression.checkReferencedRulesExist(grammar); },
ZeroOrMore:
function(grammar) { this.expression.checkReferencedRulesExist(grammar); },
OneOrMore:
function(grammar) { this.expression.checkReferencedRulesExist(grammar); },
Action:
function(grammar) { this.expression.checkReferencedRulesExist(grammar); },
RuleRef:
function(grammar) {
if (grammar[this.name] === undefined) {
throw new PEG.Grammar.GrammarError(
"Referenced rule \"" + this.name + "\" does not exist."
);
}
},
Literal: nop,
Any: nop,
Class: nop
});
/* ===== Left Recursion Checks ===== */
PEG.Grammar.extendNodes("checkNoLeftRecursion", {
Rule:
function(grammar, appliedRules) {
this.expression.checkNoLeftRecursion(grammar, appliedRules.concat(this.name));
},
Choice:
function(grammar, appliedRules) {
PEG.ArrayUtils.each(this.alternatives, function(alternative) {
alternative.checkNoLeftRecursion(grammar, appliedRules);
});
},
Sequence:
function(grammar, appliedRules) {
if (this.elements.length > 0) {
this.elements[0].checkNoLeftRecursion(grammar, appliedRules);
}
},
AndPredicate:
function(grammar, appliedRules) {
this.expression.checkNoLeftRecursion(grammar, appliedRules);
},
NotPredicate:
function(grammar, appliedRules) {
this.expression.checkNoLeftRecursion(grammar, appliedRules);
},
Optional:
function(grammar, appliedRules) {
this.expression.checkNoLeftRecursion(grammar, appliedRules);
},
ZeroOrMore:
function(grammar, appliedRules) {
this.expression.checkNoLeftRecursion(grammar, appliedRules);
},
OneOrMore:
function(grammar, appliedRules) {
this.expression.checkNoLeftRecursion(grammar, appliedRules);
},
Action:
function(grammar, appliedRules) {
this.expression.checkNoLeftRecursion(grammar, appliedRules);
},
RuleRef:
function(grammar, appliedRules) {
if (PEG.ArrayUtils.contains(appliedRules, this.name)) {
throw new PEG.Grammar.GrammarError("Left recursion detected for rule \"" + this.name + "\".");
}
grammar[this.name].checkNoLeftRecursion(grammar, appliedRules);
},
Literal: nop,
Any: nop,
Class: nop
});
/* ===== PEG.Compiler ===== */
PEG.Compiler = {
@ -482,8 +336,45 @@ PEG.Compiler = {
_checks: [
/* Checks that all referenced rules exist. */
function(ast, startRule) {
function nop() {}
function checkExpression(node) { check(node.expression); }
function checkSubnodes(propertyName) {
return function(node) {
PEG.ArrayUtils.each(node[propertyName], check);
};
}
var checkFunctions = {
rule: checkExpression,
choice: checkSubnodes("alternatives"),
sequence: checkSubnodes("elements"),
and_predicate: checkExpression,
not_predicate: checkExpression,
optional: checkExpression,
zero_or_more: checkExpression,
one_or_more: checkExpression,
action: checkExpression,
rule_ref:
function(node) {
if (ast[node.name] === undefined) {
throw new PEG.Grammar.GrammarError(
"Referenced rule \"" + node.name + "\" does not exist."
);
}
},
literal: nop,
any: nop,
"class": nop
};
function check(node) { checkFunctions[node.type](node); }
for (var rule in ast) {
ast[rule].checkReferencedRulesExist(ast);
check(ast[rule]);
}
},
@ -498,8 +389,60 @@ PEG.Compiler = {
/* Checks that no left recursion is present. */
function(ast, startRule) {
function nop() {}
function checkExpression(node, appliedRules) {
check(node.expression, appliedRules);
}
var checkFunctions = {
rule:
function(node, appliedRules) {
check(node.expression, appliedRules.concat(node.name));
},
choice:
function(node, appliedRules) {
PEG.ArrayUtils.each(node.alternatives, function(alternative) {
check(alternative, appliedRules);
});
},
sequence:
function(node, appliedRules) {
if (node.elements.length > 0) {
check(node.elements[0], appliedRules);
}
},
and_predicate: checkExpression,
not_predicate: checkExpression,
optional: checkExpression,
zero_or_more: checkExpression,
one_or_more: checkExpression,
action: checkExpression,
rule_ref:
function(node, appliedRules) {
if (PEG.ArrayUtils.contains(appliedRules, node.name)) {
throw new PEG.Grammar.GrammarError(
"Left recursion detected for rule \"" + node.name + "\"."
);
}
check(ast[node.name], appliedRules);
},
literal: nop,
any: nop,
"class": nop
};
function check(node, appliedRules) {
checkFunctions[node.type](node, appliedRules);
}
for (var rule in ast) {
ast[rule].checkNoLeftRecursion(ast, []);
check(ast[rule], []);
}
}
],

Loading…
Cancel
Save