Last active
December 27, 2015 15:29
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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); } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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