You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1342 lines
37 KiB
JavaScript

/*
* PEG.js compiler.
*
* The runtime.js file must be included before this file.
*/
(function() {
/* ===== PEG ===== */
/*
* Generates a parser from a specified grammar and start rule and returns it.
*
* The grammar may be either an object or a string. If it is an object, it
* must contain AST of the parsing expressions (i.e. instances of |PEG.Grammar.*
* classes| for the grammar rules in its properties. If it is a string, it is
* parsed using |PEG.grammarParser| to obtain the grammar AST and thus it must
* be in a format that this parser accepts (see the source code for details).
*
* The start rule may be unspecified, in which case "start" is used.
*
* Throws |PEG.Grammar.GrammarError| if the grammar definition is not object nor
* string or if it contains an error. Note that not all errors are detected
* during the generation and some may protrude to the generated parser and cause
* its malfunction.
*/
PEG.buildParser = function(grammar, startRule) {
startRule = startRule || "start";
switch (typeof(grammar)) {
case "object":
var ast = grammar;
break;
case "string":
var ast = PEG.grammarParser.parse(grammar);
break;
default:
throw new PEG.Grammar.GrammarError("Grammar must be object or string.");
}
if (ast[startRule] === undefined) {
throw new PEG.Grammar.GrammarError("Missing \"" + startRule + "\" rule.");
}
return PEG.Compiler.compileParser(ast, startRule);
};
/* ===== PEG.Grammar ===== */
/* Namespace with grammar AST nodes. */
PEG.Grammar = {};
/* ===== PEG.GrammarError ===== */
/* Thrown when the grammar contains an error. */
PEG.Grammar.GrammarError = function(message) {
this.name = "PEG.Grammar.GrammarError";
this.message = message;
};
PEG.Grammar.GrammarError.prototype = Error.prototype;
/* ===== PEG.Grammar.* ===== */
PEG.Grammar.Rule = function(name, humanName, expression) {
this._name = name;
this._humanName = humanName;
this._expression = expression;
};
PEG.Grammar.Rule.prototype = {
getName: function() { return this._name; }
};
PEG.Grammar.Literal = function(value) { this._value = value; };
PEG.Grammar.Any = function() {};
PEG.Grammar.Sequence = function(elements) { this._elements = elements; };
PEG.Grammar.Choice = function(alternatives) {
this._alternatives = alternatives;
};
PEG.Grammar.ZeroOrMore = function(element) { this._element = element; };
PEG.Grammar.NotPredicate = function(expression) {
this._expression = expression;
};
PEG.Grammar.RuleRef = function(name) { this._name = name; };
PEG.Grammar.Action = function(expression, action) {
this._expression = expression;
this._action = action;
};
/* ===== PEG.Compiler ===== */
PEG.Compiler = {
/*
* Takes parts of code, interpolates variables inside them and joins them with
* a newline.
*
* Variables are delimited with "${" and "}" and their names must be valid
* identifiers (i.e. they must match [a-zA-Z_][a-zA-Z0-9_]*). Variable values
* are specified as properties of the last parameter (if this is an object,
* otherwise empty variable set is assumed). Undefined variables result in
* throwing |Error|.
*
* There can be a filter specified after the variable name, prefixed with "|".
* The filter name must be a valid identifier. The only recognized filter
* right now is "string", which quotes the variable value as a JavaScript
* string. Unrecognized filters result in throwing |Error|.
*
* If any part has multiple lines and the first line is indented by some
* amount of whitespace (as defined by the /\s+/ JavaScript regular
* expression), second to last lines are indented by the same amount of
* whitespace. This results in nicely indented multiline code in variables
* without making the templates look ugly.
*
* Examples:
*
* PEG.Compiler.formatCode("foo", "bar"); // "foo\nbar"
* PEG.Compiler.formatCode(
* "foo", "${bar}",
* { bar: "baz" }
* ); // "foo\nbaz"
* PEG.Compiler.formatCode("foo", "${bar}"); // throws Error
* PEG.Compiler.formatCode(
* "foo", "${bar|string}",
* { bar: "baz" }
* ); // "foo\n\"baz\""
* PEG.Compiler.formatCode(
* "foo", "${bar|eeek}",
* { bar: "baz" }
* ); // throws Error
* PEG.Compiler.formatCode(
* "foo", "${bar}",
* { bar: " baz\nqux" }
* ); // "foo\n baz\n qux"
*/
formatCode: function() {
function interpolateVariablesInParts(parts) {
return PEG.ArrayUtils.map(parts, function(part) {
return part.replace(
/\$\{([a-zA-Z_][a-zA-Z0-9_]*)(\|([a-zA-Z_][a-zA-Z0-9_]*))?\}/g,
function(match, name, dummy, filter) {
var value = vars[name];
if (value === undefined) {
throw new Error("Undefined variable: \"" + name + "\".");
}
if (filter !== undefined && filter != "") { // JavaScript engines differ here.
if (filter === "string") {
return PEG.StringUtils.quote(value);
} else {
throw new Error("Unrecognized filter: \"" + filter + "\".");
}
} else {
return value;
}
}
);
});
}
function indentMultilineParts(parts) {
return PEG.ArrayUtils.map(parts, function(part) {
if (!/\n/.test(part)) { return part; }
var firstLineWhitespacePrefix = part.match(/^\s*/)[0];
var lines = part.split("\n");
var linesIndented = [lines[0]].concat(
PEG.ArrayUtils.map(lines.slice(1), function(line) {
return firstLineWhitespacePrefix + line;
})
);
return linesIndented.join("\n");
});
}
var args = Array.prototype.slice.call(arguments);
var vars = args[args.length - 1] instanceof Object ? args.pop() : {};
return indentMultilineParts(interpolateVariablesInParts(args)).join("\n");
},
_uniqueIdentifierCounters: {},
_resetUniqueIdentifierCounters: function() {
this._uniqueIdentifierCounters = {};
},
/* Generates a unique identifier with specified prefix. */
generateUniqueIdentifier: function(prefix) {
this._uniqueIdentifierCounters[prefix]
= this._uniqueIdentifierCounters[prefix] || 0;
return prefix + this._uniqueIdentifierCounters[prefix]++;
},
/*
* Generates a parser from a specified grammar and start rule.
*/
compileParser: function(grammar, startRule) {
/*
* This ensures that the same grammar and start rule always generate exactly
* the same parser.
*/
this._resetUniqueIdentifierCounters();
var parseFunctionDefinitions = [];
for (var key in grammar) {
parseFunctionDefinitions.push(grammar[key].compile());
}
var source = this.formatCode(
"(function(){",
" var result = new PEG.Parser(${startRule|string});",
" ",
" ${parseFunctionDefinitions}",
" ",
" return result;",
"})()",
{
parseFunctionDefinitions: parseFunctionDefinitions.join("\n\n"),
startRule: startRule
}
);
var result = eval(source);
result._source = source;
return result;
}
};
PEG.Grammar.Rule.prototype.compile = function() {
var resultVar = PEG.Compiler.generateUniqueIdentifier("result");
if (this._humanName !== null) {
var setReportMatchFailuresCode = PEG.Compiler.formatCode(
"var savedReportMatchFailures = context.reportMatchFailures;",
"context.reportMatchFailures = false;"
);
var restoreReportMatchFailuresCode = PEG.Compiler.formatCode(
"context.reportMatchFailures = savedReportMatchFailures;"
);
var reportMatchFailureCode = PEG.Compiler.formatCode(
"if (context.reportMatchFailures && ${resultVar} === null) {",
" this._matchFailed(new PEG.Parser.NamedRuleMatchFailure(${humanName|string}));",
"}",
{
humanName: this._humanName,
resultVar: resultVar
}
);
} else {
var setReportMatchFailuresCode = "";
var restoreReportMatchFailuresCode = "";
var reportMatchFailureCode = "";
}
return PEG.Compiler.formatCode(
"result._parse_${name} = function(context) {",
" this._cache[${name|string}] = this._cache[${name|string}] || [];",
" var cachedResult = this._cache[${name|string}][this._pos];",
" if (cachedResult !== undefined) {",
" this._pos += cachedResult.length;",
" return cachedResult.result;",
" }",
" ",
" var pos = this._pos;",
" ",
" ${setReportMatchFailuresCode}",
" ${code}",
" ${restoreReportMatchFailuresCode}",
" ${reportMatchFailureCode}",
" ",
" this._cache[${name|string}][pos] = {",
" length: this._pos - pos,",
" result: ${resultVar}",
" };",
" return ${resultVar};",
"};",
{
name: this._name,
setReportMatchFailuresCode: setReportMatchFailuresCode,
restoreReportMatchFailuresCode: restoreReportMatchFailuresCode,
reportMatchFailureCode: reportMatchFailureCode,
code: this._expression.compile(resultVar),
resultVar: resultVar
}
);
};
/*
* The contract for all code fragments generated by the following |compile|
* methods is as follows:
*
* * The code fragment should try to match a part of the input starting with the
* position indicated in |this._pos|. That position may point past the end of
* the input.
*
* * If the code fragment matches the input, it advances |this._pos| after the
* matched part of the input and sets variable with a name stored in
* |resultVar| to appropriate value, which is always non-null.
*
* * If the code fragment does not match the input, it does not change
* |this._pos| and it sets a variable with a name stored in |resultVar| to
* |null|.
*/
PEG.Grammar.Literal.prototype.compile = function(resultVar) {
return PEG.Compiler.formatCode(
"if (this._input.substr(this._pos, ${length}) === ${value|string}) {",
" var ${resultVar} = ${value|string};",
" this._pos += ${length};",
"} else {",
" var ${resultVar} = null;",
" if (context.reportMatchFailures) {",
" this._matchFailed(new PEG.Parser.LiteralMatchFailure(${value|string}));",
" }",
"}",
{
value: this._value,
length: this._value.length,
resultVar: resultVar
}
);
};
PEG.Grammar.Any.prototype.compile = function(resultVar) {
return PEG.Compiler.formatCode(
"if (this._input.length > this._pos) {",
" var ${resultVar} = this._input[this._pos];",
" this._pos++;",
"} else {",
" var ${resultVar} = null;",
" if (context.reportMatchFailures) {",
" this._matchFailed(new PEG.Parser.AnyMatchFailure());",
" }",
"}",
{ resultVar: resultVar }
);
};
PEG.Grammar.Sequence.prototype.compile = function(resultVar) {
var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos");
var elementResultVars = PEG.ArrayUtils.map(this._elements, function() {
return PEG.Compiler.generateUniqueIdentifier("result")
});
var code = PEG.Compiler.formatCode(
"var ${resultVar} = ${elementResultVarArray};",
{
resultVar: resultVar,
elementResultVarArray: "[" + elementResultVars.join(", ") + "]"
}
);
for (var i = this._elements.length - 1; i >= 0; i--) {
code = PEG.Compiler.formatCode(
"${elementCode}",
"if (${elementResultVar} !== null) {",
" ${code}",
"} else {",
" var ${resultVar} = null;",
" this._pos = ${savedPosVar};",
"}",
{
elementCode: this._elements[i].compile(elementResultVars[i]),
elementResultVar: elementResultVars[i],
code: code,
savedPosVar: savedPosVar,
resultVar: resultVar
}
);
}
return PEG.Compiler.formatCode(
"var ${savedPosVar} = this._pos;",
"${code}",
{
code: code,
savedPosVar: savedPosVar
}
);
};
PEG.Grammar.Choice.prototype.compile = function(resultVar) {
var code = PEG.Compiler.formatCode(
"var ${resultVar} = null;",
{ resultVar: resultVar }
);
for (var i = this._alternatives.length - 1; i >= 0; i--) {
var alternativeResultVar = PEG.Compiler.generateUniqueIdentifier("result");
code = PEG.Compiler.formatCode(
"${alternativeCode}",
"if (${alternativeResultVar} !== null) {",
" var ${resultVar} = ${alternativeResultVar};",
"} else {",
" ${code};",
"}",
{
alternativeCode: this._alternatives[i].compile(alternativeResultVar),
alternativeResultVar: alternativeResultVar,
code: code,
resultVar: resultVar
}
);
}
return code;
};
PEG.Grammar.ZeroOrMore.prototype.compile = function(resultVar) {
var elementResultVar = PEG.Compiler.generateUniqueIdentifier("result");
return PEG.Compiler.formatCode(
"var ${resultVar} = [];",
"${elementCode}",
"while (${elementResultVar} !== null) {",
" ${resultVar}.push(${elementResultVar});",
" ${elementCode}",
"}",
{
elementCode: this._element.compile(elementResultVar),
elementResultVar: elementResultVar,
resultVar: resultVar
}
);
};
PEG.Grammar.NotPredicate.prototype.compile = function(resultVar) {
var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos");
var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar");
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
return PEG.Compiler.formatCode(
"var ${savedPosVar} = this._pos;",
"var ${savedReportMatchFailuresVar} = context.reportMatchFailures;",
"context.reportMatchFailures = false;",
"${expressionCode}",
"context.reportMatchFailures = ${savedReportMatchFailuresVar};",
"if (${expressionResultVar} === null) {",
" var ${resultVar} = '';",
"} else {",
" var ${resultVar} = null;",
" this._pos = ${savedPosVar};",
"}",
{
expressionCode: this._expression.compile(expressionResultVar),
expressionResultVar: expressionResultVar,
savedPosVar: savedPosVar,
savedReportMatchFailuresVar: savedReportMatchFailuresVar,
resultVar: resultVar
}
);
};
PEG.Grammar.RuleRef.prototype.compile = function(resultVar) {
return PEG.Compiler.formatCode(
"var ${resultVar} = this.${ruleMethod}(context);",
{
ruleMethod: "_parse_" + this._name,
resultVar: resultVar
}
);
};
PEG.Grammar.Action.prototype.compile = function(resultVar) {
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
if (typeof(this._action) === "function") {
var actionFunction = this._action.toString();
} else {
var actionCode = this._action.replace(
/\$(\d+)/g,
function(match, digits) {
return PEG.Compiler.formatCode(
"(arguments[${index}])",
{ index: parseInt(digits) - 1 }
);
}
)
var actionFunction = PEG.Compiler.formatCode(
"function() { ${actionCode} }",
{ actionCode: actionCode }
);
}
/*
* In case of sequences, we splat their elements into function arguments one
* by one. Example:
*
* start: "a" "b" "c" { alert(arguments.length) } // => "3"
*/
var invokeFunctionName = this._expression instanceof PEG.Grammar.Sequence
? "apply"
: "call";
return PEG.Compiler.formatCode(
"${expressionCode}",
"var ${resultVar} = ${expressionResultVar} !== null",
" ? (${actionFunction}).${invokeFunctionName}(this, ${expressionResultVar})",
" : null;",
{
expressionCode: this._expression.compile(expressionResultVar),
expressionResultVar: expressionResultVar,
actionFunction: actionFunction,
invokeFunctionName: invokeFunctionName,
resultVar: resultVar
}
);
};
/* ===== PEG.grammarParser ===== */
var returnFirstArg = function() { return arguments[0]; }
var returnSecondArg = function() { return arguments[1]; }
var returnSecondArgJoined = function() { return arguments[1].join(""); }
var returnFirstArgAndSecondArgJoined = function() { return arguments[0] + arguments[1].join(""); }
function characterRule(name, ch) {
with (PEG.Grammar) {
return new Rule(
name,
null,
new Action(
new Sequence([new Literal(ch), new RuleRef("__")]),
returnFirstArg
)
);
}
}
/* Bootstrapping is really badly needed. */
with (PEG.Grammar) {
PEG.grammarParser = PEG.buildParser({
grammar:
new Rule(
"grammar",
null,
new Action(
new Sequence([
new RuleRef("__"),
new RuleRef("rule"),
new ZeroOrMore(new RuleRef("rule"))
]),
function(dummy, first, rest) {
var rules = [first].concat(rest);
var result = {};
for (var i = 0; i < rules.length; i++) {
result[rules[i].getName()] = rules[i];
}
return result;
}
)
),
rule:
new Rule(
"rule",
null,
new Action(
new Sequence([
new RuleRef("identifier"),
new Choice([new RuleRef("literal"), new Literal("")]),
new RuleRef("colon"),
new RuleRef("expression")
]),
function(name, humanName, dummy, expression) {
return new PEG.Grammar.Rule(
name,
humanName !== "" ? humanName : null,
expression
);
}
)
),
expression:
new Rule("expression", null, new RuleRef("choice")),
choice:
new Rule(
"choice",
null,
new Action(
new Sequence([
new RuleRef("sequence"),
new ZeroOrMore(
new Sequence([new RuleRef("slash"), new RuleRef("sequence")])
)
]),
function(first, rest) {
return rest.length > 0
? new PEG.Grammar.Choice([first].concat(PEG.ArrayUtils.map(
rest,
function(element) { return element[1]; }
)))
: first;
}
)
),
sequence:
new Rule(
"sequence",
null,
new Choice([
new Action(
new Sequence([
new ZeroOrMore(new RuleRef("prefixed")),
new RuleRef("action")
]),
function(expressions, action) {
return new PEG.Grammar.Action(
expressions.length != 1
? new PEG.Grammar.Sequence(expressions)
: expressions[0],
action
);
}
),
new Action(
new ZeroOrMore(new RuleRef("prefixed")),
function(expressions) {
return expressions.length != 1
? new PEG.Grammar.Sequence(expressions)
: expressions[0];
}
)
])
),
prefixed:
new Rule(
"prefixed",
null,
new Choice([
new Action(
new Sequence([new RuleRef("and"), new RuleRef("suffixed")]),
function(dummy, expression) {
return new PEG.Grammar.NotPredicate(
new PEG.Grammar.NotPredicate(expression)
);
}
),
new Action(
new Sequence([new RuleRef("not"), new RuleRef("suffixed")]),
function(dummy, expression) {
return new PEG.Grammar.NotPredicate(expression);
}
),
new RuleRef("suffixed")
])
),
suffixed:
new Rule(
"suffixed",
null,
new Choice([
new Action(
new Sequence([new RuleRef("primary"), new RuleRef("question")]),
function(expression) {
return new PEG.Grammar.Choice([
expression,
new PEG.Grammar.Literal("")
]);
}
),
new Action(
new Sequence([new RuleRef("primary"), new RuleRef("star")]),
function(expression) { return new PEG.Grammar.ZeroOrMore(expression); }
),
new Action(
new Sequence([new RuleRef("primary"), new RuleRef("plus")]),
function(expression) {
return new PEG.Grammar.Action(
new PEG.Grammar.Sequence([
expression,
new PEG.Grammar.ZeroOrMore(expression)
]),
function(first, rest) { return [first].concat(rest); }
);
}
),
new RuleRef("primary")
])
),
primary:
new Rule(
"primary",
null,
new Choice([
new Action(
new Sequence([
new RuleRef("identifier"),
new NotPredicate(
new Sequence([
new Choice([new RuleRef("literal"), new Literal("")]),
new RuleRef("colon")
])
)
]),
function(identifier) { return new PEG.Grammar.RuleRef(identifier); }
),
new Action(
new RuleRef("literal"),
function(literal) { return new PEG.Grammar.Literal(literal); }
),
new Action(
new RuleRef("dot"),
function() { return new PEG.Grammar.Any(); }
),
new Action(
new RuleRef("class"),
function(characters) {
return new PEG.Grammar.Choice(
PEG.ArrayUtils.map(
characters.split(""),
function(character) {
return new PEG.Grammar.Literal(character);
}
)
);
}
),
new Action(
new Sequence([
new RuleRef("lparen"),
new RuleRef("expression"),
new RuleRef("rparen")
]),
returnSecondArg
)
])
),
/* "Lexical" elements */
action:
new Rule(
"action",
"action",
new Action(
new Sequence([new RuleRef("braced"), new RuleRef("__")]),
function(braced) { return braced.substr(1, braced.length - 2); }
)
),
braced:
new Rule(
"braced",
null,
new Action(
new Sequence([
new Literal("{"),
new ZeroOrMore(
new Choice([
new RuleRef("braced"),
new RuleRef("nonBraceCharacters")
])
),
new Literal("}")
]),
function(leftBrace, parts, rightBrace) {
return leftBrace + parts.join("") + rightBrace;
}
)
),
nonBraceCharacters:
new Rule(
"nonBraceCharacters",
null,
new Action(
new Sequence([
new RuleRef("nonBraceCharacter"),
new ZeroOrMore(new RuleRef("nonBraceCharacter"))
]),
returnFirstArgAndSecondArgJoined
)
),
nonBraceCharacter:
new Rule(
"nonBraceCharacter",
null,
new Action(
new Sequence([
new NotPredicate(new Choice([new Literal("{"), new Literal("}")])),
new Any()
]),
returnSecondArg
)
),
colon: characterRule("colon", ":"),
slash: characterRule("slash", "/"),
and: characterRule("and", "&"),
not: characterRule("not", "!"),
question: characterRule("question", "?"),
star: characterRule("star", "*"),
plus: characterRule("plus", "+"),
lparen: characterRule("lparen", "("),
rparen: characterRule("rparen", ")"),
dot: characterRule("dot", "."),
/*
* Modelled after ECMA-262, 5th ed., 7.6, but much simplified:
*
* * no Unicode escape sequences
*
* * "Unicode combining marks" and "Unicode connection punctuation" can't
* be part of the identifier
*
* * only [a-zA-Z] is considered a "Unicode letter"
*
* * only [0-9] is considered a "Unicode digit"
*
* The simplifications were made just to make the implementation little
* bit easier, there is no "philosophical" reason behind them.
*/
identifier:
new Rule(
"identifier",
"identifier",
new Action(
new Sequence([
new Choice([
new RuleRef("letter"),
new Literal("_"),
new Literal("$")
]),
new ZeroOrMore(
new Choice([
new RuleRef("letter"),
new RuleRef("digit"),
new Literal("_"),
new Literal("$")
])
),
new RuleRef("__")
]),
returnFirstArgAndSecondArgJoined
)
),
/*
* Modelled after ECMA-262, 5th ed., 7.8.4. (syntax & semantics, rules only
* vaguely),
*/
literal:
new Rule(
"literal",
"literal",
new Action(
new Sequence([
new Choice([
new RuleRef("doubleQuotedLiteral"),
new RuleRef("singleQuotedLiteral")
]),
new RuleRef("__")
]),
returnFirstArg
)
),
doubleQuotedLiteral:
new Rule(
"doubleQuotedLiteral",
null,
new Action(
new Sequence([
new Literal('"'),
new ZeroOrMore(new RuleRef("doubleQuotedCharacter")),
new Literal('"')
]),
returnSecondArgJoined
)
),
doubleQuotedCharacter:
new Rule(
"doubleQuotedCharacter",
null,
new Choice([
new RuleRef("simpleDoubleQuotedCharacter"),
new RuleRef("simpleEscapeSequence"),
new RuleRef("zeroEscapeSequence"),
new RuleRef("hexEscapeSequence"),
new RuleRef("unicodeEscapeSequence"),
new RuleRef("eolEscapeSequence")
])
),
simpleDoubleQuotedCharacter:
new Rule(
"simpleDoubleQuotedCharacter",
null,
new Action(
new Sequence([
new NotPredicate(
new Choice([
new Literal('"'),
new Literal("\\"),
new RuleRef("eolChar")
])
),
new Any()
]),
returnSecondArg
)
),
singleQuotedLiteral:
new Rule(
"singleQuotedLiteral",
null,
new Action(
new Sequence([
new Literal("'"),
new ZeroOrMore(new RuleRef("singleQuotedCharacter")),
new Literal("'")
]),
returnSecondArgJoined
)
),
singleQuotedCharacter:
new Rule(
"singleQuotedCharacter",
null,
new Choice([
new RuleRef("simpleSingleQuotedCharacter"),
new RuleRef("simpleEscapeSequence"),
new RuleRef("zeroEscapeSequence"),
new RuleRef("hexEscapeSequence"),
new RuleRef("unicodeEscapeSequence"),
new RuleRef("eolEscapeSequence")
])
),
simpleSingleQuotedCharacter:
new Rule(
"simpleSingleQuotedCharacter",
null,
new Action(
new Sequence([
new NotPredicate(
new Choice([
new Literal("'"),
new Literal("\\"),
new RuleRef("eolChar")
])
),
new Any()
]),
returnSecondArg
)
),
"class":
new Rule(
"class",
"character class",
new Action(
new Sequence([
new Literal("["),
new ZeroOrMore(
new Choice([
new RuleRef("classCharacterRange"),
new RuleRef("classCharacter")
])
),
new Literal("]"),
new RuleRef("__")
]),
returnSecondArgJoined
)
),
classCharacterRange:
new Rule(
"classCharacterRange",
null,
new Action(
new Sequence([
new RuleRef("bracketDelimitedCharacter"),
new Literal("-"),
new RuleRef("bracketDelimitedCharacter")
]),
function(begin, dummy2, end) {
var beginCharCode = begin.charCodeAt(0);
var endCharCode = end.charCodeAt(0);
if (beginCharCode > endCharCode) {
throw new PEG.Parser.SyntaxError(
"Invalid character range: " + begin + "-" + end + "."
);
}
var result = "";
for (var charCode = beginCharCode; charCode <= endCharCode; charCode++) {
result += String.fromCharCode(charCode);
}
return result;
}
)
),
classCharacter:
new Rule("classCharacter", null, new RuleRef("bracketDelimitedCharacter")),
bracketDelimitedCharacter:
new Rule(
"bracketDelimitedCharacter",
null,
new Choice([
new RuleRef("simpleBracketDelimitedCharacter"),
new RuleRef("simpleEscapeSequence"),
new RuleRef("zeroEscapeSequence"),
new RuleRef("hexEscapeSequence"),
new RuleRef("unicodeEscapeSequence"),
new RuleRef("eolEscapeSequence")
])
),
simpleBracketDelimitedCharacter:
new Rule(
"simpleBracketDelimitedCharacter",
null,
new Action(
new Sequence([
new NotPredicate(
new Choice([
new Literal(']'),
new Literal("\\"),
new RuleRef("eolChar")
])
),
new Any()
]),
returnSecondArg
)
),
simpleEscapeSequence:
new Rule(
"simpleEscapeSequence",
null,
new Action(
new Sequence([
new Literal("\\"),
new NotPredicate(
new Choice([
new RuleRef("digit"),
new Literal("x"),
new Literal("u"),
new RuleRef("eolChar")
])
),
new Any()
]),
function(dummy1, dummy2, character) {
return character
.replace("b", "\b")
.replace("f", "\f")
.replace("n", "\n")
.replace("r", "\r")
.replace("t", "\t")
.replace("v", "\v")
}
)
),
zeroEscapeSequence:
new Rule(
"zeroEscapeSequence",
null,
new Action(
new Sequence([
new Literal("\\0"),
new NotPredicate(new RuleRef("digit"))
]),
function() { return "\0" }
)
),
hexEscapeSequence:
new Rule(
"hexEscapeSequence",
null,
new Action(
new Sequence([
new Literal("\\x"),
new RuleRef("hexDigit"),
new RuleRef("hexDigit")
]),
function(dummy, digit1, digit2) {
return String.fromCharCode(parseInt("0x" + digit1 + digit2));
}
)
),
unicodeEscapeSequence:
new Rule(
"unicodeEscapeSequence",
null,
new Action(
new Sequence([
new Literal("\\u"),
new RuleRef("hexDigit"),
new RuleRef("hexDigit"),
new RuleRef("hexDigit"),
new RuleRef("hexDigit")
]),
function(dummy, digit1, digit2, digit3, digit4) {
return String.fromCharCode(parseInt(
"0x" + digit1 + digit2 + digit3 + digit4
));
}
)
),
eolEscapeSequence:
new Rule(
"eolEscapeSequence",
null,
new Action(
new Sequence([new Literal("\\"), new RuleRef("eol")]),
returnSecondArg
)
),
digit:
new Rule(
"digit",
null,
new Choice([
new Literal("0"),
new Literal("1"),
new Literal("2"),
new Literal("3"),
new Literal("4"),
new Literal("5"),
new Literal("6"),
new Literal("7"),
new Literal("8"),
new Literal("9")
])
),
hexDigit:
new Rule(
"hexDigit",
null,
new Choice([
new Literal("0"),
new Literal("1"),
new Literal("2"),
new Literal("3"),
new Literal("4"),
new Literal("5"),
new Literal("6"),
new Literal("7"),
new Literal("8"),
new Literal("9"),
new Literal("a"),
new Literal("b"),
new Literal("c"),
new Literal("d"),
new Literal("e"),
new Literal("f"),
new Literal("A"),
new Literal("B"),
new Literal("C"),
new Literal("D"),
new Literal("E"),
new Literal("F")
])
),
letter:
new Rule(
"letter",
null,
new Choice([
new RuleRef("lowerCaseLetter"),
new RuleRef("upperCaseLetter")
])
),
lowerCaseLetter:
new Rule(
"lowerCaseLetter",
null,
new Choice([
new Literal("a"),
new Literal("b"),
new Literal("c"),
new Literal("d"),
new Literal("e"),
new Literal("f"),
new Literal("g"),
new Literal("h"),
new Literal("i"),
new Literal("j"),
new Literal("k"),
new Literal("l"),
new Literal("m"),
new Literal("n"),
new Literal("o"),
new Literal("p"),
new Literal("q"),
new Literal("r"),
new Literal("s"),
new Literal("t"),
new Literal("u"),
new Literal("v"),
new Literal("w"),
new Literal("x"),
new Literal("y"),
new Literal("z")
])
),
upperCaseLetter:
new Rule(
"upperCaseLetter",
null,
new Choice([
new Literal("A"),
new Literal("B"),
new Literal("C"),
new Literal("D"),
new Literal("E"),
new Literal("F"),
new Literal("G"),
new Literal("H"),
new Literal("I"),
new Literal("J"),
new Literal("K"),
new Literal("L"),
new Literal("M"),
new Literal("N"),
new Literal("O"),
new Literal("P"),
new Literal("Q"),
new Literal("R"),
new Literal("S"),
new Literal("T"),
new Literal("U"),
new Literal("V"),
new Literal("W"),
new Literal("X"),
new Literal("Y"),
new Literal("Z")
])
),
__:
new Rule(
"__",
null,
new ZeroOrMore(
new Choice([new RuleRef("whitespace"), new RuleRef("eol")])
)
),
/* Modelled after ECMA-262, 5th ed., 7.3. */
eol:
new Rule(
"eol",
"end of line",
new Choice([
new Literal("\n"),
new Literal("\r\n"),
new Literal("\r"),
new Literal("\u2028"),
new Literal("\u2029")
])
),
eolChar:
new Rule(
"eolChar",
null,
new Choice([
new Literal("\n"),
new Literal("\r"),
new Literal("\u2028"),
new Literal("\u2029")
])
),
/* Modelled after ECMA-262, 5th ed., 7.2. */
whitespace:
new Rule(
"whitespace",
"whitespace",
new Choice([
new Literal(" "),
new Literal("\t"),
new Literal("\v"),
new Literal("\f"),
new Literal("\xA0"),
// Should be here, but causes infinite loop in Rhino:
// new Literal("\uFEFF"),
new Literal("\u1680"),
new Literal("\u180E"),
new Literal("\u2000"),
new Literal("\u2001"),
new Literal("\u2002"),
new Literal("\u2003"),
new Literal("\u2004"),
new Literal("\u2005"),
new Literal("\u2006"),
new Literal("\u2007"),
new Literal("\u2008"),
new Literal("\u2009"),
new Literal("\u200A"),
new Literal("\u202F"),
new Literal("\u205F"),
new Literal("\u3000")
])
),
}, "grammar");
}
})();