UNPKG

@light0x00/parser-generator

Version:

A Parser Generator

469 lines (413 loc) 10.9 kB
import { Production, Terminal, Symbol, NonTerminal, ActionGrammar, RawActionGrammar, TraitsTable } from "../Definition"; import { addAll_Set, assert } from "@light0x00/shim"; import { ILexer, ASTree } from "@light0x00/parser-definition"; /* 项是所属项集中的一个成员,一个「项」由「产生式」和一个「点」组成,例如 A->a·B */ export class Item { prod: Production; private _dot: number = 0; /* 由于lookaheadSet可能重复,所以使用Set来存储 为什么lookaheadSet可能重复? S->A A->BC B->b | 𝝴 C->b | 𝝴 S->·A , {$} A->·BC , {b,b,$} //重复 */ lookaheadSet: Set<Terminal> constructor(production: Production, dot: number = 0, lookaheadSet: Set<Terminal> = new Set<Terminal>()) { this.prod = production; /* 对于空推导(形如A->𝝴). 直接将 dot 置于末尾(A->𝝴·) */ this._dot = production.isEpsilon() ? production.length : dot; this.lookaheadSet = lookaheadSet; } get dotPos() { return this._dot; } hasNext(): boolean { return this._dot < this.prod.length; } nextSymbol(): Symbol { if (!this.hasNext()) throw new Error(`${this} has no next item!`); return this.prod[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; 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.name}->${str}`; } equals(other: Item) { /* 两个项是否相等, 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; //n2复杂度 暂时不管 for (let t of this.lookaheadSet) { // let find = false // for (let o of other.lookaheadSet) { // if (o == t) { // find = true // break // } // } // if (!find) // return false if (!other.lookaheadSet.has(t)) { return false; } } return true; } /* 😇New features to support LALR */ isSameCoreWith(other: Item) { if (other.prod.id != this.prod.id) return false; if (other._dot != this._dot) return false; return true; } private hashCodeCache: string hashCode(): string { if (this.hashCodeCache == null) { // this.hashCodeCache = this.prod.id + '' + this._dot + hash.keys(this.lookaheadSet) this.hashCodeCache = this.prod.id + "-" + this._dot; } return this.hashCodeCache; } // reducible() { // return this.dot == this.prod.length // } } export class ItemSet extends Array<Item> { //用于记录已经添加的项的Hash 防止逻辑上的重复 added = new Map<string, Item>() items = new Array<Item>() constructor(...items: Item[]) { super(...items); for (let item of items) this.added.set(item.hashCode(), item); } contains(item: Item) { return this.added.has(item.hashCode()); } pushOrMerge(item: Item) { let existing = this.added.get(item.hashCode()); if (existing === undefined) { this.push(item); } else { //已经存在 则合并LR展望符集 addAll_Set(existing.lookaheadSet, item.lookaheadSet); } } //最好的办法是 将数组作为内部对象 而不是继承 push(item: Item) { this.added.set(item.hashCode(), item); return super.push(item); } toString() { let str = ""; for (let item of this) { str += item.toString() + "\n"; } return str.replace(/\n$/, ""); } equals(other: ItemSet) { /* 两个项集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<Symbol>(); for (let item of this) { if (item.hasNext()) { nextSymbols.add(item.nextSymbol()); } } return nextSymbols; } } /* 状态基本等同于项集 唯一的区别是状态需要id 而项集不需要 */ export class State extends ItemSet { id: number /* 描述当前状态在接受某个符号后去往的后继状态 */ mapping = new Map<Symbol, State>() constructor(id: number, items: Item[]) { super(...items); this.id = id; } toString() { return `<${this.id}>\n${super.toString()}`; } /** * 添加后继状态 * 添加当前状态输入「指定符号」后去往的「后继状态」 * * 原则上一个状态对于同一符号只能有一个后继状态,如果重复将抛出异常 * @param symbol 输入符号 * @param nextState 后继状态 */ addNextState(symbol: Symbol, nextState: State) { 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: Symbol) { return this.mapping.get(symbol); } } export class StateSet extends Array<State>{ mapping = new Map<State, Map<Symbol, State>>() /* 判断状态集中是否已经存在 相同性质(参见ItemSet的equals方法) 的状态(项集) */ getExisting(state: ItemSet): null | State { for (let existing of this) { if (existing.equals(state)) { return existing; } } return null; } satrtState(): State { assert(this[0] != null); return this[0]; } } /* LR\SLR 在构造状态时有所不同 因此将不同部分独立处理 */ export interface AutomataTools { GOTO(I: ItemSet, inputSymbol: Symbol): ItemSet closure(itemSet: ItemSet): ItemSet getStartState(grammar: ActionGrammar): State } /** * * 将二维数组形式的产生式集(Symbol[][]) 转为带有编号的Production集合,并赋予指定的非终结符 * * 一个非终结符的产生式集可视为一个Symbol二维数组, * 为了便于实现LR分析表,需要将这个二维数组转为Production集合, * 且为每个Production赋予一个编号. */ export class AugmentedGrammar extends ActionGrammar { constructor(rawGrammar: RawActionGrammar, traits: TraitsTable) { super(rawGrammar, traits); } afterConstruct() { assert(this.startNT().length == 1 && this.startNT()[0].length == 1, "The start symbol of an augmented grammar has one and only one production, and the production contains only one symbol!"); } /* 开始符号的产生式 原则上只能有一个 */ get prodOfStartNT(): Production { return this.startNT()[0]; } } /* 表示LR语法分析表格的操作 */ export abstract class Operation { abstract equals(obj: Object): boolean isReduce(): this is Reduce { return false; } isShift(): this is Shift { return false; } isGoto(): this is Goto { return false; } isAccept(): this is Accept { return false; } } export class Shift extends Operation { nextState: State prec: number; assoc: boolean; constructor(nextState: State, prec: number = -1, assoc: boolean = false) { super(); this.nextState = nextState; this.prec = prec; this.assoc = assoc; } setIfLarger(prec: number, assoc: boolean) { if (prec > this.prec) { this.prec = prec; this.assoc = assoc; } } toString() { return `shift ${this.nextState.id}`; } equals(obj: Object) { return obj instanceof Shift && obj.nextState == this.nextState; } isShift() { return true; } } export class Goto extends Operation { nextState: State constructor(nextState: State) { super(); this.nextState = nextState; } toString() { return `goto ${this.nextState.id}`; } equals(obj: Object) { return obj instanceof Goto && obj.nextState == this.nextState; } isGoto() { return true; } } export class Reduce extends Operation { /* 用于归约的产生式 */ prod: Production; prec: number; leftAssoc: boolean; constructor(prod: Production, prec: number = -1, leftAssoc: boolean = false) { super(); this.prod = prod; this.prec = prec; this.leftAssoc = leftAssoc; } toString() { return `reduce ${this.prod.id}`; } equals(obj: Object) { return obj instanceof Reduce && obj.prod == this.prod; } isReduce() { return true; } } export class Accept extends { constructor(nextState: State) { super(); this.nextState = nextState; } equals(obj: Object) { return obj instanceof Accept && obj.nextState == this.nextState; } toString() { return `accept ${this.nextState.id}`; } isAccept() { return true; } } export class ParsingTable { private parsingTable = new Map<State, Map<Symbol, Operation>>() get table() { return this.parsingTable; } put(state: State, symbol: Symbol, op: Operation): void { let row = this.parsingTable.get(state); if (row == undefined) { row = new Map<Symbol, Operation>(); this.parsingTable.set(state, row); } row.set(symbol, op); } has(state: State, symbol: Symbol, 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: State, symbol: Symbol): 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: State): Symbol[] { let row = this.parsingTable.get(state); if (row == undefined) return []; return Array.from(row.keys()); } getExpectedTokens(state: State) { let expS = this.getExpectedSymbols(state); return expS.filter(s => !(s instanceof NonTerminal)); } } export interface IParser { parse(lexer: ILexer): ASTree } export class ProdsInjector { prodIdCounter = 0 inject(nt: NonTerminal, rawProds: Symbol[][]) { for (let rawProd of rawProds) { nt.addProd(this.prodIdCounter++, rawProd); } } }