Skip to content

Instantly share code, notes, and snippets.

@shamansir
Last active December 27, 2015 15:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shamansir/7348144 to your computer and use it in GitHub Desktop.
Save shamansir/7348144 to your computer and use it in GitHub Desktop.
Comparison between parser generated with Peg-js and parser generated with Peg-js-fn
/*
* Classic example grammar, which recognizes simple arithmetic expressions like
* "2*(3+4)". The parser generated from this grammar then computes their value.
*/
start
= additive
additive
= left:multiplicative "+" right:additive { return left + right; }
/ multiplicative
multiplicative
= left:primary "*" right:multiplicative { return left * right; }
/ primary
primary
= integer
/ "(" additive:additive ")" { return additive; }
integer "integer"
= digits:[0-9]+ { return parseInt(digits.join(""), 10); }
module.exports = (function(){
/* Generated by PEG.js-fn 0.7.0, Functional modification by shaman.sir@gmail.com (http://shamansir.github.io/pegjs-fn/). */
/* Original version developed by David Majda (http://pegjs.majda.cz/) */
/* ########### ENVIRONMENT ########### */
var input,
options;
var pos, // 0, parser position
p_pos; // 0, previous parser position
// This code encloses all of the user blocks (initializer and/or actions)
// in their own sandbox, so if there is an initializer, its inner variables
// will [only] be accessible to actions; this, however, requires an initializer
// not to have any first-level return statements. Also, this approach keeps parser
// inner variables safe from user access, except the ones defined above.
var __p_blocks = (function() { return function() {
// backwards compatibility with original peg-js
function offset() { return p_pos; }
function text() { return input.substring(p_pos, pos); }
/* ########### USER CODE ########### */
/* ----------- BLOCKS ----------- */
// Blocks are grouped by rule name and id; they all get access to current context
// through $ctx variable which they expand into their arguments. Arguments
// names are precalculated during parser generation process.
// $f and $ctx variables are named so creepy just to ensure that parser writer will not use them
// for naming variables in his code (only $ctx may clash in this architecture, in fact),
// we hope any modern environment supports Unicode now
return {
"additive": [
function($ctx) {
// additive[0]
return (function(left,right) {
return left + right;
})($ctx.left,$ctx.right);
}
],
"multiplicative": [
function($ctx) {
// multiplicative[0]
return (function(left,right) {
return left * right;
})($ctx.left,$ctx.right);
}
],
"primary": [
function($ctx) {
// primary[0]
return (function(additive) {
return additive;
})($ctx.additive);
}
],
"integer": [
function($ctx) {
// integer[0]
return (function(digits) {
return parseInt(digits, 10);
})($ctx.digits);
}
]
};
}; })();
// $f and $ctx variables are named so creepy just to ensure that parser writer will not use them
// for naming variables in his code (only $ctx may clash in this architecture, in fact),
// we hope any modern environment supports Unicode now
var $f = null; // holds a pointer to current rule blocks, will be initialized in parse() function
/* ########### PARSER ########### */
var __parser = function() {
/* =========== PARSER-DEPENDENT CODE =========== */
/* ----------- RULES DEFINITIONS ----------- */
var rules = {}; (function() {
rules.start = function() {
return (
ref(rules.additive)
());
};
rules.additive = function() {
var _code = $f.additive;
return (
choice(
action(
seqnc(
label("left",
ref(rules.multiplicative)
),
match("+"),
label("right",
ref(rules.additive)
)
),
/*{ return left + right; }*/
_code[0]),
ref(rules.multiplicative)
)
());
};
rules.multiplicative = function() {
var _code = $f.multiplicative;
return (
choice(
action(
seqnc(
label("left",
ref(rules.primary)
),
match("*"),
label("right",
ref(rules.multiplicative)
)
),
/*{ return left * right; }*/
_code[0]),
ref(rules.primary)
)
());
};
rules.primary = function() {
var _code = $f.primary;
return (
choice(
ref(rules.integer),
action(
seqnc(
match("("),
label("additive",
ref(rules.additive)
),
match(")")
),
/*{ return additive; }*/
_code[0])
)
());
};
rules.integer = function() {
var _code = $f.integer;
return (
as("integer",
action(
label("digits",
text(
some(
re(/^[0-9]/, "[0-9]")
)
)
),
/*{ return parseInt(digits, 10); }*/
_code[0])
)
());
};
})();
/* ----------- OPERATORS ----------- */
// get current char
function cc() { return (pos < ilen) ? input.charAt(pos) : EOI; }
var ref = def(inctx); // will call rule inside context
function action(f, code) {
return inctx(function() {
p_pos = pos; var res; // save previous position
f(); res = code(cctx);
if (res === null) { pos = p_pos;
failed(SOMETHING, NOTHING); }
return res;
});
}
action = def(action);
function seqnc(/*f...*/) {
var p_pos = pos; // save previous position locally
var fs = arguments,
s = [],
on_miss = function(e) {
pos = p_pos; throw e; };
for (var fi = 0, fl = fs.length;
fi < fl; fi++) {
s.push(safe(fs[fi], on_miss));
}
return s;
}
seqnc = def(seqnc);
function as(name, f) {
alias = name; var res = f();
alias = ''; return res;
}
as = def(as);
function choice(/*f...*/) {
var fs = arguments,
missed = 0,
my_e = null,
on_miss = function(e) {
my_e = e; missed = 1;
};
for (var fi = 0, fl = fs.length;
fi < fl; fi++) {
var res = safe(fs[fi], on_miss);
if (!missed) return res;
missed = 0;
}
throw my_e;
}
choice = def(choice);
function match(str) {
var slen = str.length;
if ((pos + slen) > ilen) {
failed(quote(str), EOI); // exits
}
if (input.substr(pos, slen) === str) {
pos += slen;
return str;
}
failed(quote(str), cc());
}
match = def(match);
function label(lbl, f) {
return cctx[lbl] = f();
}
label = def(label);
function text(f) {
var p_pos = pos; // save previous position locally
f(); return input.substr(p_pos,pos-p_pos);
}
text = def(text);
function some(f) {
return [f()].concat(any(f)());
}
some = def(some);
function any(f) {
var s = [],
missed = 0,
on_miss = function() { missed = 1; };
while (!missed) {
s.push(safe(f, on_miss));
}
if (missed) s.splice(-1);
return s;
}
any = def(any);
function re(rx, desc) {
var res, desc = desc || rx.source;
if (res = rx.exec(input.substr(pos))) {
if (res.index !== 0) failed(desc, cc());
pos += res[0].length;
return res[0];
} else failed(desc, cc());
}
re = def(re);
/* =========== PARSER-INDEPENDENT CODE =========== */
/* ----------- VARIABLES ----------- */
var ctx, // { ... }, root of the context
cctx; // { ... }, current context pointer
var current, // '-', current rule name
alias; // '', current rule alias, if defined
var failures, // {}, failures data
rmfpos, // 0, rightmost failure position
nr; // 0, no-report, fire errors w/o reporting
var /*input, */ilen; // input, input length
/* ----------- CONTEXT ----------- */
function prepare_ctx() { return ctx_lvl(); }
function ctx_lvl(parent) {
function CtxLevel() {
this.__p = parent;
this.__c = null;
}
CtxLevel.prototype = parent;
return new CtxLevel();
}
function din() { // dive in
if (!cctx.__c) cctx.__c = ctx_lvl(cctx);
cctx = cctx.__c;
}
function dout() { // dive out
if (!cctx.__p) throw new Error('reached top context level');
cctx = cctx.__p;
}
function inctx(f) { // execute in own context and return
var r, e;
din(); r = safe(f, function(err) { e = err; });
dout(); if (e) throw e;
return r;
}
/* ----------- DEFERRED ----------- */
// Makes passed function to save its argument values,
// but not execute until specially requested
function def(f) {
return function() {
return (function(f, args) {
return function() { return f.apply(null, args); };
})(f, arguments);
};
}
/* ----------- RULES WRAPPER ----------- */
function wrap(name, rule) {
return function() { current = name; return rule(); };
}
for (var rule in rules) {
rules[rule] = wrap(rule, rules[rule]);
}
/* ----------- RESULT OBJECT + PARSE FUNCTION ----------- */
var result = {
/*
* 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.MatchFailed| describing the error.
*/
parse: function(_input, _opts) {
// initialize variables
pos = 0, p_pos = 0, input = _input, options = _opts || {};
ilen = input.length, failures = {}, rmfpos = 0, nr = 0;
ctx = prepare_ctx(), cctx = ctx;
current = '-';
var startRule = options.startRule || "start";
if (["start"].indexOf(startRule) < 0) {
throw new Error("Can't start parsing from rule " + quote(startRule) + ".");
}
// call user initializer and also
// get blocks lying in the same context
$f = __p_blocks();
// find start rule
if (startRule) {
if (rules[startRule] === undefined) {
throw new SyntaxError("Rule not found: " + quote(startRule) + ".");
}
} else {
throw new Error("Start rule is not defined in options, no 'start' rule found and first rule in grammar was empty");
}
// and execute it
var res;
try {
res = rules[startRule]();
if ((pos < ilen) ||
(res === null)) failed(EOI, cc());
} catch(e) {
if (e instanceof _MatchFailed) {
// throw rightmost error instead
throw adapt(failures[rmfpos]);
}
throw e;
}
return res;
},
/* makes error type accessible outside */
MatchFailed: _MatchFailed,
SyntaxError: _SyntaxError
};
/* ----------- UTILS ----------- */
function Marker(human_str) { this.str=human_str; }
Marker.prototype.toString = function() { return this.str; };
var EOI = new Marker('end of input'),
ANY = new Marker('any character'),
SOMETHING = new Marker('progress'),
NOTHING = new Marker('nothing');
function hexOf(ch) {
var x = ch.charCodeAt(0),
v = x.toString(16).toUpperCase(),
h = (x > 0xFF),
i = (h ? 4 : 2) - v.length;
while (i--) v = v + '0';
return '\\' + (h ? 'u' : 'x') + v;
}
function quote(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.
*
* For portability, we also escape escape all control and non-ASCII
* characters. Note that "\0" and "\v" escape sequences are not used
* because JSHint does not like the first and IE the second.
*/
return '"' + s
.replace(/\\/g, '\\\\') // backslash
.replace(/"/g, '\\"') // closing quote character
.replace(/\x08/g, '\\b') // backspace
.replace(/\t/g, '\\t') // horizontal tab
.replace(/\n/g, '\\n') // line feed
.replace(/\f/g, '\\f') // form feed
.replace(/\r/g, '\\r') // carriage return
.replace(/[\x00-\x07\x0B\x0E-\x1F\x80-\uFFFF]/g, hexOf)
+ '"';
}
/* ----------- FAILURES ----------- */
function _MatchFailed(what, found, expected) {
this.what = what;
this.expected = expected || [];
this.found = found;
this.offset = pos;
this.xpos = [-1, -1];
this.line = -1;
this.column = -1;
}
_MatchFailed.prototype = new Error();
_MatchFailed.prototype.toString =
function() { return 'MatchFailed: '+emsg(this); };
var merr = function(fnd, exp) {
return new _MatchFailed(alias || current, fnd, exp);
};
function failed(expected, found) {
var expected = alias || expected;
// if no report required, just throw
if (nr) throw merr(found, [expected]);
if (pos > rmfpos) rmfpos = pos;
var e = failures[pos] ||
(failures[pos] = merr(found));
/*if (e.found !== found)*/ e.found = found;
var prev = e.expected;
var f; for (var i = prev.length; i--;) {
if (prev[i] === expected) {
f = 1; break;
}
}; if (!f) prev.push(expected);
throw e;
}
function safe(f, callback) {
try { return f();
} catch(e) {
if (e instanceof _MatchFailed) {
if (callback) callback(e);
} else { throw e; }
}
}
function emsg(e) {
var found_str, exp_str;
if (e.found instanceof Marker) {
found_str = e.found.str;
} else {
found_str = quote(e.found);
}
if (e.expected instanceof Marker) {
exp_str = e.expected.str;
} else if ((e.expected.length === 1) &&
(e.expected[0] instanceof Marker)) {
exp_str = e.expected[0].str;
} else {
var xs = e.expected;
exp_str = ((xs.length > 1)
? (xs.slice(0,-1).join(', ')+' '+
'or '+xs.slice(-1))
: xs[0]);
}
return /*'Stopped at '+quote(e.what)+': */'Expected '+exp_str+
' but '+found_str+' found.';
}
function adapt(e) {
e.message = emsg(e);
if ((e.found instanceof Marker) && (e.found === EOI)) e.found = null;
var xs = e.expected.sort();
if ((xs.length === 1) &&
(xs[0] === EOI)) {
e.expected = [];
}
for (var i = xs.length; i--;)
{ if (xs[i] instanceof Marker) xs[i] = xs[i].str; }
return e;
}
function _SyntaxError(msg) { // may be thrown from parser
this.message = msg;
}
_SyntaxError.prototype = new Error();
_SyntaxError.prototype.toString =
function() { return 'SyntaxError: '+this.message; };
/* ---------- RETURN RESULT OBJECT ----------- */
return result;
};
/* ----------- RETURN PARSER ----------- */
return __parser();
})();
module.exports = (function(){
/* Generated by PEG.js 0.6.2 (http://pegjs.majda.cz/). */
var result = {
/*
* 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, startRule) {
var parseFunctions = {
"additive": parse_additive,
"integer": parse_integer,
"multiplicative": parse_multiplicative,
"primary": parse_primary
};
if (startRule !== undefined) {
if (parseFunctions[startRule] === undefined) {
throw new Error("Invalid rule name: " + quote(startRule) + ".");
}
} else {
startRule = "additive";
}
var pos = 0;
var reportMatchFailures = true;
var rightmostMatchFailuresPos = 0;
var rightmostMatchFailuresExpected = [];
var cache = {};
function padLeft(input, padding, length) {
var result = input;
var padLength = length - input.length;
for (var i = 0; i < padLength; i++) {
result = padding + result;
}
return result;
}
function escape(ch) {
var charCode = ch.charCodeAt(0);
if (charCode <= 0xFF) {
var escapeChar = 'x';
var length = 2;
} else {
var escapeChar = 'u';
var length = 4;
}
return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length);
}
function quote(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(/\n/g, '\\n') // line feed
.replace(/[\x80-\uFFFF]/g, escape) // non-ASCII characters
+ '"';
}
function matchFailed(failure) {
if (pos < rightmostMatchFailuresPos) {
return;
}
if (pos > rightmostMatchFailuresPos) {
rightmostMatchFailuresPos = pos;
rightmostMatchFailuresExpected = [];
}
rightmostMatchFailuresExpected.push(failure);
}
function parse_additive() {
var cacheKey = 'additive@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var savedPos1 = pos;
var result5 = parse_multiplicative();
if (result5 !== null) {
if (input.substr(pos, 1) === "+") {
var result6 = "+";
pos += 1;
} else {
var result6 = null;
if (reportMatchFailures) {
matchFailed("\"+\"");
}
}
if (result6 !== null) {
var result7 = parse_additive();
if (result7 !== null) {
var result3 = [result5, result6, result7];
} else {
var result3 = null;
pos = savedPos1;
}
} else {
var result3 = null;
pos = savedPos1;
}
} else {
var result3 = null;
pos = savedPos1;
}
var result4 = result3 !== null
? (function(left, right) { return left + right; })(result3[0], result3[2])
: null;
if (result4 !== null) {
var result2 = result4;
} else {
var result2 = null;
pos = savedPos0;
}
if (result2 !== null) {
var result0 = result2;
} else {
var result1 = parse_multiplicative();
if (result1 !== null) {
var result0 = result1;
} else {
var result0 = null;;
};
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_multiplicative() {
var cacheKey = 'multiplicative@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var savedPos1 = pos;
var result5 = parse_primary();
if (result5 !== null) {
if (input.substr(pos, 1) === "*") {
var result6 = "*";
pos += 1;
} else {
var result6 = null;
if (reportMatchFailures) {
matchFailed("\"*\"");
}
}
if (result6 !== null) {
var result7 = parse_multiplicative();
if (result7 !== null) {
var result3 = [result5, result6, result7];
} else {
var result3 = null;
pos = savedPos1;
}
} else {
var result3 = null;
pos = savedPos1;
}
} else {
var result3 = null;
pos = savedPos1;
}
var result4 = result3 !== null
? (function(left, right) { return left * right; })(result3[0], result3[2])
: null;
if (result4 !== null) {
var result2 = result4;
} else {
var result2 = null;
pos = savedPos0;
}
if (result2 !== null) {
var result0 = result2;
} else {
var result1 = parse_primary();
if (result1 !== null) {
var result0 = result1;
} else {
var result0 = null;;
};
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_primary() {
var cacheKey = 'primary@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var result7 = parse_integer();
if (result7 !== null) {
var result0 = result7;
} else {
var savedPos0 = pos;
var savedPos1 = pos;
if (input.substr(pos, 1) === "(") {
var result4 = "(";
pos += 1;
} else {
var result4 = null;
if (reportMatchFailures) {
matchFailed("\"(\"");
}
}
if (result4 !== null) {
var result5 = parse_additive();
if (result5 !== null) {
if (input.substr(pos, 1) === ")") {
var result6 = ")";
pos += 1;
} else {
var result6 = null;
if (reportMatchFailures) {
matchFailed("\")\"");
}
}
if (result6 !== null) {
var result2 = [result4, result5, result6];
} else {
var result2 = null;
pos = savedPos1;
}
} else {
var result2 = null;
pos = savedPos1;
}
} else {
var result2 = null;
pos = savedPos1;
}
var result3 = result2 !== null
? (function(additive) { return additive; })(result2[1])
: null;
if (result3 !== null) {
var result1 = result3;
} else {
var result1 = null;
pos = savedPos0;
}
if (result1 !== null) {
var result0 = result1;
} else {
var result0 = null;;
};
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_integer() {
var cacheKey = 'integer@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedReportMatchFailures = reportMatchFailures;
reportMatchFailures = false;
var savedPos0 = pos;
if (input.substr(pos).match(/^[0-9]/) !== null) {
var result3 = input.charAt(pos);
pos++;
} else {
var result3 = null;
if (reportMatchFailures) {
matchFailed("[0-9]");
}
}
if (result3 !== null) {
var result1 = [];
while (result3 !== null) {
result1.push(result3);
if (input.substr(pos).match(/^[0-9]/) !== null) {
var result3 = input.charAt(pos);
pos++;
} else {
var result3 = null;
if (reportMatchFailures) {
matchFailed("[0-9]");
}
}
}
} else {
var result1 = null;
}
var result2 = result1 !== null
? (function(digits) { return parseInt(digits.join(""), 10); })(result1)
: null;
if (result2 !== null) {
var result0 = result2;
} else {
var result0 = null;
pos = savedPos0;
}
reportMatchFailures = savedReportMatchFailures;
if (reportMatchFailures && result0 === null) {
matchFailed("integer");
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function buildErrorMessage() {
function buildExpected(failuresExpected) {
failuresExpected.sort();
var lastFailure = null;
var failuresExpectedUnique = [];
for (var i = 0; i < failuresExpected.length; i++) {
if (failuresExpected[i] !== lastFailure) {
failuresExpectedUnique.push(failuresExpected[i]);
lastFailure = failuresExpected[i];
}
}
switch (failuresExpectedUnique.length) {
case 0:
return 'end of input';
case 1:
return failuresExpectedUnique[0];
default:
return failuresExpectedUnique.slice(0, failuresExpectedUnique.length - 1).join(', ')
+ ' or '
+ failuresExpectedUnique[failuresExpectedUnique.length - 1];
}
}
var expected = buildExpected(rightmostMatchFailuresExpected);
var actualPos = Math.max(pos, rightmostMatchFailuresPos);
var actual = actualPos < input.length
? quote(input.charAt(actualPos))
: '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 line = 1;
var column = 1;
var seenCR = false;
for (var i = 0; i < rightmostMatchFailuresPos; 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 };
}
var result = parseFunctions[startRule]();
/*
* The parser is now in one of the following three states:
*
* 1. The parser successfully parsed the whole input.
*
* - |result !== null|
* - |pos === input.length|
* - |rightmostMatchFailuresExpected| may or may not contain something
*
* 2. The parser successfully parsed only a part of the input.
*
* - |result !== null|
* - |pos < input.length|
* - |rightmostMatchFailuresExpected| may or may not contain something
*
* 3. The parser did not successfully parse any part of the input.
*
* - |result === null|
* - |pos === 0|
* - |rightmostMatchFailuresExpected| contains at least one failure
*
* All code following this comment (including called functions) must
* handle these states.
*/
if (result === null || pos !== input.length) {
var errorPosition = computeErrorPosition();
throw new this.SyntaxError(
buildErrorMessage(),
errorPosition.line,
errorPosition.column
);
}
return result;
},
/* Returns the parser source code. */
toSource: function() { return this._source; }
};
/* Thrown when a parser encounters a syntax error. */
result.SyntaxError = function(message, line, column) {
this.name = 'SyntaxError';
this.message = message;
this.line = line;
this.column = column;
};
result.SyntaxError.prototype = Error.prototype;
return result;
})();
module.exports = (function() {
/*
* Generated by PEG.js 0.8.0.
*
* http://pegjs.majda.cz/
*/
function peg$subclass(child, parent) {
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor();
}
function peg$SyntaxError(message, expected, found, offset, line, column) {
this.message = message;
this.expected = expected;
this.found = found;
this.offset = offset;
this.line = line;
this.column = column;
this.name = "SyntaxError";
}
peg$subclass(peg$SyntaxError, Error);
function peg$parse(input) {
var options = arguments.length > 1 ? arguments[1] : {},
parser = this,
peg$FAILED = {},
peg$startRuleFunctions = { start: peg$parsestart },
peg$startRuleFunction = peg$parsestart,
peg$c0 = "+",
peg$c1 = { type: "literal", value: "+", description: "\"+\"" },
peg$c2 = function(left, right) { return left + right; },
peg$c3 = "*",
peg$c4 = { type: "literal", value: "*", description: "\"*\"" },
peg$c5 = function(left, right) { return left * right; },
peg$c6 = "(",
peg$c7 = { type: "literal", value: "(", description: "\"(\"" },
peg$c8 = ")",
peg$c9 = { type: "literal", value: ")", description: "\")\"" },
peg$c10 = function(additive) { return additive; },
peg$c11 = { type: "other", description: "integer" },
peg$c12 = /^[0-9]/,
peg$c13 = { type: "class", value: "[0-9]", description: "[0-9]" },
peg$c14 = function(digits) { return parseInt(digits, 10); },
peg$currPos = 0,
peg$reportedPos = 0,
peg$cachedPos = 0,
peg$cachedPosDetails = { line: 1, column: 1, seenCR: false },
peg$maxFailPos = 0,
peg$maxFailExpected = [],
peg$silentFails = 0,
peg$result;
if ("startRule" in options) {
if (!(options.startRule in peg$startRuleFunctions)) {
throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
}
peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
}
function text() {
return input.substring(peg$reportedPos, peg$currPos);
}
function offset() {
return peg$reportedPos;
}
function line() {
return peg$computePosDetails(peg$reportedPos).line;
}
function column() {
return peg$computePosDetails(peg$reportedPos).column;
}
function expected(description) {
throw peg$buildException(
null,
[{ type: "other", description: description }],
peg$reportedPos
);
}
function error(message) {
throw peg$buildException(message, null, peg$reportedPos);
}
function peg$computePosDetails(pos) {
function advance(details, startPos, endPos) {
var p, ch;
for (p = startPos; p < endPos; p++) {
ch = input.charAt(p);
if (ch === "\n") {
if (!details.seenCR) { details.line++; }
details.column = 1;
details.seenCR = false;
} else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") {
details.line++;
details.column = 1;
details.seenCR = true;
} else {
details.column++;
details.seenCR = false;
}
}
}
if (peg$cachedPos !== pos) {
if (peg$cachedPos > pos) {
peg$cachedPos = 0;
peg$cachedPosDetails = { line: 1, column: 1, seenCR: false };
}
advance(peg$cachedPosDetails, peg$cachedPos, pos);
peg$cachedPos = pos;
}
return peg$cachedPosDetails;
}
function peg$fail(expected) {
if (peg$currPos < peg$maxFailPos) { return; }
if (peg$currPos > peg$maxFailPos) {
peg$maxFailPos = peg$currPos;
peg$maxFailExpected = [];
}
peg$maxFailExpected.push(expected);
}
function peg$buildException(message, expected, pos) {
function cleanupExpected(expected) {
var i = 1;
expected.sort(function(a, b) {
if (a.description < b.description) {
return -1;
} else if (a.description > b.description) {
return 1;
} else {
return 0;
}
});
while (i < expected.length) {
if (expected[i - 1] === expected[i]) {
expected.splice(i, 1);
} else {
i++;
}
}
}
function buildMessage(expected, found) {
function stringEscape(s) {
function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }
return s
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\x08/g, '\\b')
.replace(/\t/g, '\\t')
.replace(/\n/g, '\\n')
.replace(/\f/g, '\\f')
.replace(/\r/g, '\\r')
.replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
.replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); })
.replace(/[\u0100-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); })
.replace(/[\u1000-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); });
}
var expectedDescs = new Array(expected.length),
expectedDesc, foundDesc, i;
for (i = 0; i < expected.length; i++) {
expectedDescs[i] = expected[i].description;
}
expectedDesc = expected.length > 1
? expectedDescs.slice(0, -1).join(", ")
+ " or "
+ expectedDescs[expected.length - 1]
: expectedDescs[0];
foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input";
return "Expected " + expectedDesc + " but " + foundDesc + " found.";
}
var posDetails = peg$computePosDetails(pos),
found = pos < input.length ? input.charAt(pos) : null;
if (expected !== null) {
cleanupExpected(expected);
}
return new peg$SyntaxError(
message !== null ? message : buildMessage(expected, found),
expected,
found,
pos,
posDetails.line,
posDetails.column
);
}
function peg$parsestart() {
var s0;
s0 = peg$parseadditive();
return s0;
}
function peg$parseadditive() {
var s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$parsemultiplicative();
if (s1 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 43) {
s2 = peg$c0;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c1); }
}
if (s2 !== peg$FAILED) {
s3 = peg$parseadditive();
if (s3 !== peg$FAILED) {
peg$reportedPos = s0;
s1 = peg$c2(s1, s3);
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$parsemultiplicative();
}
return s0;
}
function peg$parsemultiplicative() {
var s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$parseprimary();
if (s1 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 42) {
s2 = peg$c3;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c4); }
}
if (s2 !== peg$FAILED) {
s3 = peg$parsemultiplicative();
if (s3 !== peg$FAILED) {
peg$reportedPos = s0;
s1 = peg$c5(s1, s3);
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$parseprimary();
}
return s0;
}
function peg$parseprimary() {
var s0, s1, s2, s3;
s0 = peg$parseinteger();
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 40) {
s1 = peg$c6;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c7); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parseadditive();
if (s2 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 41) {
s3 = peg$c8;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c9); }
}
if (s3 !== peg$FAILED) {
peg$reportedPos = s0;
s1 = peg$c10(s2);
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
return s0;
}
function peg$parseinteger() {
var s0, s1, s2, s3;
peg$silentFails++;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = [];
if (peg$c12.test(input.charAt(peg$currPos))) {
s3 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c13); }
}
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
if (peg$c12.test(input.charAt(peg$currPos))) {
s3 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c13); }
}
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s1 = input.substring(s1, peg$currPos);
} else {
s1 = s2;
}
if (s1 !== peg$FAILED) {
peg$reportedPos = s0;
s1 = peg$c14(s1);
}
s0 = s1;
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c11); }
}
return s0;
}
peg$result = peg$startRuleFunction();
if (peg$result !== peg$FAILED && peg$currPos === input.length) {
return peg$result;
} else {
if (peg$result !== peg$FAILED && peg$currPos < input.length) {
peg$fail({ type: "end", description: "end of input" });
}
throw peg$buildException(null, peg$maxFailExpected, peg$maxFailPos);
}
}
return {
SyntaxError: peg$SyntaxError,
parse: peg$parse
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment