@light0x00/parser-generator
Version:
A Parser Generator
242 lines (205 loc) • 26.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getParsingTable = getParsingTable;
var _shim = require("@light0x00/shim");
var _parserDefinition = require("@light0x00/parser-definition");
var _Definition = require("../Definition");
var _Definition2 = require("./Definition");
var _debug = _interopRequireDefault(require("debug"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
let debug = (0, _debug.default)("APP:LR:parsing-table");
/*
输入: 一个由「项集族」和「项集间跳转关系」组成的状态机
输出: LR/SLR分析表
注: 用 stateA.next_state(input_symbol) 表示状态A输入符号后进入的下一个状态
遍历每一个状态S中的每一个项I
- I为Reduce/Accpet项,形如 A->𝜶𝜷·
- 若A是不是开始符号,则为Follow(T)中的每一个符号f填入: T[S][f] = Reduce A
- 若A是开始符号,操作同上,不过填入得时 Accept A (本质上Accept项是一个特殊的归约项)
- I为Shift/Goto, A->𝜶·𝜷
- 若𝜷为非终结符,则产生 跳转项, T[S][𝜷] = Goto S.next_state(𝜷)
- 若𝜷为终结符,则产生式 移入项, T[S][𝜷] = Shift S.next_state(𝜷)
*/
/**
*
* @param stateSet 状态机
* @param startSymbol 开始符号
* @param allowedFollowsWhenReducing 对于任意一个归约项,返回其允许的「归约展望符」(SLR/LR)
*/
function getParsingTable(stateSet, startSymbol, allowedFollowsWhenReducing) {
let parsingTable = new _Definition2.ParsingTable(); //状态集中的每一个状态
for (let curState of stateSet) {
//状态中的每一个项
for (let curItem of curState) {
let {
prec: curPrec,
leftAssoc: curLeftAssoc
} = curItem.prod; // 移入项 形如: A->𝜶·𝜷
if (curItem.hasNext()) {
let followSymbol = curItem.nextSymbol();
let existingOp = parsingTable.get(curState, followSymbol);
if (existingOp != null) {
//issue #关于冲突检查 1
if (existingOp.isReduce()) {
//issue #关于冲突检查 2. 移入-归约冲突
if (!compareForShiftReduceConflict({
prec: curPrec,
leftAssoc: curLeftAssoc
}, {
prec: existingOp.prec,
leftAssoc: existingOp.leftAssoc
})) continue;
} else if (existingOp.isShift()) //issue #关于优先级与结合性 2
existingOp.setIfLarger(curPrec, curLeftAssoc);
}
let nextState = curState.getNextState(followSymbol);
(0, _shim.assert)(nextState != null);
let op; //goto
if (followSymbol instanceof _Definition.NonTerminal) op = new _Definition2.Goto(nextState); //shift
else op = new _Definition2.Shift(nextState, curPrec, curLeftAssoc);
debug(`state(${curState.id}) ${followSymbol} ${op}`);
parsingTable.put(curState, followSymbol, op);
} //归约项 形如: A->𝜶𝜷·
else {
let allowedFollows = allowedFollowsWhenReducing(curItem); //issue #关于lookahead的确定
(0, _shim.assert)(allowedFollows != null);
for (let followSymbol of allowedFollows) {
let existingOp = parsingTable.get(curState, followSymbol);
if (existingOp != null) {
//issue #关于冲突检查
if (existingOp.isReduce() && existingOp.prod.nonTerminal != curItem.prod.nonTerminal) {
//issue #关于冲突检查 1. 归约-归约冲突
if (!compareForReduceReduceConflict({
prec: curPrec,
leftAssoc: curLeftAssoc
}, {
prec: existingOp.prec,
leftAssoc: curLeftAssoc
})) {
continue;
}
} else if (existingOp.isShift()) {
//issue #关于冲突检查 2. 移入-归约冲突
if (!compareForShiftReduceConflict({
prec: curPrec,
leftAssoc: curLeftAssoc
}, {
prec: existingOp.prec,
leftAssoc: curLeftAssoc
})) {
continue;
}
}
}
let op; //accept
if (followSymbol == _parserDefinition.EOF && curItem.prod.nonTerminal == startSymbol) op = new _Definition2.Accept(); //reduce
else op = new _Definition2.Reduce(curItem.prod, curPrec, curLeftAssoc);
debug(`state(${curState.id}) ${followSymbol} ${op}`);
parsingTable.put(curState, followSymbol, op);
}
}
}
}
return parsingTable;
} //issue #关于优先级与结合性 3.1 移入归约冲突
function compareForShiftReduceConflict(cur, existing) {
let {
prec: curPrec,
leftAssoc: curLeftAssoc
} = cur;
let {
prec: existingPrec
} = existing;
if (curPrec > existingPrec) {
//shift
return true;
} else if (curPrec == existingPrec) {
// assert(curLeftAssoc == exisingAssoc, `Both ${existingOp.prod},${curItem.prod} contain the same priority ${curPrec} but assoc differently`);
if (curLeftAssoc) {
//reduce
return false;
} else {
//shift
return true;
}
} else {
//reduce
return false;
}
} //issue #关于优先级与结合性 3.2 归约归约冲突
function compareForReduceReduceConflict(cur, existing) {
let {
prec: curPrec
} = cur;
let {
prec: existingPrec
} = existing;
if (curPrec > existingPrec) {
return true;
} else {
return false;
}
}
/*
关于Shift、GOTO、Reduce、Accept的产生时机
- Shift/GOTO,每当遇到形如A->𝜶·𝜷的项时,如果𝜷是非终结符则产生GOTO动作,否则为Shift
- Reduce/Accept,每当遇到形如A->𝜶·的项时,如果A是开始符号则Accpet,否则为Reduce
关于lookahead的确定
- Shift/GOTO,对于一个形如A->𝜶·𝜷的项,其lookahead为𝜷
- Reduce/Accept,取决于具体分析算法:
- SLR,lookhead存在于「项对应的产生式的Follow集」
- LR,lookhead存在于项的展望集(lookaheadSet)
关于冲突检查
1. 移入-归约冲突
1.1 允许多个项移入同一符号,考虑如下两个项:
S->·E
E->·E+T
它们的 nextSymbol 都是 E, 在处理第一个项时,已经计算了当前状态对输入E的后继状态,所以在当前状态第二次遇到E时,不必再重新处理.
1.2 对同一lookahead,存在移入、归约操作,考虑如下项集:
E->E+E·,{+,*,EOF}
E->E·+E,{+,*,EOF}
E->E·*E,{+,*,EOF}
该项集对于输入{+,*,EOF},同时存在移入、归约操作.
2. 归约-归约冲突, 原则上同一个状态对同一符号只能有一个操作,若个一个状态中的多个归约项的Follow集存在交集,那么分为如下两种情况进行处理:
2.1 允许状态中多个项归约为同一个符号. 如下例子中,虽然两个项的Follow集都为Follow(A),但是归约动作/结果都是相同的(都是A),因此允许该情况存在.
A->𝜶·
A->𝜶·
2.2 不允许状态中多个项归约为不同符号. 如下例子中,Follow(A) ∩ Follow(B)不为空的情况下,无法预知归约为A还是B,因此如下情况不允许存在.
A->𝜶·
B->𝜶·
3. 其他冲突
3.1 允许GOTO-GOTO冲突, 即: A->a·C B->b·C
3.2 Accept和 GOTO、Shift、Reduce都不可能冲突,因为Accept具备以下特征:
- EOF才可能触发Accept (Shift、GOTO不具备)
- 归约项必须为开始符号 (Reduce不具备)
关于优先级与结合性
1.每一个项都具有优先级、结合性属性.
E->E+E·,{+,*,EOF}
E->E·+E,{+,*,EOF}
E->E·*E,{+,*,EOF}
+: prec=1,assoc=left
*: prec=2,assoc=left
2. 如何确定一个项、操作的优先级、结合性?
2.1 项的prec、assoc由产生式中的符号决定,例如 E->E+E中只有`+`会有优先级、结合性,因此该产生式的优先级、结合性取决于`+`;
如果项中存在多个符号具有prec、assoc,则以prec最大的符号为准,例如 E->E+E*, 其优先级以`*`为准.
2.2 操作的prec、assoc取决于产生式该移入操作的项,例如 E->E·+E,会产生式一个对+的`shift`操作,该操作的优先级、结合性取决于项E->E·+E;
类似的,E->E+E·会产生式一个`reduce`操作,该操作的prec、assoc取决于E->E+E·
3. 如何根据优先级、结合性决定解决冲突?
通过比较两个操作的prec、assoc,
3.1 移入归约冲突
如果相同优先级
结合方向相同
- 左结合,优先归约
- 右结合,优先移入
结合方向不同
抛出异常
不同优先级
以高优先级的操作的prec、assoc为准
3.2 归约归约冲突
以高优先级的操作的prec、assoc为准
例1,E->E·+E产生一个`shift`,而E->E+E·产生一个`reduce`,两个操作的优先级相同,且为左结合,因此选择`reduce`
例2,E->E·*E将产生一个`shift`, 而E->E+E·产生一个`reduce`,前者的优先级更高,因此选择`shift`
*/
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9MUi9QYXJzaW5nVGFibGUudHMiXSwibmFtZXMiOlsiZGVidWciLCJnZXRQYXJzaW5nVGFibGUiLCJzdGF0ZVNldCIsInN0YXJ0U3ltYm9sIiwiYWxsb3dlZEZvbGxvd3NXaGVuUmVkdWNpbmciLCJwYXJzaW5nVGFibGUiLCJQYXJzaW5nVGFibGUiLCJjdXJTdGF0ZSIsImN1ckl0ZW0iLCJwcmVjIiwiY3VyUHJlYyIsImxlZnRBc3NvYyIsImN1ckxlZnRBc3NvYyIsInByb2QiLCJoYXNOZXh0IiwiZm9sbG93U3ltYm9sIiwibmV4dFN5bWJvbCIsImV4aXN0aW5nT3AiLCJnZXQiLCJpc1JlZHVjZSIsImNvbXBhcmVGb3JTaGlmdFJlZHVjZUNvbmZsaWN0IiwiaXNTaGlmdCIsInNldElmTGFyZ2VyIiwibmV4dFN0YXRlIiwiZ2V0TmV4dFN0YXRlIiwib3AiLCJOb25UZXJtaW5hbCIsIkdvdG8iLCJTaGlmdCIsImlkIiwicHV0IiwiYWxsb3dlZEZvbGxvd3MiLCJub25UZXJtaW5hbCIsImNvbXBhcmVGb3JSZWR1Y2VSZWR1Y2VDb25mbGljdCIsIkVPRiIsIkFjY2VwdCIsIlJlZHVjZSIsImN1ciIsImV4aXN0aW5nIiwiZXhpc3RpbmdQcmVjIl0sIm1hcHBpbmdzIjoiOzs7Ozs7O0FBQUE7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBSUE7Ozs7QUFDQSxJQUFJQSxLQUFLLEdBQUcsb0JBQVUsc0JBQVYsQ0FBWjtBQUVBOzs7Ozs7Ozs7Ozs7Ozs7QUFjQTs7Ozs7OztBQU1PLFNBQVNDLGVBQVQsQ0FBeUJDLFFBQXpCLEVBQTZDQyxXQUE3QyxFQUF1RUMsMEJBQXZFLEVBQWlKO0FBRXZKLE1BQUlDLFlBQVksR0FBRyxJQUFJQyx5QkFBSixFQUFuQixDQUZ1SixDQUl2Sjs7QUFDQSxPQUFLLElBQUlDLFFBQVQsSUFBcUJMLFFBQXJCLEVBQStCO0FBQzlCO0FBQ0EsU0FBSyxJQUFJTSxPQUFULElBQW9CRCxRQUFwQixFQUE4QjtBQUM3QixVQUFJO0FBQUVF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QVA7QUFDQSxLQUhELE1BR087QUFDTjtBQUNBLGFBQU8sSUFBUDtBQUNBO0FBQ0QsR0FUTSxNQVNBO0FBQ047QUFDQSxXQUFPLEtBQVA7QUFDQTtBQUNELEMsQ0FFRDs7O0FBQ0EsU0FBU3FCLDhCQUFULENBQXdDSSxHQUF4QyxFQUFtRkMsUUFBbkYsRUFBbUk7QUFDbEksTUFBSTtBQUFFN0IsSUFBQUEsSUFBSSxFQUFFQztBQUFSLE1BQW9CMkIsR0FBeEI7QUFDQSxNQUFJO0FBQUU1QixJQUFBQSxJQUFJLEVBQUU4QjtBQUFSLE1BQXlCRCxRQUE3Qjs7QUFFQSxNQUFJNUIsT0FBTyxHQUFHNkIsWUFBZCxFQUE0QjtBQUMzQixXQUFPLElBQVA7QUFDQSxHQUZELE1BRU87QUFDTixXQUFPLEtBQVA7QUFDQTtBQUNEO0FBRUQiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBhc3NlcnQgfSBmcm9tIFwiQGxpZ2h0MHgwMC9zaGltXCI7XG5pbXBvcnQgeyBFT0YgfSBmcm9tIFwiQGxpZ2h0MHgwMC9wYXJzZXItZGVmaW5pdGlvblwiO1xuaW1wb3J0IHsgTm9uVGVybWluYWwsIFRlcm1pbmFsIH0gZnJvbSBcIi4uL0RlZmluaXRpb25cIjtcbmltcG9ydCB7IFN0YXRlU2V0LCBPcGVyYXRpb24sIFBhcnNpbmdUYWJsZSwgU2hpZnQsIEdvdG8sIFJlZHVjZSwgQWNjZXB0LCBJdGVtIH0gZnJvbSBcIi4vRGVmaW5pdGlvblwiO1xuXG5pbXBvcnQgeyBJRnVuY3Rpb24gfSBmcm9tIFwiQGxpZ2h0MHgwMC9zaGltXCI7XG5cbmltcG9ydCByb290RGVidWcgZnJvbSBcImRlYnVnXCI7XG5sZXQgZGVidWcgPSByb290RGVidWcoXCJBUFA6TFI6cGFyc2luZy10YWJsZVwiKTtcblxuLypcbui+k+WFpTog5LiA5Liq55Sx44CM6aG56ZuG5peP44CN5ZKM44CM6aG56ZuG6Ze06Lez6L2s5YWz57O744CN57uE5oiQ55qE54q25oCB5py6XG7ovpPlh7o6IExSL1NMUuWIhuaekOihqFxuXG7ms6g6IOeUqCBzdGF0ZUEubmV4dF9zdGF0ZShpbnB1dF9zeW1ib2wpIOihqOekuueKtuaAgUHovpPlhaXnrKblj7flkI7ov5vlhaXnmoTkuIvkuIDkuKrnirbmgIFcblxu6YGN5Y6G5q+P5LiA5Liq54q25oCBU+S4reeahOavj+S4gOS4qumhuUlcbiAgICAtIEnkuLpSZWR1Y2UvQWNjcGV06aG5LOW9ouWmgiBBLT7wnZy28J2ct8K3XG4gICAgICAgIC0g6IulQeaYr+S4jeaYr+W8gOWni+espuWPtyzliJnkuLpGb2xsb3coVCnkuK3nmoTmr4/kuIDkuKrnrKblj7dm5aGr5YWlOiBUW1NdW2ZdID0gUmVkdWNlIEFcbiAgICAgICAgLSDoi6VB5piv5byA5aeL56ym5Y+3LOaTjeS9nOWQjOS4iizkuI3ov4floavlhaXlvpfml7YgQWNjZXB0IEEgKOacrOi0qOS4ikFjY2VwdOmhueaYr+S4gOS4queJueauiueahOW9kue6pumhuSlcbiAgICAtIEnkuLpTaGlmdC9Hb3RvLCBBLT7wnZy2wrfwnZy3XG4gICAgICAgIC0g6Iul8J2ct+S4uumdnue7iOe7k+espizliJnkuqfnlJ8g6Lez6L2s6aG5LCBUW1NdW/CdnLddID0gR290byBTLm5leHRfc3RhdGUo8J2ctylcbiAgICAgICAgLSDoi6XwnZy35Li657uI57uT56ymLOWImeS6p+eUn+W8jyDnp7vlhaXpobksIFRbU11b8J2ct10gPSBTaGlmdCBTLm5leHRfc3RhdGUo8J2ctylcbiovXG4vKipcbiAqXG4gKiBAcGFyYW0gc3RhdGVTZXQg54q25oCB5py6XG4gKiBAcGFyYW0gc3RhcnRTeW1ib2wg5byA5aeL56ym5Y+3XG4gKiBAcGFyYW0gYWxsb3dlZEZvbGxvd3NXaGVuUmVkdWNpbmcg5a+55LqO5Lu75oSP5LiA5Liq5b2S57qm6aG5LOi/lOWbnuWFtuWFgeiuuOeahOOAjOW9kue6puWxleacm+espuOAjShTTFIvTFIpXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRQYXJzaW5nVGFibGUoc3RhdGVTZXQ6IFN0YXRlU2V0LCBzdGFydFN5bWJvbDogTm9uVGVybWluYWwsIGFsbG93ZWRGb2xsb3dzV2hlblJlZHVjaW5nOiBJRnVuY3Rpb248SXRlbSwgU2V0PFRlcm1pbmFsPj4pOiBQYXJzaW5nVGFibGUge1xuXG5cdGxldCBwYXJzaW5nVGFibGUgPSBuZXcgUGFyc2luZ1RhYmxlKCk7XG5cblx0Ly/nirbmgIHpm4bkuK3nmoTmr4/kuIDkuKrnirbmgIFcblx0Zm9yIChsZXQgY3VyU3RhdGUgb2Ygc3RhdGVTZXQpIHtcblx0XHQvL+eKtuaAgeS4reeahOavj+S4gOS4qumhuVxuXHRcdGZvciAobGV0IGN1ckl0ZW0gb2YgY3VyU3RhdGUpIHtcblx0XHRcdGxldCB7IHByZWM6IGN1clByZWMsIGxlZnRBc3NvYzogY3VyTGVmdEFzc29jIH0gPSBjdXJJdGVtLnByb2Q7XG5cdFx0XHQvLyDnp7vlhaXpobkg5b2i5aaCOiBBLT7wnZy2wrfwnZy3XG5cdFx0XHRpZiAoY3VySXRlbS5oYXNOZXh0KCkpIHtcblx0XHRcdFx0bGV0IGZvbGxvd1N5bWJvbCA9IGN1ckl0ZW0ubmV4dFN5bWJvbCgpO1xuXHRcdFx0XHRsZXQgZXhpc3RpbmdPcCA9IHBhcnNpbmdUYWJsZS5nZXQoY3VyU3RhdGUsIGZvbGxvd1N5bWJvbCk7XG5cdFx0XHRcdGlmIChleGlzdGluZ09wICE9IG51bGwpIHsgLy9pc3N1ZSAj5YWz5LqO5Yay56qB5qOA5p+lIDFcblx0XHRcdFx0XHRpZiAoZXhpc3RpbmdPcC5pc1JlZHVjZSgpKSB7IC8vaXNzdWUgI+WFs+S6juWGsueqgeajgOafpSAyLiDnp7vlhaUt5b2S57qm5Yay56qBXG5cdFx0XHRcdFx0XHRpZiAoIWNvbXBhcmVGb3JTaGlmdFJlZHVjZUNvbmZsaWN0KHsgcHJlYzogY3VyUHJlYywgbGVmdEFzc29jOiBjdXJMZWZ0QXNzb2MgfSwgeyBwcmVjOiBleGlzdGluZ09wLnByZWMsIGxlZnRBc3NvYzogZXhpc3RpbmdPcC5sZWZ0QXNzb2MgfSkpXG5cdFx0XHRcdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRlbHNlIGlmIChleGlzdGluZ09wLmlzU2hpZnQoKSkgLy9pc3N1ZSAj5YWz5LqO5LyY5YWI57qn5LiO57uT5ZCI5oCnIDJcblx0XHRcdFx0XHRcdGV4aXN0aW5nT3Auc2V0SWZMYXJnZXIoY3VyUHJlYywgY3VyTGVmdEFzc29jKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRsZXQgbmV4dFN0YXRlID0gY3VyU3RhdGUuZ2V0TmV4dFN0YXRlKGZvbGxvd1N5bWJvbCk7XG5cdFx0XHRcdGFzc2VydChuZXh0U3RhdGUgIT0gbnVsbCk7XG5cdFx0XHRcdGxldCBvcDogT3BlcmF0aW9uO1xuXHRcdFx0XHQvL2dvdG9cblx0XHRcdFx0aWYgKGZvbGxvd1N5bWJvbCBpbnN0YW5jZW9mIE5vblRlcm1pbmFsKVxuXHRcdFx0XHRcdG9wID0gbmV3IEdvdG8obmV4dFN0YXRlKTtcblx0XHRcdFx0Ly9zaGlmdFxuXHRcdFx0XHRlbHNlXG5cdFx0XHRcdFx0b3AgPSBuZXcgU2hpZnQobmV4dFN0YXRlLCBjdXJQcmVjLCBjdXJMZWZ0QXNzb2MpO1xuXHRcdFx0XHRkZWJ1Zyhgc3RhdGUoJHtjdXJTdGF0ZS5pZH0pICR7Zm9sbG93U3ltYm9sfSAke29wfWApO1xuXHRcdFx0XHRwYXJzaW5nVGFibGUucHV0KGN1clN0YXRlLCBmb2xsb3dTeW1ib2wsIG9wKTtcblx0XHRcdH1cblx0XHRcdC8v5b2S57qm6aG5IOW9ouWmgjogQS0+8J2ctvCdnLfCt1xuXHRcdFx0ZWxzZSB7XG5cdFx0XHRcdGxldCBhbGxvd2VkRm9sbG93cyA9IGFsbG93ZWRGb2xsb3dzV2hlblJlZHVjaW5nKGN1ckl0ZW0pOyAvL2lzc3VlICPlhbPkuo5sb29rYWhlYWTnmoTnoa7lrppcblx0XHRcdFx0YXNzZXJ0KGFsbG93ZWRGb2xsb3dzICE9IG51bGwpO1xuXHRcdFx0XHRmb3IgKGxldCBmb2xsb3dTeW1ib2wgb2YgYWxsb3dlZEZvbGxvd3MpIHtcblx0XHRcdFx0XHRsZXQgZXhpc3RpbmdPcCA9IHBhcnNpbmdUYWJsZS5nZXQoY3VyU3RhdGUsIGZvbGxvd1N5bWJvbCk7XG5cdFx0XHRcdFx0aWYgKGV4aXN0aW5nT3AgIT0gbnVsbCkgeyAvL2lzc3VlICPlhbPkuo7lhrLnqoHmo4Dmn6Vcblx0XHRcdFx0XHRcdGlmIChleGlzdGluZ09wLmlzUmVkdWNlKCkgJiYgZXhpc3RpbmdPcC5wcm9kLm5vblRlcm1pbmFsICE9IGN1ckl0ZW0ucHJvZC5ub25UZXJtaW5hbCkgeyAvL2lzc3VlICPlhbPkuo7lhrLnqoHmo4Dmn6UgMS4g5b2S57qmLeW9kue6puWGsueqgVxuXHRcdFx0XHRcdFx0XHRpZiAoIWNvbXBhcmVGb3JSZWR1Y2VSZWR1Y2VDb25mbGljdChcblx0XHRcdFx0XHRcdFx0XHR7IHByZWM6IGN1clByZWMsIGxlZnRBc3NvYzogY3VyTGVmdEFzc29jIH0sXG5cdFx0XHRcdFx0XHRcdFx0eyBwcmVjOiBleGlzdGluZ09wLnByZWMsIGxlZnRBc3NvYzogY3VyTGVmdEFzc29jIH0pKSB7XG5cdFx0XHRcdFx0XHRcdFx0Y29udGludWU7XG5cdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdGVsc2UgaWYgKGV4aXN0aW5nT3AuaXNTaGlmdCgpKSB7IC8vaXNzdWUgI+WFs+S6juWGsueqgeajgOafpSAyLiDnp7vlhaUt5b2S57qm5Yay56qBXG5cdFx0XHRcdFx0XHRcdGlmICghY29tcGFyZUZvclNoaWZ0UmVkdWNlQ29uZmxpY3QoXG5cdFx0XHRcdFx0XHRcdFx0eyBwcmVjOiBjdXJQcmVjLCBsZWZ0QXNzb2M6IGN1ckxlZnRBc3NvYyB9LFxuXHRcdFx0XHRcdFx0XHRcdHsgcHJlYzogZXhpc3RpbmdPcC5wcmVjLCBsZWZ0QXNzb2M6IGN1ckxlZnRBc3NvYyB9KSkge1xuXHRcdFx0XHRcdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGxldCBvcDtcblx0XHRcdFx0XHQvL2FjY2VwdFxuXHRcdFx0XHRcdGlmIChmb2xsb3dTeW1ib2wgPT0gRU9GICYmIGN1ckl0ZW0ucHJvZC5ub25UZXJtaW5hbCA9PSBzdGFydFN5bWJvbClcblx0XHRcdFx0XHRcdG9wID0gbmV3IEFjY2VwdCgpO1xuXHRcdFx0XHRcdC8vcmVkdWNlXG5cdFx0XHRcdFx0ZWxzZVxuXHRcdFx0XHRcdFx0b3AgPSBuZXcgUmVkdWNlKGN1ckl0ZW0ucHJvZCxjdXJQcmVjLGN1ckxlZnRBc3NvYyk7XG5cdFx0XHRcdFx0ZGVidWcoYHN0YXRlKCR7Y3VyU3RhdGUuaWR9KSAke2ZvbGxvd1N5bWJvbH0gJHtvcH1gKTtcblx0XHRcdFx0XHRwYXJzaW5nVGFibGUucHV0KGN1clN0YXRlLCBmb2xsb3dTeW1ib2wsIG9wKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fVxuXHRyZXR1cm4gcGFyc2luZ1RhYmxlO1xufVxuXG4vL2lzc3VlICPlhbPkuo7kvJjlhYjnuqfkuI7nu5PlkIjmgKcgMy4xIOenu+WFpeW9kue6puWGsueqgVxuZnVuY3Rpb24gY29tcGFyZUZvclNoaWZ0UmVkdWNlQ29uZmxpY3QoY3VyOiB7IHByZWM6IG51bWJlciwgbGVmdEFzc29jOiBib29sZWFuIH0sIGV4aXN0aW5nOiB7IHByZWM6IG51bWJlciwgbGVmdEFzc29jOiBib29sZWFuIH0pIHtcblx0bGV0IHsgcHJlYzogY3VyUHJlYywgbGVmdEFzc29jOiBjdXJMZWZ0QXNzb2MgfSA9IGN1cjtcblx0bGV0IHsgcHJlYzogZXhpc3RpbmdQcmVjIH0gPSBleGlzdGluZztcblxuXHRpZiAoY3VyUHJlYyA+IGV4aXN0aW5nUHJlYykge1xuXHRcdC8vc2hpZnRcblx0XHRyZXR1cm4gdHJ1ZTtcblx0fSBlbHNlIGlmIChjdXJQcmVjID09IGV4aXN0aW5nUHJlYykge1xuXHRcdC8vIGFzc2VydChjdXJMZWZ0QXNzb2MgPT0gZXhpc2luZ0Fzc29jLCBgQm90aCAke2V4aXN0aW5nT3AucHJvZH0sJHtjdXJJdGVtLnByb2R9IGNvbnRhaW4gdGhlIHNhbWUgcHJpb3JpdHkgJHtjdXJQcmVjfSBidXQgYXNzb2MgZGlmZmVyZW50bHlgKTtcblx0XHRpZiAoY3VyTGVmdEFzc29jKSB7XG5cdFx0XHQvL3JlZHVjZVxuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHQvL3NoaWZ0XG5cdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHR9XG5cdH0gZWxzZSB7XG5cdFx0Ly9yZWR1Y2Vcblx0XHRyZXR1cm4gZmFsc2U7XG5cdH1cbn1cblxuLy9pc3N1ZSAj5YWz5LqO5LyY5YWI57qn5LiO57uT5ZCI5oCnIDMuMiDlvZLnuqblvZLnuqblhrLnqoFcbmZ1bmN0aW9uIGNvbXBhcmVGb3JSZWR1Y2VSZWR1Y2VDb25mbGljdChjdXI6IHsgcHJlYzogbnVtYmVyLCBsZWZ0QXNzb2M6IGJvb2xlYW4gfSwgZXhpc3Rpbmc6IHsgcHJlYzogbnVtYmVyLCBsZWZ0QXNzb2M6IGJvb2xlYW4gfSkge1xuXHRsZXQgeyBwcmVjOiBjdXJQcmVjIH0gPSBjdXI7XG5cdGxldCB7IHByZWM6IGV4aXN0aW5nUHJlYyB9ID0gZXhpc3Rpbmc7XG5cblx0aWYgKGN1clByZWMgPiBleGlzdGluZ1ByZWMpIHtcblx0XHRyZXR1cm4gdHJ1ZTtcblx0fSBlbHNlIHtcblx0XHRyZXR1cm4gZmFsc2U7XG5cdH1cbn1cblxuLypcbuWFs+S6jlNoaWZ044CBR09UT+OAgVJlZHVjZeOAgUFjY2VwdOeahOS6p+eUn+aXtuaculxuXHQtIFNoaWZ0L0dPVE8s5q+P5b2T6YGH5Yiw5b2i5aaCQS0+8J2ctsK38J2ct+eahOmhueaXtizlpoLmnpzwnZy35piv6Z2e57uI57uT56ym5YiZ5Lqn55SfR09UT+WKqOS9nCzlkKbliJnkuLpTaGlmdFxuXHQtIFJlZHVjZS9BY2NlcHQs5q+P5b2T6YGH5Yiw5b2i5aaCQS0+8J2ctsK355qE6aG55pe2LOWmguaenEHmmK/lvIDlp4vnrKblj7fliJlBY2NwZXQs5ZCm5YiZ5Li6UmVkdWNlXG5cbuWFs+S6jmxvb2thaGVhZOeahOehruWumlxuXHQtIFNoaWZ0L0dPVE8s5a+55LqO5LiA5Liq5b2i5aaCQS0+8J2ctsK38J2ct+eahOmhuSzlhbZsb29rYWhlYWTkuLrwnZy3XG5cdC0gUmVkdWNlL0FjY2VwdCzlj5blhrPkuo7lhbfkvZPliIbmnpDnrpfms5U6XG5cdFx0LSBTTFIsbG9va2hlYWTlrZjlnKjkuo7jgIzpobnlr7nlupTnmoTkuqfnlJ/lvI/nmoRGb2xsb3fpm4bjgI1cblx0XHQtIExSLGxvb2toZWFk5a2Y5Zyo5LqO6aG555qE5bGV5pyb6ZuGKGxvb2thaGVhZFNldClcblxu5YWz5LqO5Yay56qB5qOA5p+lXG4xLiDnp7vlhaUt5b2S57qm5Yay56qBXG5cdDEuMSDlhYHorrjlpJrkuKrpobnnp7vlhaXlkIzkuIDnrKblj7cs6ICD6JmR5aaC5LiL5Lik5Liq6aG5OlxuXHRcdFMtPsK3RVxuXHRcdEUtPsK3RStUXG5cdFx05a6D5Lus55qEIG5leHRTeW1ib2wg6YO95pivIEUsIOWcqOWkhOeQhuesrOS4gOS4qumhueaXtizlt7Lnu4/orqHnrpfkuoblvZPliY3nirbmgIHlr7novpPlhaVF55qE5ZCO57un54q25oCBLOaJgOS7peWcqOW9k+WJjeeKtuaAgeesrOS6jOasoemBh+WIsEXml7Ys5LiN5b+F5YaN6YeN5paw5aSE55CGLlxuXHQxLjIg5a+55ZCM5LiAbG9va2FoZWFkLOWtmOWcqOenu+WFpeOAgeW9kue6puaTjeS9nCzogIPomZHlpoLkuIvpobnpm4Y6XG5cdFx0RS0+RStFwrcseyssKixFT0Z9XG5cdFx0RS0+RcK3K0UseyssKixFT0Z9XG5cdFx0RS0+RcK3KkUseyssKixFT0Z9XG5cblx0XHTor6Xpobnpm4blr7nkuo7ovpPlhaV7KywqLEVPRn0s5ZCM5pe25a2Y5Zyo56e75YWl44CB5b2S57qm5pON5L2cLlxuXG4yLiDlvZLnuqYt5b2S57qm5Yay56qBLCDljp/liJnkuIrlkIzkuIDkuKrnirbmgIHlr7nlkIzkuIDnrKblj7flj6rog73mnInkuIDkuKrmk43kvZws6Iul5Liq5LiA5Liq54q25oCB5Lit55qE5aSa5Liq5b2S57qm6aG555qERm9sbG936ZuG5a2Y5Zyo5Lqk6ZuGLOmCo+S5iOWIhuS4uuWmguS4i+S4pOenjeaDheWGtei/m+ihjOWkhOeQhjpcblx0Mi4xIOWFgeiuuOeKtuaAgeS4reWkmuS4qumhueW9kue6puS4uuWQjOS4gOS4quespuWPty4g5aaC5LiL5L6L5a2Q5LitLOiZveeEtuS4pOS4qumhueeahEZvbGxvd+mbhumDveS4ukZvbGxvdyhBKSzkvYbmmK/lvZLnuqbliqjkvZwv57uT5p6c6YO95piv55u45ZCM55qEKOmDveaYr0EpLOWboOatpOWFgeiuuOivpeaDheWGteWtmOWcqC5cblx0XHRBLT7wnZy2wrdcblx0XHRBLT7wnZy2wrdcblx0Mi4yIOS4jeWFgeiuuOeKtuaAgeS4reWkmuS4qumhueW9kue6puS4uuS4jeWQjOespuWPty4g5aaC5LiL5L6L5a2Q5LitLEZvbGxvdyhBKSDiiKkgRm9sbG93KEIp5LiN5Li656m655qE5oOF5Ya15LiLLOaXoOazlemihOefpeW9kue6puS4ukHov5jmmK9CLOWboOatpOWmguS4i+aDheWGteS4jeWFgeiuuOWtmOWcqC5cblx0XHRBLT7wnZy2wrdcblx0XHRCLT7wnZy2wrdcblxuMy4g5YW25LuW5Yay56qBXG5cdDMuMSDlhYHorrhHT1RPLUdPVE/lhrLnqoEsIOWNszogQS0+YcK3QyBCLT5iwrdDXG5cdDMuMiBBY2NlcHTlkowgR09UT+OAgVNoaWZ044CBUmVkdWNl6YO95LiN5Y+v6IO95Yay56qBLOWboOS4ukFjY2VwdOWFt+Wkh+S7peS4i+eJueW+gTpcblx0XHQtIEVPRuaJjeWPr+iDveinpuWPkUFjY2VwdCAoU2hpZnTjgIFHT1RP5LiN5YW35aSHKVxuXHRcdC0g5b2S57qm6aG55b+F6aG75Li65byA5aeL56ym5Y+3IChSZWR1Y2XkuI3lhbflpIcpXG5cbuWFs+S6juS8mOWFiOe6p+S4jue7k+WQiOaAp1xuXHQxLuavj+S4gOS4qumhuemDveWFt+acieS8mOWFiOe6p+OAgee7k+WQiOaAp+WxnuaApy5cblx0XHRFLT5FK0XCtyx7KywqLEVPRn1cblx0XHRFLT5FwrcrRSx7KywqLEVPRn1cblx0XHRFLT5FwrcqRSx7KywqLEVPRn1cblx0XHQrOiBwcmVjPTEsYXNzb2M9bGVmdFxuXHRcdCo6IHByZWM9Mixhc3NvYz1sZWZ0XG5cblx0Mi4g5aaC5L2V56Gu5a6a5LiA5Liq6aG544CB5pON5L2c55qE5LyY5YWI57qn44CB57uT5ZCI5oCnP1xuXHRcdDIuMSDpobnnmoRwcmVj44CBYXNzb2PnlLHkuqfnlJ/lvI/kuK3nmoTnrKblj7flhrPlrpos5L6L5aaCIEUtPkUrReS4reWPquaciWArYOS8muacieS8mOWFiOe6p+OAgee7k+WQiOaApyzlm6DmraTor6XkuqfnlJ/lvI/nmoTkvJjlhYjnuqfjgIHnu5PlkIjmgKflj5blhrPkuo5gK2A7XG5cdFx0XHTlpoLmnpzpobnkuK3lrZjlnKjlpJrkuKrnrKblj7flhbfmnIlwcmVj44CBYXNzb2Ms5YiZ5LulcHJlY+acgOWkp+eahOespuWPt+S4uuWHhizkvovlpoIgRS0+RStFKiwg5YW25LyY5YWI57qn5LulYCpg5Li65YeGLlxuXHRcdDIuMiDmk43kvZznmoRwcmVj44CBYXNzb2Plj5blhrPkuo7kuqfnlJ/lvI/or6Xnp7vlhaXmk43kvZznmoTpobks5L6L5aaCIEUtPkXCtytFLOS8muS6p+eUn+W8j+S4gOS4quWvuSvnmoRgc2hpZnRg5pON5L2cLOivpeaTjeS9nOeahOS8mOWFiOe6p+OAgee7k+WQiOaAp+WPluWGs+S6jumhuUUtPkXCtytFO1xuXHRcdFx057G75Ly855qELEUtPkUrRcK35Lya5Lqn55Sf5byP5LiA5LiqYHJlZHVjZWDmk43kvZws6K+l5pON5L2c55qEcHJlY+OAgWFzc29j5Y+W5Yaz5LqORS0+RStFwrdcblxuXHQzLiDlpoLkvZXmoLnmja7kvJjlhYjnuqfjgIHnu5PlkIjmgKflhrPlrprop6PlhrPlhrLnqoE/XG5cdFx06YCa6L+H5q+U6L6D5Lik5Liq5pON5L2c55qEcHJlY+OAgWFzc29jLFxuXHRcdDMuMSDnp7vlhaXlvZLnuqblhrLnqoFcblx0XHRcdOWmguaenOebuOWQjOS8mOWFiOe6p1xuXHRcdFx0XHTnu5PlkIjmlrnlkJHnm7jlkIxcblx0XHRcdFx0XHQtIOW3pue7k+WQiCzkvJjlhYjlvZLnuqZcblx0XHRcdFx0XHQtIOWPs+e7k+WQiCzkvJjlhYjnp7vlhaVcblx0XHRcdFx057uT5ZCI5pa55ZCR5LiN5ZCMXG5cdFx0XHRcdFx05oqb5Ye65byC5bi4XG5cdFx0XHTkuI3lkIzkvJjlhYjnuqdcblx0XHRcdFx05Lul6auY5LyY5YWI57qn55qE5pON5L2c55qEcHJlY+OAgWFzc29j5Li65YeGXG5cdFx0My4yIOW9kue6puW9kue6puWGsueqgVxuXHRcdFx05Lul6auY5LyY5YWI57qn55qE5pON5L2c55qEcHJlY+OAgWFzc29j5Li65YeGXG5cdFx05L6LMSxFLT5FwrcrReS6p+eUn+S4gOS4qmBzaGlmdGAs6ICMRS0+RStFwrfkuqfnlJ/kuIDkuKpgcmVkdWNlYCzkuKTkuKrmk43kvZznmoTkvJjlhYjnuqfnm7jlkIws5LiU5Li65bem57uT5ZCILOWboOatpOmAieaLqWByZWR1Y2VgXG5cdFx05L6LMixFLT5FwrcqReWwhuS6p+eUn+S4gOS4qmBzaGlmdGAsIOiAjEUtPkUrRcK35Lqn55Sf5LiA5LiqYHJlZHVjZWAs5YmN6ICF55qE5LyY5YWI57qn5pu06auYLOWboOatpOmAieaLqWBzaGlmdGBcbiovIl19