UNPKG

jscc-parser

Version:

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

707 lines (652 loc) 22.1 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. */ (function(root, factory) { /* istanbul ignore next */ if (typeof define === 'function' && define.amd) { define(['require', './global', './log/log', './classes/Param', './classes/Nfa', './enums/EDGE'], factory); } else if (typeof module === 'object' && module.exports) { module.exports = factory(require); } else { root.jsccregex = factory(function(mod) { return root["jscc" + mod.split("/").pop()]; }); } }(this, /** * @param {reqParameter} require * @param {...*} others * @returns {function(string, number, boolean, number)} */ function(require, others) { var first_nfa; var last_nfa; //>>excludeStart("closure", pragmas.closure); var has = /** @type {hasObject} */ (require("./localHas")); //>>excludeEnd("closure"); var log, global = /** @type {jscc.global} */ (require("./global")), Param = /** @type {function(new:jscc.classes.Param, number=, number=)} */ (require("./classes/Param")), Nfa = /** @type {function(new:jscc.classes.Nfa, ?NfaOptions=)} */ (require("./classes/Nfa")), EDGE = require("./enums/EDGE"); /** * @suppress {uselessCode} */ (function() { if (has("node")) { log = /** @type {jscc.log} */ (require("./log/logNode")); } else { log = /** @type {jscc.log} */ (require("./log/log")); } })(); 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 = ({ }) 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 = [{line:[[[[1, [[1, [[[2, 3], [4, 5]], [1, [6, 1]]]], [1, [1, [1, [1, 7]]]]]], [[1, [1, [[1, [1, 8]], [[13, 9], 1]]]], [1, [1, [1, [[10, 1], 1]]]]]], [1, [1, [1, [1, [1, [1, [1, null]]]]]]]]], accept:-1}, {line:[], accept:13}, {line:[], accept:6}, {line:[], accept:7}, {line:[], accept:3}, {line:[], accept:4}, {line:[], accept:10}, {line:[], accept:5}, {line:[], accept:8}, {line:[], accept:9}, {line:[], accept:2}, {line:[], accept:12}, {line:[[[[null, [null, [12, [[12, null], null]]]], null], null]], accept:11}, {line:[[[[11, [11, [12, [[12, 11], 11]]]], 11], [11, [11, [11, [11, [11, [11, [11, null]]]]]]]]], accept:13}]; }; /** * @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; } } }; var pop_tab =[[0,1],[15,1],[14,3],[14,1],[16,2],[16,1],[17,2],[17,2],[17,2],[17,1],[18,1],[18,1],[18,3],[20,3],[20,1],[21,2],[21,0],[19,1],[19,1],[19,1]]; /** @type {!Array<!Array<number>>} */ var act_tab =[[6,8,11,9,12,10,13,11,8,12,10,13],[],[2,14],[6,8,11,9,12,10,13,11,8,12,10,13],[],[5,16,4,17,3,18],[],[],[6,8,11,9,12,10,13,11,8,12,10,13],[],[],[],[],[],[6,8,11,9,12,10,13,11,8,12,10,13],[],[],[],[],[7,22,2,14],[9,24,11,9,12,10,13,11],[6,8,11,9,12,10,13,11,8,12,10,13],[],[],[]]; var goto_tab =[[15,1,14,2,16,3,17,4,18,5,19,6,20,7],[],[],[17,15,18,5,19,6,20,7],[],[],[],[],[14,19,16,3,17,4,18,5,19,6,20,7],[],[],[],[21,20],[],[16,21,17,4,18,5,19,6,20,7],[],[],[],[],[],[19,23],[17,15,18,5,19,6,20,7],[],[],[]]; var defact_tab =[-1,0,1,3,5,9,10,11,-1,17,18,19,16,14,-1,4,8,7,6,-1,-1,2,12,15,13]; var labels = [{"label":"RegEx'","kind":{},"prods":[0],"nullable":0,"id":0,"code":"","level":0,"special":{},"defined":true,"first":[6,11,12,13,8,10]},{"label":"ERROR_RESYNC","kind":{},"prods":[],"nullable":false,"id":1,"code":"","level":0,"special":{},"defined":true,"first":[1]},{"label":"|","kind":{},"prods":[],"nullable":false,"id":2,"code":"","level":0,"special":{},"defined":false,"first":[2]},{"label":"*","kind":{},"prods":[],"nullable":false,"id":3,"code":"","level":0,"special":{},"defined":false,"first":[3]},{"label":"+","kind":{},"prods":[],"nullable":false,"id":4,"code":"","level":0,"special":{},"defined":false,"first":[4]},{"label":"?","kind":{},"prods":[],"nullable":false,"id":5,"code":"","level":0,"special":{},"defined":false,"first":[5]},{"label":"(","kind":{},"prods":[],"nullable":false,"id":6,"code":"","level":0,"special":{},"defined":false,"first":[6]},{"label":")","kind":{},"prods":[],"nullable":false,"id":7,"code":"","level":0,"special":{},"defined":false,"first":[7]},{"label":"[","kind":{},"prods":[],"nullable":false,"id":8,"code":"","level":0,"special":{},"defined":false,"first":[8]},{"label":"]","kind":{},"prods":[],"nullable":false,"id":9,"code":"","level":0,"special":{},"defined":false,"first":[9]},{"label":"ANY_CHAR","kind":{},"prods":[],"nullable":false,"id":10,"code":"","level":0,"special":{},"defined":false,"first":[10]},{"label":"ASCII_CODE","kind":{},"prods":[],"nullable":false,"id":11,"code":"","level":0,"special":{},"defined":false,"first":[11]},{"label":"ESCAPED_CHAR","kind":{},"prods":[],"nullable":false,"id":12,"code":"","level":0,"special":{},"defined":false,"first":[12]},{"label":"ANY","kind":{},"prods":[],"nullable":false,"id":13,"code":"","level":0,"special":{},"defined":false,"first":[13]},{"label":"Expression","kind":{},"prods":[2,3],"nullable":0,"id":14,"code":"","level":0,"special":{},"defined":true,"first":[6,11,12,13,8,10]},{"label":"RegEx","kind":{},"prods":[1],"nullable":0,"id":15,"code":"","level":0,"special":{},"defined":true,"first":[6,11,12,13,8,10]},{"label":"Catenation","kind":{},"prods":[4,5],"nullable":0,"id":16,"code":"","level":0,"special":{},"defined":true,"first":[6,11,12,13,8,10]},{"label":"Factor","kind":{},"prods":[6,7,8,9],"nullable":0,"id":17,"code":"","level":0,"special":{},"defined":true,"first":[6,11,12,13,8,10]},{"label":"Term","kind":{},"prods":[10,11,12],"nullable":0,"id":18,"code":"","level":0,"special":{},"defined":true,"first":[6,11,12,13,8,10]},{"label":"Character","kind":{},"prods":[17,18,19],"nullable":0,"id":19,"code":"","level":0,"special":{},"defined":true,"first":[11,12,13]},{"label":"CharacterSet","kind":{},"prods":[13,14],"nullable":0,"id":20,"code":"","level":0,"special":{},"defined":true,"first":[8,10]},{"label":"CharClass","kind":{},"prods":[15,16],"nullable":1,"id":21,"code":"","level":0,"special":{},"defined":true,"first":[11,12,13]},{"label":"$","kind":{},"prods":[],"nullable":false,"id":22,"code":"","level":0,"special":{},"defined":false,"first":[22]}]; var ACTIONS = (function(){ var PCB = {}; var actions = [ function(){ var rval;rval = arguments[0] ; return rval;}, function(){ var rval; rval = new Param(); global.nfa_states.value[ first_nfa ].follow = arguments[0] .start; last_nfa = arguments[0] .end; return rval;}, function(){ var rval; rval = new Param(global.nfa_states.create(), global.nfa_states.create()); global.nfa_states.value[rval.start].follow = arguments[2] .start; global.nfa_states.value[rval.start].follow2 = arguments[0] .start; global.nfa_states.value[ arguments[2] .end].follow = rval.end; global.nfa_states.value[ arguments[0] .end].follow = rval.end; return rval;}, function(){ var rval;rval = arguments[0] ; return rval;}, function(){ var rval; var weight=global.nfa_states.value[ arguments[1] .end].weight;///SV: if weight unused - delete this global.nfa_states.value[ arguments[1] .end]=new Nfa(global.nfa_states.value[ arguments[0] .start]); global.nfa_states.value[ arguments[1] .end].weight=weight;///SV: if weight unused - delete this global.nfa_states.value[ arguments[0] .start].edge = EDGE.FREE; arguments[1] .end = arguments[0] .end; rval = arguments[1] ; return rval;}, function(){ var rval;rval = arguments[0] ; return rval;}, function(){ var rval; rval = new Param(global.nfa_states.create(), global.nfa_states.create()); global.nfa_states.value[rval.start].follow = arguments[1] .start; global.nfa_states.value[ arguments[1] .end].follow = rval.end; global.nfa_states.value[rval.start].follow2 = rval.end; global.nfa_states.value[ arguments[1] .end].follow2 = arguments[1] .start; return rval;}, function(){ var rval; rval = new Param(global.nfa_states.create(), global.nfa_states.create()); global.nfa_states.value[rval.start].follow = arguments[1] .start; global.nfa_states.value[ arguments[1] .end].follow = rval.end; global.nfa_states.value[ arguments[1] .end].follow2 = arguments[1] .start; return rval;}, function(){ var rval; rval = new Param(global.nfa_states.create(), global.nfa_states.create()); global.nfa_states.value[rval.start].follow = arguments[1] .start; global.nfa_states.value[rval.start].follow2 = rval.end; global.nfa_states.value[ arguments[1] .end].follow = rval.end; return rval;}, function(){ var rval;rval = arguments[0] ; return rval;}, function(){ var rval; rval = new Param(); rval.start = global.nfa_states.create(); rval.end = global.nfa_states.value[rval.start].follow = global.nfa_states.create(); global.nfa_states.value[rval.start].edge = EDGE.CHAR; global.nfa_states.value[rval.start].ccl.set( arguments[0] .charCodeAt( 0 ), true ); return rval;}, function(){ var rval;rval = arguments[0] ; return rval;}, function(){ var rval; rval = arguments[1] ; return rval;}, function(){ var rval; var negate = false; var i = 0, j, start; rval = new Param(); rval.start = global.nfa_states.create(); rval.end = global.nfa_states.value[rval.start].follow = global.nfa_states.create(); global.nfa_states.value[rval.start].edge = EDGE.CHAR; if( arguments[1] .charAt( i ) == '^' ){ negate = true; for( j = global.MIN_CHAR; j < global.MAX_CHAR; j++ ) global.nfa_states.value[rval.start].ccl.set(j,true); i++; } for( ; i < arguments[1] .length; i++ ){ if( arguments[1] .charAt( i+1 ) == '-' && i+2 < arguments[1] .length ){ i++; for( j = arguments[1] .charCodeAt( i-1 ); j < arguments[1] .charCodeAt( i+1 ); j++ ) global.nfa_states.value[rval.start].ccl.set(j, !negate); } else global.nfa_states.value[rval.start].ccl.set( arguments[1] .charCodeAt(i), !negate); } return rval;}, function(){ var rval; rval = new Param(); rval.start = global.nfa_states.create(); rval.end = global.nfa_states.value[rval.start].follow = global.nfa_states.create(); global.nfa_states.value[rval.start].edge = EDGE.CHAR; for( var i = global.MIN_CHAR; i < global.MAX_CHAR; i++ ) global.nfa_states.value[rval.start].ccl.set(i, true); return rval;}, function(){ var rval; rval = arguments[1] + arguments[0] ; return rval;}, function(){ var rval; rval = ""; return rval;}, function(){ var rval; rval = String.fromCharCode( arguments[0] .substr( 1 ) ); return rval;}, function(){ var rval; rval = {n:'\n',r:'\r',t:'\t',a:'\a'}[ arguments[0] .substr(1)]|| arguments[0] .substr(1); return rval;}, function(){ var rval; rval = arguments[0] ; return rval;}, ]; 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; })(22,-1,1); /** * Compiles the given regex into a nondeterministic finite automata. * @param {string} str - The regex to compile. * @param {number} accept - The id of the symbol accepted. * @param {boolean} case_insensitive - Whether the regex is case insensitive. * @param {number} cur_line - The current line number being parsed. Used in error * logging. * @module {jscc.regex} jscc/regex * @requires module:jscc/global * @requires module:jscc/log/log */ function compile_regex( str, accept, case_insensitive, cur_line ){ var i, j; var weight = 0; var true_edges = 0; var error_offsets = []; var error_expects = []; var error_count = 0; if( str == "" ) return; cur_line = cur_line || 0; //_print( "str = >" + str + "< " + case_insensitive ); first_nfa = global.nfa_states.create(); if( ( error_count = __parse( str, error_offsets, error_expects ) ) == 0 ){ //If the symbol should be case-insensitive, manipulate the //character sets on the newly created items. if( case_insensitive ){ for( i = 0; i < global.nfa_states.value.length; i++ ){ if( global.nfa_states.value[i].edge == EDGE.CHAR ){ for( j = global.MIN_CHAR; j < global.MAX_CHAR; j++ ){ if( global.nfa_states.value[i].ccl.get( j ) ){ global.nfa_states.value[i].ccl.set(String.fromCharCode( j ).toUpperCase().charCodeAt( 0 ), true ); global.nfa_states.value[i].ccl.set(String.fromCharCode( j ).toLowerCase().charCodeAt( 0 ), true ); } } } } } /* 2008-5-9 Radim Cebis: I think that computing weight of the nfa_states.value is weird, IMHO nfa_state which accepts a symbol, should have weight according to the order... */ global.nfa_states.value[ last_nfa ].accept = accept; global.nfa_states.value[ last_nfa ].weight = global.regex_weight++; if( first_nfa > 0 ){ i = 0; while( global.nfa_states.value[i].follow2 != -1 ) i = global.nfa_states.value[i].follow2; global.nfa_states.value[i].follow2 = first_nfa; } }else{ for( i = 0; i < error_count; i++ ){ var spaces = ''; for( j = 0; j < error_offsets[i]; j++ ) spaces += " "; log.error( "Regular expression:\n\t" + str + "\n\t" + spaces + "^ expecting " + error_expects[i].join() + " on line " + cur_line ); } } } return compile_regex; //TESTING AREA ;) //compile_regex( "[A-Z][A-Z0-9]*", 0 ); //compile_regex( "ab|c", 1 ); //compile_regex( "[0-9]+", 1 ); //print_nfa(); //var d = create_subset( nfa_states.value ); //print_dfa( d ); //d = minimize_dfa( d ); //print_dfa( d ); }));