@apistudio/apim-cli
Version:
CLI for API Management Products
1,479 lines (1,319 loc) • 193 kB
JavaScript
/*
* 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