UNPKG

@parser-generator/core

Version:

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

140 lines (135 loc) 5.57 kB
/******************************************************************************************************* 关于积聚 LL分析是自顶向下的,这意味者其构造AST节点的顺序也是自顶向下.在实现过程存在的问题是: 构造父节点时,所需的子节点尚未构造. 因此,需要一种机制使父节点延迟构造,只有所依赖的子节点构造完成时才构造父节点. 我将这种机制称为`积聚`. *******************************************************************************************************/ import { EOF, NIL, isNonTerminal } from "@parser-generator/definition"; import { Stack, assert } from "@light0x00/shim"; import rootDebug from "debug"; import { MismatchError } from "../utils"; let debug = rootDebug("PG:LLParser"); /** * 自定向下分析的积聚处理 #issue 关于积聚 */ class ASTAccumulator { constructor() { this.astStack = new Stack(); this.actionStack = new Stack(); } get ast() { assert(this.rootASTree != undefined, "尚未积聚完成,LL分析树尚未构造到根节点"); return this.rootASTree; } add(action, len) { assert(len > 0, "按照设计,不应该有长度为0的的产生式"); this.actionStack.push({ action, len, accLen: 0 }); } acc(token) { assert(this.actionStack.size() > 0, "动作栈为空!\n\t积聚的目的是为了确定何时触发动作(类似LR中确定句柄),如果没有动作,则积聚无意义,是为bug"); this.astStack.push(token); let top = this.actionStack.peek(); ++top.accLen; while (top.accLen == top.len) { //子节已经积聚满 可以执动作构造父节点 let { action, len } = this.actionStack.pop(); let eles = []; for (let i = 0; i < len; i++) eles.unshift(this.astStack.pop()); //栈中弹出顺序和产生式顺序相反,因此倒序添加 let ast = action(eles, this.astStack); debug("make ast " + ast); this.astStack.push(ast); //由于当前节点积聚完成 将其父节点积聚点+1 (根节点除外,因为没有父节点) if (this.actionStack.size() == 0) { debug("已生成了根节点!"); this.rootASTree = ast; break; } top = this.actionStack.peek(); ++top.accLen; } } } /* Nonrecursive LL Predictive Parsing */ export class LLParser { constructor(startSymbol, firstTable, followTable) { this.startNT = startSymbol; this.firstTable = firstTable; this.followTable = followTable; checkAmbiguity(firstTable, followTable); } parse(lexer) { let astAcc = new ASTAccumulator(); let symStack = new Stack(); symStack.push(EOF); symStack.push(this.startNT); let lookahead = lexer.next(); while (symStack.size() > 0) { let symbol = symStack.pop(); let lookaheadKey = lookahead.key(); //非终结符 if (isNonTerminal(symbol)) { let firstSet = this.firstTable.get(symbol); let followSet = this.followTable.get(symbol); let candidate_prod = firstSet.get(lookaheadKey); //如果当前非终结符存在 对lookahead的非ε推导 if (candidate_prod != null) { //将候选式符号倒序压入栈 for (let i = candidate_prod.body.length - 1; i >= 0; i--) symStack.push(candidate_prod.body[i]); astAcc.add(candidate_prod.postAction, candidate_prod.body.length); } //否则 else { //检查当前非终结符是否可推出ε, 并且lookahead存在于followSet if (firstSet.has(NIL) && followSet.has(lookaheadKey)) { astAcc.acc(NIL); //积聚 } else { throw new MismatchError(firstSet.keys(), lookahead); } } } //终结符 else { if (lookaheadKey != symbol) throw new MismatchError(symbol, lookahead); debug(`matched ${lookahead}`); if (lookahead.key() != EOF) astAcc.acc(lookahead); //积聚 lookahead = lexer.next(); } } debug("success!"); return astAcc.ast; } } /* 1. 二义性 如果一个非终结符A可以推出ε,那么必须满足 First(A) ∩ Follow(A)=∅, 否则就存在二义性 S->Aa A->a|ε First(A)={a,ε} Follow(A)={a} 预测表: +------+--------------+ | | a | +------+--------------+ |A | A->a,A->ε | +------+--------------+ 在输入串为「a」时,分别选择A->a,A->ε,前者将匹配失败,而后者匹配成功. 无法确定一个唯一的选择. */ function checkAmbiguity(firstTable, followTable) { /* issue: 1. 二义性 */ for (let [pname, firstMap] of firstTable) { if (firstMap.has(NIL)) { let followSet = followTable.get(pname); for (let v of firstMap.keys()) { if (followSet.has(v)) throw new Error(`Found ambiguity in grammar. There is conflict between the First-set(${pname}) and Follow-Set(${pname}) on ${v}`); } } } } //# sourceMappingURL=ll-parser.js.map