Skip to content

Commit

Permalink
AST refactoring 3/6: Rewrite checks to not extend the AST nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
dmajda committed May 21, 2010
1 parent c595165 commit 5885a34
Showing 1 changed file with 92 additions and 149 deletions.
241 changes: 92 additions & 149 deletions lib/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

(function() {

function nop() {}

/* ===== PEG ===== */

/* no var */ PEG = {};
Expand Down Expand Up @@ -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 ===== */

Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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]);
}
},

Expand All @@ -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], []);
}
}
],
Expand Down

0 comments on commit 5885a34

Please sign in to comment.