框架接口介绍
LRTable: LR 分析表
所谓 LR 分析表, 实际上就是一张类似自动机的状态转移图, 当在当前状态遇到某某符号时便执行动作并转移. 规约时就根据 "Action 表" 里写的产生式生成非终结符, 然后将生成的非终结符作为 "输入符号" 根据 "Goto 表" 来确定转移; 移入时就直接根据 "Action 表" 转移到对应状态.
我们采用一种更接近邻接表而非邻接矩阵的方式来提供对 ACTION 与 GOTO 的访问; 虽然, 我们亦在 LRTable 中提供了 "更像邻接矩阵" 的访问接口:
class LRTable {
/**
* 根据当前状态与当前词法单元获取对应动作
* @param status 当前状态
* @param token 当前词法单元
* @return 应采取的动作
*/
public Action getAction(Status status, Token token) {
final var tokenKind = token.getKind();
return status.getAction(tokenKind);
}
/**
* 根据当前状态与规约到非终结符获得应转移到的状态
* @param status 当前状态
* @param nonTerminal 规约出的非终结符
* @return 应转移到的状态
*/
public Status getGoto(Status status, NonTerminal nonTerminal) {
return status.getGoto(nonTerminal);
}
/**
* @return 起始状态
*/
public Status getInit() {
// return initStatus;
return statusInIndexOrder.get(0);
}
}
理论上一个 LRTable 只需要保存起始状态 (initStatus) 即可. 但是为了方便将整个表输出为 csv 查看, 我们于 LRTable 中保存了一些多余的状态, 而你的实现 不应该 使用这些信息:
class LRTable {
private final List<Status> statusInIndexOrder;
private final List<TokenKind> terminals;
private final List<NonTerminal> nonTerminals;
}
Status: 状态
class Status {
private Map<TokenKind, Action> action;
private Map<NonTerminal, Status> goto_;
}
class Status {
// API
}
Action: 动作
enum ActionKind {
Shift, Reduce, Accept, Error
}
class Action {
private ActionKind kind;
private Production production; // nullable, only for Reduce
private Status status; // nullable, only for Shift
}
class Action {
public Production getProduction() {
}
public Status getStatus() {
}
}