UNPKG

@parser-generator/core

Version:

A Parser Generator that supports LL,SLR,LR1,LALR

300 lines (293 loc) 8.78 kB
import { Shift, Reduce } from "@parser-generator/definition"; import { addAll_Set, assert } from "@light0x00/shim"; export class Item { constructor(production, dot = 0, lookaheadSet = new Set()) { this._dot = 0; this._prod = production; /* 对于空推导(形如A->𝝴). 直接将 dot 置于末尾(A->𝝴·) */ this._dot = production.isEpsilon() ? production.body.length : dot; this.lookaheadSet = lookaheadSet; } get prod() { return this._prod; } /* TODO 更名为dot */ get dotPos() { return this._dot; } hasNext() { return this._dot < this.prod.body.length; } nextSymbol() { if (!this.hasNext()) throw new Error(`${this} has no next item!`); return this.prod.body[this._dot]; } nextItem() { if (!this.hasNext()) throw new Error(`${this} has no next item!`); /* 😇New features to support LR 为什么当前项展望集应该被后继项继承? 考虑如下文法: S->As A->a I0 S->·As ,$ A->·a ,s I1 A->a· ,s 如上所示,I0输入a得到I1 其中「A->a· ,s」继承了「 A->·a ,s」的展望符 */ return new Item(this.prod, this._dot + 1, this.lookaheadSet); } toString() { let str = ""; let symbols = this.prod.body; for (let i = 0; i < symbols.length; i++) { if (this._dot == i) str += "·"; str += symbols[i]; } if (this._dot == symbols.length) str += "·"; str += ","; str += "{" + Array.from(this.lookaheadSet).join(",") + "}"; return `${this.prod.head.name}->${str}`; } equals(other) { /* 两个项是否相等, 1. 所含产生式一样 2. 「dot」所在位置一样 😇New features to support LR 3. 展望集(lookaheadSet)一样 */ if (other.prod.id != this.prod.id) return false; if (other._dot != this._dot) return false; if (other.lookaheadSet.size != this.lookaheadSet.size) return false; for (let t of this.lookaheadSet) { if (!other.lookaheadSet.has(t)) { return false; } } return true; } /* */ /** * 😇New features to support LALR * 判断是否是同心项(prod、dot相等) * @param other */ isSameCoreWith(other) { if (other.prod.id != this.prod.id) return false; if (other._dot != this._dot) return false; return true; } } /* TODO 不再继承数组 */ export class ItemSet extends Array { constructor(...items) { super(...items); /** * 同心项是无意义的,此Map会记录每一个项的key(由项的 prodId、dot组成). * 每当添加一个新项时,检查是否存在与之同心的项, 如果有,则只需合并即可. */ this.added = new Map(); for (let item of items) this.added.set(this.coreKey(item), item); } contains(item) { return this.added.has(this.coreKey(item)); } coreKey(item) { return item.prod.id + "" + item.dotPos; } pushOrMerge(item) { let existing = this.added.get(this.coreKey(item)); if (existing === undefined) { this.push(item); } else { //已经存在 则合并LR展望符集 addAll_Set(existing.lookaheadSet, item.lookaheadSet); } } //最好的办法是 将数组作为内部对象 而不是继承 push(item) { this.added.set(this.coreKey(item), item); return super.push(item); } toString() { let str = ""; for (let item of this) { str += item.toString() + "\n"; } return str.replace(/\n$/, ""); } equals(other) { /* 两个项集A、B是否相等,取决于 1. 它们所包含的项个数相等 2. A、B中的每一个项在对方集合中都存在相等的项 */ if (other.length != this.length) return false; //又写出一个n2复杂度23333 !!更好的办法是用Set来存放 for (let o1 of this) { let hasMatched = false; for (let o2 of other) { if (o1.equals(o2)) { hasMatched = true; break; } } if (!hasMatched) return false; } return true; } /* 返回当前状态的后继符号集 例: 对于下状态(项集),其后继符号为: {E,T,F,(,id} I0 S->·E E->·E+T E->·T T->·T*F T->·F F->·(E) F->·id */ getNextSymbols() { let nextSymbols = new Set(); for (let item of this) { if (item.hasNext()) { nextSymbols.add(item.nextSymbol()); } } return nextSymbols; } } export class State extends ItemSet { constructor(id, items) { super(...items); /* 描述当前状态在接受某个符号后去往的后继状态 */ this.mapping = new Map(); this.id = id; } toString() { return `<${this.id}>\n${super.toString()}`; } /** * 添加后继状态 * 添加当前状态输入「指定符号」后去往的「后继状态」 * * 原则上一个状态对于同一符号只能有一个后继状态,如果重复将抛出异常 * @param symbol 输入符号 * @param nextState 后继状态 */ addNextState(symbol, nextState) { assert(this.mapping.get(symbol) == null, `A state can only have one next state to the same symbol`); this.mapping.set(symbol, nextState); } getNextState(symbol) { return this.mapping.get(symbol); } } export class StateSet extends Array { constructor() { super(...arguments); this.mapping = new Map(); } /* 判断状态集中是否已经存在 相同性质(参见ItemSet的equals方法) 的状态(项集) */ getExisting(state) { for (let existing of this) { if (existing.equals(state)) { return existing; } } return null; } satrtState() { assert(this[0] != null); return this[0]; } } export class ShiftExt extends Shift { constructor(nextStateId, prec = -1, assoc = false) { super(nextStateId); this.prec = prec; this.assoc = assoc; } setIfLarger(prec, assoc) { if (prec > this.prec) { this.prec = prec; this.assoc = assoc; } } isShift() { return true; } } export class ReduceExt extends Reduce { constructor(prod, prec = -1, leftAssoc = false) { super(prod); this.prec = prec; this.leftAssoc = leftAssoc; } equals(obj) { return obj instanceof Reduce && obj.prod == this.prod; } isReduce() { return true; } } // export type LRParsingTable = Map<number, Map<SSymbol, Operation>>; // export class ParsingTable { // private parsingTable = new Map<number, Map<SSymbol, Operation>>() // get table() { // return this.parsingTable; // } // put(state: number, symbol: SSymbol, op: Operation): void { // let row = this.parsingTable.get(state); // if (row == undefined) { // row = new Map<SSymbol, Operation>(); // this.parsingTable.set(state, row); // } // row.set(symbol, op); // } // has(state: number, symbol: SSymbol, op: Operation): boolean { // let row = this.parsingTable.get(state); // if (row == undefined) // return false; // let exstingOp = row.get(symbol); // if (exstingOp == null) // return false; // return exstingOp.equals(op); // } // get(state: number, symbol: SSymbol): Operation | undefined { // let row = this.parsingTable.get(state); // // assert(row != null, `state(${state.id}) is not found in parsing table!`) // if (row == undefined) // return undefined; // return row.get(symbol); // } // getExpectedSymbols(state: number): SSymbol[] { // let row = this.parsingTable.get(state); // if (row == undefined) // return []; // return Array.from(row.keys()); // } // getExpectedTokens(state: number) { // let expS = this.getExpectedSymbols(state); // return expS.filter(s => !(s instanceof NonTerminal)); // } // } //# sourceMappingURL=definition.js.map