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