|
|
|
@ -57,7 +57,31 @@ PEG.buildParser = function(grammar, startRule) {
|
|
|
|
|
|
|
|
|
|
/* Namespace with grammar AST nodes. */
|
|
|
|
|
|
|
|
|
|
PEG.Grammar = {};
|
|
|
|
|
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.GrammarError ===== */
|
|
|
|
|
|
|
|
|
@ -109,88 +133,99 @@ PEG.Grammar.Action = function(expression, action) {
|
|
|
|
|
|
|
|
|
|
/* ===== Referenced Rule Existence Checks ===== */
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Rule.prototype.checkReferencedRulesExist = function(grammar) {
|
|
|
|
|
this._expression.checkReferencedRulesExist(grammar);
|
|
|
|
|
};
|
|
|
|
|
PEG.Grammar.extendNodes("checkReferencedRulesExist", {
|
|
|
|
|
Rule:
|
|
|
|
|
function(grammar) {
|
|
|
|
|
this._expression.checkReferencedRulesExist(grammar);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Literal.prototype.checkReferencedRulesExist = nop;
|
|
|
|
|
Literal: nop,
|
|
|
|
|
Class: nop,
|
|
|
|
|
Any: nop,
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Class.prototype.checkReferencedRulesExist = nop;
|
|
|
|
|
Sequence:
|
|
|
|
|
function(grammar) {
|
|
|
|
|
PEG.ArrayUtils.each(this._elements, function(element) {
|
|
|
|
|
element.checkReferencedRulesExist(grammar);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Any.prototype.checkReferencedRulesExist = nop;
|
|
|
|
|
Choice:
|
|
|
|
|
function(grammar) {
|
|
|
|
|
PEG.ArrayUtils.each(this._alternatives, function(alternative) {
|
|
|
|
|
alternative.checkReferencedRulesExist(grammar);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Sequence.prototype.checkReferencedRulesExist = function(grammar) {
|
|
|
|
|
PEG.ArrayUtils.each(this._elements, function(element) {
|
|
|
|
|
element.checkReferencedRulesExist(grammar);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
ZeroOrMore:
|
|
|
|
|
function(grammar) { this._element.checkReferencedRulesExist(grammar); },
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Choice.prototype.checkReferencedRulesExist = function(grammar) {
|
|
|
|
|
PEG.ArrayUtils.each(this._alternatives, function(alternative) {
|
|
|
|
|
alternative.checkReferencedRulesExist(grammar);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
NotPredicate:
|
|
|
|
|
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.ZeroOrMore.prototype.checkReferencedRulesExist = function(grammar) {
|
|
|
|
|
this._element.checkReferencedRulesExist(grammar);
|
|
|
|
|
};
|
|
|
|
|
RuleRef:
|
|
|
|
|
function(grammar) {
|
|
|
|
|
if (grammar[this._name] === undefined) {
|
|
|
|
|
throw new PEG.Grammar.GrammarError(
|
|
|
|
|
"Referenced rule \"" + this._name + "\" does not exist."
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.NotPredicate.prototype.checkReferencedRulesExist = function(grammar) {
|
|
|
|
|
this._expression.checkReferencedRulesExist(grammar);
|
|
|
|
|
};
|
|
|
|
|
Action:
|
|
|
|
|
function(grammar) { this._expression.checkReferencedRulesExist(grammar); }
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.RuleRef.prototype.checkReferencedRulesExist = function(grammar) {
|
|
|
|
|
if (grammar[this._name] === undefined) {
|
|
|
|
|
throw new PEG.Grammar.GrammarError("Referenced rule \"" + this._name + "\" does not exist.");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Action.prototype.checkReferencedRulesExist = function(grammar) {
|
|
|
|
|
this._expression.checkReferencedRulesExist(grammar);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* ===== Left Recursion Checks ===== */
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Rule.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
|
|
|
|
|
this._expression.checkNoLeftRecursion(grammar, appliedRules.concat(this._name));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Literal.prototype.checkNoLeftRecursion = nop;
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Class.prototype.checkNoLeftRecursion = nop;
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Any.prototype.checkNoLeftRecursion = nop;
|
|
|
|
|
PEG.Grammar.extendNodes("checkNoLeftRecursion", {
|
|
|
|
|
Rule:
|
|
|
|
|
function(grammar, appliedRules) {
|
|
|
|
|
this._expression.checkNoLeftRecursion(grammar, appliedRules.concat(this._name));
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Sequence.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
|
|
|
|
|
if (this._elements.length > 0) {
|
|
|
|
|
this._elements[0].checkNoLeftRecursion(grammar, appliedRules);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Choice.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
|
|
|
|
|
PEG.ArrayUtils.each(this._alternatives, function(alternative) {
|
|
|
|
|
alternative.checkNoLeftRecursion(grammar, appliedRules);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
Literal: nop,
|
|
|
|
|
Class: nop,
|
|
|
|
|
Any: nop,
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.ZeroOrMore.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
|
|
|
|
|
this._element.checkNoLeftRecursion(grammar, appliedRules);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.NotPredicate.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
|
|
|
|
|
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
|
|
|
|
};
|
|
|
|
|
Sequence:
|
|
|
|
|
function(grammar, appliedRules) {
|
|
|
|
|
if (this._elements.length > 0) {
|
|
|
|
|
this._elements[0].checkNoLeftRecursion(grammar, appliedRules);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.RuleRef.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
|
|
|
|
|
if (appliedRules.indexOf(this._name) !== -1) {
|
|
|
|
|
throw new PEG.Grammar.GrammarError("Left recursion detected for rule \"" + this._name + "\".");
|
|
|
|
|
}
|
|
|
|
|
grammar[this._name].checkNoLeftRecursion(grammar, appliedRules);
|
|
|
|
|
};
|
|
|
|
|
Choice:
|
|
|
|
|
function(grammar, appliedRules) {
|
|
|
|
|
PEG.ArrayUtils.each(this._alternatives, function(alternative) {
|
|
|
|
|
alternative.checkNoLeftRecursion(grammar, appliedRules);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
ZeroOrMore:
|
|
|
|
|
function(grammar, appliedRules) {
|
|
|
|
|
this._element.checkNoLeftRecursion(grammar, appliedRules);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
NotPredicate:
|
|
|
|
|
function(grammar, appliedRules) {
|
|
|
|
|
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
RuleRef:
|
|
|
|
|
function(grammar, appliedRules) {
|
|
|
|
|
if (appliedRules.indexOf(this._name) !== -1) {
|
|
|
|
|
throw new PEG.Grammar.GrammarError("Left recursion detected for rule \"" + this._name + "\".");
|
|
|
|
|
}
|
|
|
|
|
grammar[this._name].checkNoLeftRecursion(grammar, appliedRules);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Action.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
|
|
|
|
|
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
|
|
|
|
};
|
|
|
|
|
Action:
|
|
|
|
|
function(grammar, appliedRules) {
|
|
|
|
|
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* ===== PEG.Compiler ===== */
|
|
|
|
|
|
|
|
|
|