jscc-parser
Version:
A LALR(1) Parser Generator for JavaScript written in JavaScript.
348 lines (336 loc) • 8.48 kB
Plain Text
/*
This is the general, platform-independent part of every parser driver;
Input-/Output and Feature-Functions are done by the particular drivers
created for the particular platform.
*/
##HEADER##
var __parse=(function(/** number */ eof, /** number */ whitespace, /** number */ error_token){
/// there was "continue" in code, we must to replace it
var Continue = function(){throw Continue;};
/**
* @template T
* @param {T} value
* @constructor
* @extends {Error}
*/
var ReturnValue = function(value) {
Error.call(this);
this._value = value;
};
ReturnValue.prototype = Object.create(Error.prototype);
ReturnValue.prototype.constructor = ReturnValue;
/**
* @type {T}
* @private
*/
ReturnValue.prototype._value = null;
/**
* @returns {T}
*/
ReturnValue.prototype.valueOf = function() {
return this._value;
};
///can return value from any place of callback
function Return(value){
throw new ReturnValue(value);
}
var TERMINAL_ACTIONS = (function(){
function emptyFn(PCB){return PCB.att;}
var actions = ##TERMINAL_ACTIONS##
return function(/** @type {!PcbClass} */ PCB, match){
try{
return (actions[match] || emptyFn)(PCB);
}catch(e){
if(e instanceof ReturnValue)return e.valueOf();
if(e == Continue)return Continue;
throw e;
}
}
})();
/**
* @constructor
*/
var DfaLex = function() {
this._dfaData = ##DFA##;
};
/**
* @type {!Array<!{line: !Array, accept: !number}>}
* @private
*/
DfaLex.prototype._dfaData = [];
/**
* @type {number}
*/
DfaLex.prototype.match_pos = 0;
/**
* @type {?number}
*/
DfaLex.prototype.state = 0;
/**
* @type {?number}
*/
DfaLex.prototype.match = null;
/**
* @param {number} chr
* @param {number} pos
*/
DfaLex.prototype.exec = function(chr, pos) {
if (this.state !== null) {
if ((typeof this.state !== "number") || this.state >= this._dfaData.length) {
this.state = null;
throw new Error("Invalid value for DfaLex.state at chr " + chr + " and pos " + pos);
}
var line = this._dfaData[this.state].line;
if (typeof line === "undefined" || line === null) {
var badState = this.state;
this.state = null;
throw new Error("At chr " + chr + " and pos " + pos +
", DfaLex._dfaData[" + badState +
"] appears to exist, but its line property is " +
(typeof line === "undefined" ? "undefined." : "null."));
}
var p, st;
for (p = 1 << 8, st = line; p; p >>= 1) {
if ((chr & p) !== 0) {
st = st[1];
} else {
st = st[0];
}
if (typeof st === "undefined") {
st = null;
}
if (st === null)break;
if (Array.isArray(st))continue;
break;
}
var ac = this._dfaData[this.state].accept;
this.state = /** @type {?number} */ (st);
if (ac !== -1) {
this.match = /** @type{number} */ (ac);
this.match_pos = pos;
}
}
};
##TABLES##
##LABELS##
var ACTIONS = (function(){
var PCB = {};
var actions = ##ACTIONS##;
return function (/** number */ act, /** Array<*> */ vstack, /** !PcbClass */ pcb){
try{
PCB = pcb;
return actions[act].apply(null,vstack);
}catch(e){
if(e instanceof ReturnValue)return e.valueOf();
throw e;
}
}
})();
/**
* @param {number} top
* @param {?number} la
* @returns {?number}
*/
function get_act(top, la){
for(var i = 0; i < act_tab[top].length; i+=2)
if(act_tab[top][i] === la)
return act_tab[top][i+1];
return null;
}
function get_goto(top, pop){
for(var i = 0; i < goto_tab[top].length; i+=2)
if(goto_tab[top][i] === pop)
return goto_tab[top][i+1];
return null;
}
/**
* @param {!string} src
* @constructor
*/
var PcbClass = function(src) {
this.src = src;
};
/**
* @type {number}
*/
PcbClass.prototype.line = 1;
/**
* @type {number}
*/
PcbClass.prototype.column = 1;
/**
* @type {number}
*/
PcbClass.prototype.offset = 0;
/**
* @type {number}
*/
PcbClass.prototype.error_step = 0;
/**
* @type {string}
*/
PcbClass.prototype.src = "";
/**
* @type {string}
*/
PcbClass.prototype.att = "";
/**
* @type {?number}
*/
PcbClass.prototype.la = null;
/**
* @type {?number}
*/
PcbClass.prototype.act = null;
/**
* @returns {?number}
*/
PcbClass.prototype.lex = function() {
var /** number */ start, /** number */ pos, /** number */ chr, actionResult;
var dfa = new DfaLex();
var loop = true;
while(loop){
dfa.match_pos = 0;
pos = this.offset + 1;
do{
pos--;
dfa.state = 0;
dfa.match = null;
start = pos;
if(this.src.length <= start) {
this.la = eof;
return eof;
}
do{
chr = this.src.charCodeAt(pos);
dfa.exec(chr,pos);
if(dfa.state !== null)
this.accountChar(chr);
pos++;
}while(dfa.state !== null);
}while(whitespace > -1 && dfa.match === whitespace);
if(dfa.match !== null){
this.att = this.src.slice(start, dfa.match_pos);
this.offset = dfa.match_pos;
actionResult = TERMINAL_ACTIONS(this,dfa.match);
if(dfa.state !== null)
this.accountChar(chr);
if(actionResult === Continue)
continue;
this.att = actionResult;
}else {
this.att = "";
}
loop = false;
}
this.la = dfa.match;
return this.la;
};
/**
* @param {number} chr
*/
PcbClass.prototype.accountChar = function(chr) {
if( chr === 10 ){
this.line++;
this.column = 0;
}
this.column++;
};
function parse(/** string */ src, err_off, err_la){
/**
* @type {!Array<number>}
*/
var sstack = [0];
/**
* @type {!Array<*>}
*/
var vstack = [0];
/**
* @type {number}
*/
var err_cnt = 0;
/**
* @type {*}
*/
var rval;
/**
* @type {?number}
*/
var act;
/**
* @type {number}
*/
var i = 0;
var PCB = new PcbClass(src);
err_off = err_off || [];
err_la = err_la || [];
PCB.lex();
while(true){
PCB.act = get_act(sstack[0],PCB.la);
if(PCB.act === null && defact_tab[sstack[0]] >= 0)
PCB.act = -defact_tab[sstack[0]];
if(PCB.act === null){//Parse error? Try to recover!
//Report errors only when error_step is 0, and this is not a
//subsequent error from a previous parse
if(PCB.error_step === 0){
err_cnt++;
err_off.unshift(PCB.offset - PCB.att.length);
err_la.unshift([]);
for(i = 0; i < act_tab[sstack[0]].length; i+=2)
err_la[0].push(labels[act_tab[sstack[0]][i]]);
}
//Perform error recovery
while(sstack.length > 1 && PCB.act === null){
sstack.shift();
vstack.shift();
//Try to shift on error token
PCB.act = get_act(sstack[0],PCB.la);
if(PCB.act === error_token){
sstack.unshift(PCB.act);
vstack.unshift("");
}
}
//Is it better to leave the parser now?
if(sstack.length > 1 && PCB.act !== null){
//Ok, now try to shift on the next tokens
while(PCB.la !== eof){
PCB.act = act_tab[sstack[0]][i+1];
if(PCB.act != null)break;
while(PCB.lex() != null)PCB.offset++;
}
}
if(PCB.act === null || PCB.la === eof){
break;
}
//Try to parse the next three tokens successfully...
PCB.error_step = 3;
}
if(PCB.act > 0){//Shift
//Parse tree generation
sstack.unshift(PCB.act);
vstack.unshift(PCB.att);
PCB.lex();
//Successfull shift and right beyond error recovery?
if(PCB.error_step > 0)
PCB.error_step--;
}else{ //Reduce
act = -PCB.act;
//vstack.unshift(vstack);
rval = ACTIONS(act,vstack,PCB);
//vstack.shift();
sstack.splice(0,pop_tab[act][1]);
vstack.splice(0,pop_tab[act][1]);
PCB.act = get_goto(sstack[0],pop_tab[act][0]);
//Do some parse tree construction if desired
//Goal symbol match?
if(act === 0) break; //Don't use PCB.act here!
//...and push it!
sstack.unshift(PCB.act);
vstack.unshift(rval);
}
}
return err_cnt;
}
return parse;
})(##EOF##,##WHITESPACE##,##ERROR_TOKEN##);
##FOOTER##