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.

285 lines
7.8 KiB
JavaScript

/*
* PEG.js runtime.
*
* Required by all parsers generated by PEG.js.
*/
PEG = {};
(function() {
/* ===== PEG.ArrayUtils ===== */
/* Array manipulation utility functions. */
PEG.ArrayUtils = {
each: function(array, callback) {
var length = array.length;
for (var i = 0; i < length; i++) {
callback(array[i]);
}
},
map: function(array, callback) {
var result = [];
var length = array.length;
for (var i = 0; i < length; i++) {
result[i] = callback(array[i]);
}
return result;
}
};
/* ===== PEG.StringUtils ===== */
/* String manipulation utility functions. */
PEG.StringUtils = {
/*
* Surrounds the string with quotes and escapes characters inside so that the
* result is a valid JavaScript string.
*/
quote: function(s) {
/*
* ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a string
* literal except for the closing quote character, backslash, carriage
* return, line separator, paragraph separator, and line feed. Any character
* may appear in the form of an escape sequence.
*/
return '"' + s
.replace(/\\/g, '\\\\') // backslash
.replace(/"/g, '\\"') // closing quote character
.replace(/\r/g, '\\r') // carriage return
.replace(/\u2028/g, '\\u2028') // line separator
.replace(/\u2029/g, '\\u2029') // paragraph separator
.replace(/\n/g, '\\n') // line feed
+ '"';
}
};
/* ===== PEG.RegExpUtils ===== */
/* RegExp manipulation utility functions. */
PEG.RegExpUtils = {
/*
* Escapes characters inside the string so that it can be used as a list of
* characters in a character class of a regular expresion.
*/
quoteForClass: function(s) {
/* Based on ECMA-262, 5th ed., 7.8.5 & 15.10.1. */
return s
.replace(/\\/g, '\\\\') // backslash
.replace(/\//g, '\\/') // closing slash
.replace(/]/g, '\\]') // closing bracket
.replace(/-/g, '\\-') // dash
.replace(/\r/g, '\\r') // carriage return
.replace(/\u2028/g, '\\u2028') // line separator
.replace(/\u2029/g, '\\u2029') // paragraph separator
.replace(/\n/g, '\\n') // line feed
}
};
/* ===== PEG.Parser ===== */
/* Prototype of all parsers generated by PEG.js. */
PEG.Parser = function(startRule) { this._startRule = startRule; }
PEG.Parser.prototype = {
_matchFailed: function(failure) {
if (this._pos > this._rightmostMatchFailuresPos) {
this._rightmostMatchFailuresPos = this._pos;
this._rightmostMatchFailures = [];
}
this._rightmostMatchFailures.push(failure);
},
/*
* Parses the input with a generated parser. If the parsing is successfull,
* returns a value explicitly or implicitly specified by the grammar from
* which the parser was generated (see |PEG.buildParser|). If the parsing is
* unsuccessful, throws |PEG.Parser.SyntaxError| describing the error.
*/
parse: function(input) {
var that = this;
function initialize() {
that._input = input;
that._pos = 0;
that._rightmostMatchFailuresPos = 0;
that._rightmostMatchFailures = [];
that._cache = {};
}
function buildErrorMessage() {
function buildExpectedFromMatchFailures(failures) {
switch (failures.length) {
case 0:
return "end of input";
case 1:
return failures[0].toString();
default:
return PEG.ArrayUtils.map(
failures.slice(0, failures.length - 1),
function(failure) { return failure.toString(); }
).join(", ")
+ " or "
+ failures[failures.length - 1].toString();
}
}
var expected = buildExpectedFromMatchFailures(
that._rightmostMatchFailures
);
var pos = Math.max(that._pos, that._rightmostMatchFailuresPos);
var actual = pos < that._input.length
? PEG.StringUtils.quote(that._input.charAt(pos))
: "end of input";
return "Expected " + expected + " but " + actual + " found.";
}
function computeErrorPosition() {
/*
* The first idea was to use |String.split| to break the input up to the
* error position along newlines and derive the line and column from
* there. However IE's |split| implementation is so broken that it was
* enough to prevent it.
*/
var input = that._input;
var pos = that._rightmostMatchFailuresPos;
var line = 1;
var column = 1;
var seenCR = false;
for (var i = 0; i < pos; i++) {
var ch = input.charAt(i);
if (ch === "\n") {
if (!seenCR) { line++; }
column = 1;
seenCR = false;
} else if (ch === "\r" | ch === "\u2028" || ch === "\u2029") {
line++;
column = 1;
seenCR = true;
} else {
column++;
seenCR = false;
}
}
return { line: line, column: column };
}
initialize();
var initialContext = {
reportMatchFailures: true
};
var result = this["_parse_" + this._startRule](initialContext);
/*
* The parser is now in one of the following three states:
*
* 1. The parser successfully parsed the whole input.
*
* - |result !== null|
* - |that._pos === input.length|
* - |that._rightmostMatchFailures.length| may or may not contain
* something
*
* 2. The parser successfully parsed only a part of the input.
*
* - |result !== null|
* - |that._pos < input.length|
* - |that._rightmostMatchFailures.length| may or may not contain
* something
*
* 3. The parser did not successfully parse any part of the input.
*
* - |result === null|
* - |that._pos === 0|
* - |that._rightmostMatchFailures.length| contains at least one failure
*
* All code following this comment (including called functions) must
* handle these states.
*/
if (result === null || this._pos !== input.length) {
var errorPosition = computeErrorPosition();
throw new PEG.Parser.SyntaxError(
buildErrorMessage(),
errorPosition.line,
errorPosition.column
);
}
return result;
},
/* Returns the parser source code. */
toSource: function() { return this._source; }
};
/* ===== PEG.Parser.LiteralMatchFailure ===== */
/* Stores information about a literal match failure. */
PEG.Parser.LiteralMatchFailure = function(value) { this._value = value; };
PEG.Parser.LiteralMatchFailure.prototype = {
toString: function() { return PEG.StringUtils.quote(this._value); }
};
/* ===== PEG.Parser.ClassMatchFailure ===== */
/* Stores information about a class match failure. */
PEG.Parser.ClassMatchFailure = function(characters) {
this._characters = characters;
};
PEG.Parser.ClassMatchFailure.prototype = {
toString: function() { return "[" + this._characters + "]"; }
};
/* ===== PEG.Parser.AnyMatchFailure ===== */
/* Stores information about a failure to match a "." expression. */
PEG.Parser.AnyMatchFailure = function() {}
PEG.Parser.AnyMatchFailure.prototype = {
toString: function() { return "any character"; }
};
/* ===== PEG.Parser.NamedRuleMatchFailure ===== */
/* Stores information about a failure to match a named rule. */
PEG.Parser.NamedRuleMatchFailure = function(displayName) {
this._displayName = displayName;
}
PEG.Parser.NamedRuleMatchFailure.prototype = {
toString: function() { return this._displayName; }
};
/* ===== PEG.Parser.SyntaxError ===== */
/* Thrown when a parser encounters a syntax error. */
PEG.Parser.SyntaxError = function(message, line, column) {
this.name = "PEG.Parser.SyntaxError";
this.message = message;
this.line = line;
this.column = column;
};
PEG.Parser.SyntaxError.prototype = Error.prototype;
})();