UNPKG

@apistudio/apim-cli

Version:

CLI for API Management Products

1,479 lines (1,319 loc) 193 kB
/* * Copyright (c) 2009-2014 Zachary Carter * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR * A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH * THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ var require = (function () { var require = (function () { var modules = {}; var factories = {}; var r = function (id) { if (!modules[id]) { //console.log(id); modules[id] = {}; factories[id](r, modules[id], { id: id }); } return modules[id]; }; r.def = function (id, params) { //console.log('def', id); factories[id] = params.factory; }; return r; })(); require.def("jison", { factory: function (require, exports, module) { // Jison, an LR(0), SLR(1), LARL(1), LR(1) Parser Generator // Zachary Carter <zach@carter.name> // MIT X Licensed var typal = require("jison/util/typal").typal, Set = require("jison/util/set").Set, RegExpLexer = require("jison/lexer").RegExpLexer; var Jison = (exports.Jison = exports); // detect prints Jison.print = function () {}; /* if (typeof console !== 'undefined' && console.log) { Jison.print = console.log; Jison.print = function print () {}; } else if (typeof puts !== 'undefined') { Jison.print = function print () { puts([].join.call(arguments, ' ')); }; } else if (typeof print !== 'undefined') { Jison.print = print; } else { Jison.print = function print () {}; } */ Jison.Parser = (function () { // iterator utility function each(obj, func) { if (obj.forEach) { obj.forEach(func); } else { var p; for (p in obj) { if (obj.hasOwnProperty(p)) { func.call(obj, obj[p], p, obj); } } } } var Nonterminal = typal.construct({ constructor: function Nonterminal(symbol) { this.symbol = symbol; this.productions = new Set(); this.first = []; this.follows = []; this.nullable = false; }, toString: function Nonterminal_toString() { var str = this.symbol + "\n"; str += this.nullable ? "nullable" : "not nullable"; str += "\nFirsts: " + this.first.join(", "); str += "\nFollows: " + this.first.join(", "); str += "\nProductions:\n " + this.productions.join("\n "); return str; }, }); var Production = typal.construct({ constructor: function Production(symbol, handle, id) { this.symbol = symbol; this.handle = handle; this.nullable = false; this.id = id; this.first = []; this.precedence = 0; }, toString: function Production_toString() { return this.symbol + " -> " + this.handle.join(" "); }, }); var generator = typal.beget(); generator.constructor = function Jison_Generator(grammar, opt) { if (typeof grammar === "string") { grammar = require("jison/bnf").parse(grammar); } var options = typal.mix.call({}, grammar.options, opt); this.terms = {}; this.operators = {}; this.productions = []; this.conflicts = 0; this.resolutions = []; this.options = options; this.yy = {}; // accessed as yy free variable in the parser/lexer actions // source included in semantic action execution scope if (grammar.actionInclude) { if (typeof grammar.actionInclude === "function") { grammar.actionInclude = String(grammar.actionInclude) .replace(/^\s*function \(\) \{/, "") .replace(/\}\s*$/, ""); } this.actionInclude = grammar.actionInclude; } this.moduleInclude = grammar.moduleInclude || ""; this.DEBUG = options.debug || false; if (this.DEBUG) this.mix(generatorDebug); // mixin debug methods this.processGrammar(grammar); if (grammar.lex) { this.lexer = new RegExpLexer(grammar.lex, null, this.terminals_); } }; generator.processGrammar = function processGrammarDef(grammar) { var bnf = grammar.bnf, tokens = grammar.tokens, nonterminals = (this.nonterminals = {}), productions = this.productions, self = this; if (!grammar.bnf && grammar.ebnf) { bnf = grammar.bnf = require("jison/ebnf").transform(grammar.ebnf); } if (tokens) { if (typeof tokens === "string") { tokens = tokens.trim().split(" "); } else { tokens = tokens.slice(0); } } var symbols = (this.symbols = []); // calculate precedence of operators var operators = (this.operators = processOperators( grammar.operators, )); // build productions from cfg this.buildProductions( grammar.bnf, productions, nonterminals, symbols, operators, ); if (tokens && this.terminals.length !== tokens.length) { self.trace( "Warning: declared tokens differ from tokens found in rules.", ); self.trace(this.terminals); self.trace(tokens); } // augment the grammar this.augmentGrammar(grammar); }; generator.augmentGrammar = function augmentGrammar(grammar) { // use specified start symbol, or default to first user defined production this.startSymbol = grammar.start || grammar.startSymbol || this.productions[0].symbol; if (!this.nonterminals[this.startSymbol]) { throw new Error( "Grammar error: startSymbol must be a non-terminal found in your grammar.", ); } this.EOF = "$end"; // augment the grammar var acceptProduction = new Production( "$accept", [this.startSymbol, "$end"], 0, ); this.productions.unshift(acceptProduction); // prepend parser tokens this.symbols.unshift("$accept", this.EOF); this.symbols_.$accept = 0; this.symbols_[this.EOF] = 1; this.terminals.unshift(this.EOF); this.nonterminals.$accept = new Nonterminal("$accept"); this.nonterminals.$accept.productions.push(acceptProduction); // add follow $ to start symbol this.nonterminals[this.startSymbol].follows.push(this.EOF); }; // set precedence and associativity of operators function processOperators(ops) { if (!ops) return {}; var operators = {}; for (var i = 0, k, prec; (prec = ops[i]); i++) { for (k = 1; k < prec.length; k++) { operators[prec[k]] = { precedence: i + 1, assoc: prec[0] }; } } return operators; } generator.buildProductions = function buildProductions( bnf, productions, nonterminals, symbols, operators, ) { var actions = [ this.actionInclude || "", "var $0 = $$.length - 1;", "switch (yystate) {", ]; var prods, symbol; var productions_ = [0]; var symbolId = 1; var symbols_ = {}; var her = false; // has error recovery function addSymbol(s) { if (s && !symbols_[s]) { symbols_[s] = ++symbolId; symbols.push(s); } } // add error symbol; will be third symbol, or "2" ($accept, $end, error) addSymbol("error"); for (symbol in bnf) { if (!bnf.hasOwnProperty(symbol)) continue; addSymbol(symbol); nonterminals[symbol] = new Nonterminal(symbol); if (typeof bnf[symbol] === "string") { prods = bnf[symbol].split(/\s*\|\s*/g); } else { prods = bnf[symbol].slice(0); } prods.forEach(buildProduction); } var sym, terms = [], terms_ = {}; each(symbols_, function (id, sym) { if (!nonterminals[sym]) { terms.push(sym); terms_[id] = sym; } }); this.hasErrorRecovery = her; this.terminals = terms; this.terminals_ = terms_; this.symbols_ = symbols_; this.productions_ = productions_; actions.push("}"); this.performAction = Function( "yytext,yyleng,yylineno,yy,yystate,$$,_$", actions.join("\n"), ); function buildProduction(handle) { var r, rhs, i; if (handle.constructor === Array) { rhs = typeof handle[0] === "string" ? handle[0].trim().split(" ") : handle[0].slice(0); for (i = 0; i < rhs.length; i++) { if (rhs[i] === "error") her = true; if (!symbols_[rhs[i]]) { addSymbol(rhs[i]); } } if (typeof handle[1] === "string" || handle.length == 3) { // semantic action specified var action = "case " + (productions.length + 1) + ":" + handle[1] + "\nbreak;"; // replace named semantic values ($nonterminal) if (action.match(/[$@][a-zA-Z][a-zA-Z0-9_]*/)) { var count = {}, names = {}; for (i = 0; i < rhs.length; i++) { if (names[rhs[i]]) { names[rhs[i] + ++count[rhs[i]]] = i + 1; } else { names[rhs[i]] = i + 1; names[rhs[i] + "1"] = i + 1; count[rhs[i]] = 1; } } action = action .replace(/\$([a-zA-Z][a-zA-Z0-9_]*)/g, function (str, pl) { return names[pl] ? "$" + names[pl] : pl; }) .replace(/@([a-zA-Z][a-zA-Z0-9_]*)/g, function (str, pl) { return names[pl] ? "@" + names[pl] : pl; }); } action = action .replace(/([^'"])\$\$|^\$\$/g, "$1this.$") .replace(/@[0$]/g, "this._$") .replace(/\$(\d+)/g, function (_, n) { return "$$[$0" + (n - rhs.length || "") + "]"; }) .replace(/@(\d+)/g, function (_, n) { return "_$[$0" + (n - rhs.length || "") + "]"; }); actions.push(action); r = new Production(symbol, rhs, productions.length + 1); // precedence specified also if (handle[2] && operators[handle[2].prec]) { r.precedence = operators[handle[2].prec].precedence; } } else { // only precedence specified r = new Production(symbol, rhs, productions.length + 1); if (operators[handle[1].prec]) { r.precedence = operators[handle[1].prec].precedence; } } } else { rhs = handle.trim().split(" "); for (i = 0; i < rhs.length; i++) { if (rhs[i] === "error") her = true; if (!symbols_[rhs[i]]) { addSymbol(rhs[i]); } } r = new Production(symbol, rhs, productions.length + 1); } if (r.precedence === 0) { // set precedence for (i = r.handle.length - 1; i >= 0; i--) { if ( !(r.handle[i] in nonterminals) && r.handle[i] in operators ) { r.precedence = operators[r.handle[i]].precedence; } } } productions.push(r); productions_.push([ symbols_[r.symbol], r.handle[0] === "" ? 0 : r.handle.length, ]); nonterminals[symbol].productions.push(r); } }; generator.createParser = function createParser() { throw new Error("Calling abstract method."); }; // noop. implemented in debug mixin generator.trace = function trace() {}; generator.warn = function warn() { var args = Array.prototype.slice.call(arguments, 0); console.warn("Jison Warning", args); // Jison.print.call(null,args.join("")); }; generator.error = function error(msg) { throw new Error(msg); }; // Generator debug mixin var generatorDebug = { trace: function trace() { Jison.print.apply(null, arguments); }, beforeprocessGrammar: function () { this.trace("Processing grammar."); }, afteraugmentGrammar: function () { var trace = this.trace; each(this.symbols, function (sym, i) { trace(sym + "(" + i + ")"); }); }, }; /* * Mixin for common behaviors of lookahead parsers * */ var lookaheadMixin = {}; lookaheadMixin.computeLookaheads = function computeLookaheads() { if (this.DEBUG) this.mix(lookaheadDebug); // mixin debug methods this.computeLookaheads = function () {}; this.nullableSets(); this.firstSets(); this.followSets(); }; // calculate follow sets typald on first and nullable lookaheadMixin.followSets = function followSets() { var productions = this.productions, nonterminals = this.nonterminals, self = this, cont = true; // loop until no further changes have been made while (cont) { cont = false; productions.forEach(function Follow_prod_forEach(production, k) { //self.trace(production.symbol,nonterminals[production.symbol].follows); // q is used in Simple LALR algorithm determine follows in context var q; var ctx = !!self.go_; var set = [], oldcount; for (var i = 0, t; (t = production.handle[i]); ++i) { if (!nonterminals[t]) continue; // for Simple LALR algorithm, self.go_ checks if if (ctx) q = self.go_( production.symbol, production.handle.slice(0, i), ); var bool = !ctx || q === parseInt(self.nterms_[t], 10); if (i === production.handle.length + 1 && bool) { set = nonterminals[production.symbol].follows; } else { var part = production.handle.slice(i + 1); set = self.first(part); if (self.nullable(part) && bool) { set.push.apply( set, nonterminals[production.symbol].follows, ); } } oldcount = nonterminals[t].follows.length; Set.union(nonterminals[t].follows, set); if (oldcount !== nonterminals[t].follows.length) { cont = true; } } }); } }; // return the FIRST set of a symbol or series of symbols lookaheadMixin.first = function first(symbol) { // epsilon if (symbol === "") { return []; // RHS } else if (symbol instanceof Array) { var firsts = []; for (var i = 0, t; (t = symbol[i]); ++i) { if (!this.nonterminals[t]) { if (firsts.indexOf(t) === -1) firsts.push(t); } else { Set.union(firsts, this.nonterminals[t].first); } if (!this.nullable(t)) break; } return firsts; // terminal } else if (!this.nonterminals[symbol]) { return [symbol]; // nonterminal } else { return this.nonterminals[symbol].first; } }; // fixed-point calculation of FIRST sets lookaheadMixin.firstSets = function firstSets() { var productions = this.productions, nonterminals = this.nonterminals, self = this, cont = true, symbol, firsts; // loop until no further changes have been made while (cont) { cont = false; productions.forEach(function FirstSets_forEach(production, k) { var firsts = self.first(production.handle); if (firsts.length !== production.first.length) { production.first = firsts; cont = true; } }); for (symbol in nonterminals) { firsts = []; nonterminals[symbol].productions.forEach(function (production) { Set.union(firsts, production.first); }); if (firsts.length !== nonterminals[symbol].first.length) { nonterminals[symbol].first = firsts; cont = true; } } } }; // fixed-point calculation of NULLABLE lookaheadMixin.nullableSets = function nullableSets() { var firsts = (this.firsts = {}), nonterminals = this.nonterminals, self = this, cont = true; // loop until no further changes have been made while (cont) { cont = false; // check if each production is nullable this.productions.forEach(function (production, k) { if (!production.nullable) { for (var i = 0, n = 0, t; (t = production.handle[i]); ++i) { if (self.nullable(t)) n++; } if (n === i) { // production is nullable if all tokens are nullable production.nullable = cont = true; } } }); //check if each symbol is nullable for (var symbol in nonterminals) { if (!this.nullable(symbol)) { for ( var i = 0, production; (production = nonterminals[symbol].productions.item(i)); i++ ) { if (production.nullable) nonterminals[symbol].nullable = cont = true; } } } } }; // check if a token or series of tokens is nullable lookaheadMixin.nullable = function nullable(symbol) { // epsilon if (symbol === "") { return true; // RHS } else if (symbol instanceof Array) { for (var i = 0, t; (t = symbol[i]); ++i) { if (!this.nullable(t)) return false; } return true; // terminal } else if (!this.nonterminals[symbol]) { return false; // nonterminal } else { return this.nonterminals[symbol].nullable; } }; // lookahead debug mixin var lookaheadDebug = { beforenullableSets: function () { this.trace("Computing Nullable sets."); }, beforefirstSets: function () { this.trace("Computing First sets."); }, beforefollowSets: function () { this.trace("Computing Follow sets."); }, afterfollowSets: function () { var trace = this.trace; each(this.nonterminals, function (nt, t) { trace(nt, "\n"); }); }, }; /* * Mixin for common LR parser behavior * */ var lrGeneratorMixin = {}; lrGeneratorMixin.buildTable = function buildTable() { if (this.DEBUG) this.mix(lrGeneratorDebug); // mixin debug methods this.states = this.canonicalCollection(); this.table = this.parseTable(this.states); this.defaultActions = findDefaults(this.table); }; lrGeneratorMixin.Item = typal.construct({ constructor: function Item(production, dot, f, predecessor) { this.production = production; this.dotPosition = dot || 0; this.follows = f || []; this.predecessor = predecessor; this.id = parseInt(production.id + "a" + this.dotPosition, 36); this.markedSymbol = this.production.handle[this.dotPosition]; }, remainingHandle: function () { return this.production.handle.slice(this.dotPosition + 1); }, eq: function (e) { return e.id === this.id; }, handleToString: function () { var handle = this.production.handle.slice(0); handle[this.dotPosition] = "." + (handle[this.dotPosition] || ""); return handle.join(" "); }, toString: function () { var temp = this.production.handle.slice(0); temp[this.dotPosition] = "." + (temp[this.dotPosition] || ""); return ( this.production.symbol + " -> " + temp.join(" ") + (this.follows.length === 0 ? "" : " #lookaheads= " + this.follows.join(" ")) ); }, }); lrGeneratorMixin.ItemSet = Set.prototype.construct({ afterconstructor: function () { this.reductions = []; this.goes = {}; this.edges = {}; this.shifts = false; this.inadequate = false; this.hash_ = {}; for (var i = this._items.length - 1; i >= 0; i--) { this.hash_[this._items[i].id] = true; //i; } }, concat: function concat(set) { var a = set._items || set; for (var i = a.length - 1; i >= 0; i--) { this.hash_[a[i].id] = true; //i; } this._items.push.apply(this._items, a); return this; }, push: function (item) { this.hash_[item.id] = true; return this._items.push(item); }, contains: function (item) { return this.hash_[item.id]; }, valueOf: function toValue() { var v = this._items .map(function (a) { return a.id; }) .sort() .join("|"); this.valueOf = function toValue_inner() { return v; }; return v; }, }); lrGeneratorMixin.closureOperation = function closureOperation( itemSet /*, closureSet*/, ) { var closureSet = new this.ItemSet(); var self = this; var set = itemSet, itemQueue, syms = {}; do { itemQueue = new Set(); closureSet.concat(set); set.forEach(function CO_set_forEach(item) { var symbol = item.markedSymbol; // if token is a non-terminal, recursively add closures if (symbol && self.nonterminals[symbol]) { if (!syms[symbol]) { self.nonterminals[symbol].productions.forEach( function CO_nt_forEach(production) { var newItem = new self.Item(production, 0); if (!closureSet.contains(newItem)) itemQueue.push(newItem); }, ); syms[symbol] = true; } } else if (!symbol) { // reduction closureSet.reductions.push(item); closureSet.inadequate = closureSet.reductions.length > 1 || closureSet.shifts; } else { // shift closureSet.shifts = true; closureSet.inadequate = closureSet.reductions.length > 0; } }); set = itemQueue; } while (!itemQueue.isEmpty()); return closureSet; }; lrGeneratorMixin.gotoOperation = function gotoOperation( itemSet, symbol, ) { var gotoSet = new this.ItemSet(), self = this; itemSet.forEach(function goto_forEach(item, n) { if (item.markedSymbol === symbol) { gotoSet.push( new self.Item( item.production, item.dotPosition + 1, item.follows, n, ), ); } }); return gotoSet.isEmpty() ? gotoSet : this.closureOperation(gotoSet); }; /* Create unique set of item sets * */ lrGeneratorMixin.canonicalCollection = function canonicalCollection() { var item1 = new this.Item(this.productions[0], 0, [this.EOF]); var firstState = this.closureOperation(new this.ItemSet(item1)), states = new Set(firstState), marked = 0, self = this, itemSet; states.has = {}; states.has[firstState] = 0; while (marked !== states.size()) { itemSet = states.item(marked); marked++; itemSet.forEach(function CC_itemSet_forEach(item) { if (item.markedSymbol && item.markedSymbol !== self.EOF) self.canonicalCollectionInsert( item.markedSymbol, itemSet, states, marked - 1, ); }); } return states; }; // Pushes a unique state into the que. Some parsing algorithms may perform additional operations lrGeneratorMixin.canonicalCollectionInsert = function canonicalCollectionInsert( symbol, itemSet, states, stateNum, ) { var g = this.gotoOperation(itemSet, symbol); if (!g.predecessors) g.predecessors = {}; // add g to que if not empty or duplicate if (!g.isEmpty()) { var gv = g.valueOf(), i = states.has[gv]; if (i === -1 || typeof i === "undefined") { states.has[gv] = states.size(); itemSet.edges[symbol] = states.size(); // store goto transition for table states.push(g); g.predecessors[symbol] = [stateNum]; } else { itemSet.edges[symbol] = i; // store goto transition for table states.item(i).predecessors[symbol].push(stateNum); } } }; var NONASSOC = 0; lrGeneratorMixin.parseTable = function parseTable(itemSets) { var NONASSOC = 0; var states = [], nonterminals = this.nonterminals, operators = this.operators, conflictedStates = {}, // array of [state, token] tuples self = this, s = 1, // shift r = 2, // reduce a = 3; // accept // for each item set itemSets.forEach(function (itemSet, k) { var state = (states[k] = {}); var action, stackSymbol; // set shift and goto actions for (stackSymbol in itemSet.edges) { itemSet.forEach(function (item, j) { // find shift and goto actions if (item.markedSymbol == stackSymbol) { var gotoState = itemSet.edges[stackSymbol]; if (nonterminals[stackSymbol]) { // store state to go to after a reduce //self.trace(k, stackSymbol, 'g'+gotoState); state[self.symbols_[stackSymbol]] = gotoState; } else { //self.trace(k, stackSymbol, 's'+gotoState); state[self.symbols_[stackSymbol]] = [s, gotoState]; } } }); } // set accept action itemSet.forEach(function (item, j) { if (item.markedSymbol == self.EOF) { // accept state[self.symbols_[self.EOF]] = [a]; //self.trace(k, self.EOF, state[self.EOF]); } }); var allterms = self.lookAheads ? false : self.terminals; // set reductions and resolve potential conflicts itemSet.reductions.forEach(function (item, j) { // if parser uses lookahead, only enumerate those terminals var terminals = allterms || self.lookAheads(itemSet, item); terminals.forEach(function (stackSymbol) { action = state[self.symbols_[stackSymbol]]; var op = operators[stackSymbol]; // Reading a terminal and current position is at the end of a production, try to reduce if (action || (action && action.length)) { var sol = resolveConflict( item.production, op, [r, item.production.id], action[0] instanceof Array ? action[0] : action, ); self.resolutions.push([k, stackSymbol, sol]); if (sol.bydefault) { self.conflicts++; if (!self.DEBUG) { self.warn( "Conflict in grammar: multiple actions possible when lookahead token is ", stackSymbol, " in state ", k, "\n- ", printAction(sol.r, self), "\n- ", printAction(sol.s, self), ); conflictedStates[k] = true; } if (self.options.noDefaultResolve) { if (!(action[0] instanceof Array)) action = [action]; action.push(sol.r); } } else { action = sol.action; } } else { action = [r, item.production.id]; } if (action && action.length) { state[self.symbols_[stackSymbol]] = action; } else if (action === NONASSOC) { state[self.symbols_[stackSymbol]] = undefined; } }); }); }); if (!self.DEBUG && self.conflicts > 0) { self.warn("\nStates with conflicts:"); each(conflictedStates, function (val, state) { self.warn("State " + state); self.warn(" ", itemSets.item(state).join("\n ")); }); } return states; }; // find states with only one action, a reduction function findDefaults(states) { var defaults = {}; states.forEach(function (state, k) { var i = 0; for (var act in state) { if ({}.hasOwnProperty.call(state, act)) i++; } if (i === 1 && state[act][0] === 2) { // only one action in state and it's a reduction defaults[k] = state[act]; } }); return defaults; } // resolves shift-reduce and reduce-reduce conflicts function resolveConflict(production, op, reduce, shift) { var sln = { production: production, operator: op, r: reduce, s: shift, }, s = 1, // shift r = 2, // reduce a = 3; // accept if (shift[0] === r) { sln.msg = "Resolve R/R conflict (use first production declared in grammar.)"; sln.action = shift[1] < reduce[1] ? shift : reduce; if (shift[1] !== reduce[1]) sln.bydefault = true; return sln; } if (production.precedence === 0 || !op) { sln.msg = "Resolve S/R conflict (shift by default.)"; sln.bydefault = true; sln.action = shift; } else if (production.precedence < op.precedence) { sln.msg = "Resolve S/R conflict (shift for higher precedent operator.)"; sln.action = shift; } else if (production.precedence === op.precedence) { if (op.assoc === "right") { sln.msg = "Resolve S/R conflict (shift for right associative operator.)"; sln.action = shift; } else if (op.assoc === "left") { sln.msg = "Resolve S/R conflict (reduce for left associative operator.)"; sln.action = reduce; } else if (op.assoc === "nonassoc") { sln.msg = "Resolve S/R conflict (no action for non-associative operator.)"; sln.action = NONASSOC; } } else { sln.msg = "Resolve conflict (reduce for higher precedent production.)"; sln.action = reduce; } return sln; } lrGeneratorMixin.generate = function parser_generate(opt) { opt = typal.mix.call({}, this.options, opt); var code = ""; // check for illegal identifier if ( !opt.moduleName || !opt.moduleName.match(/^[A-Za-z_$][A-Za-z0-9_$]*$/) ) { opt.moduleName = "parser"; } switch (opt.moduleType) { case "js": code = this.generateModule(opt); break; case "amd": code = this.generateAMDModule(opt); break; case "esm": code = this.generateESModule(opt); break; default: code = this.generateCommonJSModule(opt); } return code; }; lrGeneratorMixin.generateAMDModule = function generateAMDModule(opt) { opt = typal.mix.call({}, this.options, opt); var out = "define([], function(){" + "\nvar parser = " + this.generateModule_(opt) + (this.lexer && this.lexer.generateModule ? "\n" + this.lexer.generateModule() + "\nparser.lexer = lexer;" : "") + "\nreturn parser;" + "\n});"; return out; }; lrGeneratorMixin.generateCommonJSModule = function generateCommonJSModule(opt) { opt = typal.mix.call({}, this.options, opt); var moduleName = opt.moduleName || "parser"; var out = this.generateModule(opt) + "\nif (typeof require !== 'undefined' && typeof exports !== 'undefined') {" + "\nexports.parser = " + moduleName + ";" + "\nexports.Parser = " + moduleName + ".Parser;" + "\nexports.parse = function () { return " + moduleName + ".parse.apply(" + moduleName + ", arguments); }" + "\n}"; return out; }; lrGeneratorMixin.generateESModule = function generateESModule(opt) { opt = typal.mix.call({}, this.options, opt); if (opt.moduleName === "parser") { opt.moduleName = "_parser"; } var out = this.generateModule(opt) + "\nexport const parser = " + opt.moduleName + ";" + "\nexport const Parser = " + opt.moduleName + ".Parser;" + "\nexport function parse () { return " + opt.moduleName + ".parse.apply(" + opt.moduleName + ", arguments); }"; return out; }; lrGeneratorMixin.generateModule = function generateModule(opt) { opt = typal.mix.call({}, this.options, opt); var moduleName = opt.moduleName || "parser"; var out = "/* Jison generated parser */\n"; out += (moduleName.match(/\./) ? moduleName : "var " + moduleName) + " = (function(){"; out += "\nvar parser = " + this.generateModule_(); out += "\n" + this.moduleInclude; if (this.lexer && this.lexer.generateModule) { out += this.lexer.generateModule(); out += "\nparser.lexer = lexer;"; } out += "\nfunction Parser () { this.yy = {}; }" + "Parser.prototype = parser;" + "parser.Parser = Parser;" + "\nreturn new Parser;\n})();"; return out; }; // returns parse function without error recovery code function removeErrorRecovery(fn) { var parseFn = String(fn); try { var JSONSelect = require("JSONSelect"); var Reflect = require("reflect"); var ast = Reflect.parse(parseFn); var labeled = JSONSelect.match( ':has(:root > .label > .name:val("_handle_error"))', ast, ); labeled[0].body.consequent.body = [ labeled[0].body.consequent.body[0], labeled[0].body.consequent.body[1], ]; return Reflect.stringify(ast) .replace(/_handle_error:\s?/, "") .replace(/\\\\n/g, "\\n"); } catch (e) { return parseFn; } } lrGeneratorMixin.generateModule_ = function generateModule_() { var parseFn = (this.hasErrorRecovery ? String : removeErrorRecovery)( parser.parse, ); var out = "{"; out += [ "trace: " + String(this.trace || parser.trace), "yy: {}", "symbols_: " + JSON.stringify(this.symbols_), "terminals_: " + JSON.stringify(this.terminals_).replace(/"([0-9]+)":/g, "$1:"), "productions_: " + JSON.stringify(this.productions_), "performAction: " + String(this.performAction), "table: " + JSON.stringify(this.table).replace(/"([0-9]+)":/g, "$1:"), "defaultActions: " + JSON.stringify(this.defaultActions).replace( /"([0-9]+)":/g, "$1:", ), "parseError: " + String( this.parseError || (this.hasErrorRecovery ? traceParseError : parser.parseError), ), "parse: " + parseFn, ].join(",\n"); out += "};"; return out; }; // default main method for generated commonjs modules function commonjsMain(args) { if (!args[1]) throw new Error("Usage: " + args[0] + " FILE"); var source, cwd; if (typeof process !== "undefined") { source = require("fs").readFileSync( require("path").resolve(args[1]), "utf8", ); } else { source = require("file") .path(require("file").cwd()) .join(args[1]) .read({ charset: "utf-8" }); } return exports.parser.parse(source); } // debug mixin for LR parser generators function printAction(a, gen) { var s = a[0] == 1 ? "shift token (then go to state " + a[1] + ")" : a[0] == 2 ? "reduce by rule: " + gen.productions[a[1]] : "accept"; return s; } var lrGeneratorDebug = { beforeparseTable: function () { this.trace("Building parse table."); }, afterparseTable: function () { var self = this; if (this.conflicts > 0) { this.resolutions.forEach(function (r, i) { if (r[2].bydefault) { self.warn( "Conflict at state: ", r[0], ", token: ", r[1], "\n ", printAction(r[2].r, self), "\n ", printAction(r[2].s, self), ); } }); this.trace( "\n" + this.conflicts + " Conflict(s) found in grammar.", ); } this.trace("Done."); }, aftercanonicalCollection: function (states) { var trace = this.trace; trace("\nItem sets\n------"); states.forEach(function (state, i) { trace( "\nitem set", i, "\n" + state.join("\n"), "\ntransitions -> ", JSON.stringify(state.edges), ); }); }, }; var parser = typal.beget(); lrGeneratorMixin.createParser = function createParser() { var p = parser.beget(); p.yy = {}; p.init({ table: this.table, defaultActions: this.defaultActions, productions_: this.productions_, symbols_: this.symbols_, terminals_: this.terminals_, performAction: this.performAction, }); // don't throw if grammar recovers from errors if (this.hasErrorRecovery) { p.parseError = traceParseError; p.recover = true; } // for debugging p.productions = this.productions; // backwards compatability p.generate = this.generate; p.lexer = this.lexer; p.generateModule = this.generateModule; p.generateCommonJSModule = this.generateCommonJSModule; p.generateESModule = this.generateESModule; p.generateModule_ = this.generateModule_; var gen = this; p.Parser = function () { return gen.createParser(); }; return p; }; parser.trace = generator.trace; parser.warn = generator.warn; parser.error = generator.error; function traceParseError(err, hash) { this.trace(err); } parser.parseError = lrGeneratorMixin.parseError = function parseError( str, hash, ) { throw new Error(str); }; parser.parse = function parse(input) { var self = this, stack = [0], vstack = [null], // semantic value stack lstack = [], // location stack table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; //this.reductionCount = this.shiftCount = 0; this.lexer.setInput(input); this.lexer.yy = this.yy; this.yy.lexer = this.lexer; this.yy.parser = this; if (typeof this.lexer.yylloc == "undefined") this.lexer.yylloc = {}; var yyloc = this.lexer.yylloc; lstack.push(yyloc); var ranges = this.lexer.options && this.lexer.options.ranges; if (typeof this.yy.parseError === "function") this.parseError = this.yy.parseError; function popStack(n) { stack.length = stack.length - 2 * n; vstack.length = vstack.length - n; lstack.length = lstack.length - n; } function lex() { var token; token = self.lexer.lex() || 1; // $end = 1 // if token isn't its numeric value, convert if (typeof token !== "number") { token = self.symbols_[token] || token; } return token; } var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; while (true) { // retreive state number from top of stack state = stack[stack.length - 1]; // use default actions if available if (this.defaultActions[state]) { action = this.defaultActions[state]; } else { if (symbol === null || typeof symbol == "undefined") { symbol = lex(); } // read action for current state and first input action = table[state] && table[state][symbol]; } // handle parse error _handle_error: if ( typeof action === "undefined" || !action.length || !action[0] ) { var errStr = ""; if (!recovering) { // Report error expected = []; for (p in table[state]) if (this.terminals_[p] && p > 2) { expected.push("'" + this.terminals_[p] + "'"); } if (this.lexer.showPosition) { errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; } else { errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1 /*EOF*/ ? "end of input" : "'" + (this.terminals_[symbol] || symbol) + "'"); } this.parseError(errStr, { text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected, }); } // just recovered from another error if (recovering == 3) { if (symbol == EOF) { throw new Error(errStr || "Parsing halted."); } // discard current lookahead and grab another yyleng = this.lexer.yyleng; yytext = this.lexer.yytext; yylineno = this.lexer.yylineno; yyloc = this.lexer.yylloc; symbol = lex(); } // try to recover from error while (1) { // check for error recovery rule in this state if (TERROR.toString() in table[state]) { break; } if (state === 0) { throw new Error(errStr || "Parsing halted."); } popStack(1); state = stack[stack.length - 1]; } preErrorSymbol = symbol == 2 ? null : symbol; // save the lookahead tok