UNPKG

jscc-parser

Version:

A LALR(1) Parser Generator for JavaScript written in JavaScript.

348 lines (336 loc) 8.48 kB
/* 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##