当一个分析器A调用另一个分析器B时,如果称A处于B的“外层”,那么除开测试文件中产生的那个FakeDefaultAnalyser之外,LR分析器是整个语法分析结构中最外层的语法分析器。因此当其它分析器有无法处理的错误时,这些烂摊子都得它来收拾。比如变量分析中可能有冗余的反方括号,表达式分析中可能有冗余的反圆括号,等等,甚至包括其它的LR分析器也有可能把错误扔给更外层的LR分析器。这样一来,LR分析器中至少得对付这样一些错误
static ErrMsg excessiveRParent = "Excessive close parenthese.",
excessiveRBracket = "Excessive close bracket.",
missingRBrace = "Missing close brace.";
此外,应该还要考虑一些简单的内部错误,比如掉分号,if语句或while语句条件掉了括号之类的static ErrMsg unknownSyntaxError = "Unknown error.",
static ErrMsg missingLParent = "Missing opening parenthese.",
missingRParent = "Missing close parenthese.",
missingRBrace = "Missing close brace.";
如果还有其它类型的错误,又不能抛给外层分析器,这些错误就统统归类为
static ErrMsg unknownSyntaxError = "Unknown error.";
好了,下面是错误处理的正文。
一个错误处理方案跟OperationAnalyser很类似,那就是增加一些fakeXXX,比如fakeShift,fakeReduce之类的。不过坦白说,LR分析器状态分析表太大,一个个地加太麻烦,如果也弄个如同fakeShiftFirstSentence之类的,那么就会一不小心跟正常的那些移进规约什么的名字冲突,所以这并不是一个好办法。
于是,我们得转头寻求一个较为简单,但是很可能比较混乱的方法,专门弄一个函数来处理错误。这里引入这样几个函数
static void wrapname(errorHandler)(struct LRAnalyser*, struct Token*);
static void wrapname(fatalError)(struct LRAnalyser*, struct Token*);
前面一个就是错误处理器,后面一个比较纠结,那就是当发生了错误处理器难以理解的unknownSyntaxError时,就认为发生了fatalError,然后就把处理权转移给这个函数。现在先来看看wrapname(errorHandler)的一个实现方式。
static void wrapname(errorHandler)(struct LRAnalyser* self, struct Token* token)
{
struct LRState* currentState = (struct LRState*)
(self->stateStack->peek(self->stateStack));
LRStateFamily stateNo = currentState - jerryLRStates;
if(IfElseBranch_1 == stateNo ||
WhileLoop_1 == stateNo) {
syntaxError(missingLParent, token);
struct Token fakeLParent = {token->line, LPARENT, NULL, "("};
self->consumeToken(self, &fakeLParent);
self->consumeToken(self, token);
} else if(IfElseBranch_3 == stateNo ||
WhileLoop_3 == stateNo) {
syntaxError(missingRParent, token);
struct Token fakeRParent = {token->line, RPARENT, NULL, ")"};
self->consumeToken(self, &fakeRParent);
self->consumeToken(self, token);
} else if(Sentence_LBraceBasicBlockRBrace_2 == stateNo
&& END == token->type) {
syntaxError(missingRBrace, token);
struct Token fakeRBrace = {token->line, RBRACE, NULL, "}"};
self->consumeToken(self, &fakeRBrace);
self->consumeToken(self, token);
} else {
if(VariableRegister_VariableInitialization_1 == stateNo ||
DeclarationVariableRegister == stateNo ||
Sentence_IOAssignmentEoS_2 == stateNo ||
Sentence_AssignmentEoS_1 == stateNo ||
IfElseBranch_4 == stateNo ||
WhileLoop_4 == stateNo) {
if(RPARENT == token->type) {
syntaxError(excessiveRParent, token);
return;
} else if(RBRACKET == token->type) {
syntaxError(excessiveRBracket, token);
return;
}
}
if(DeclarationVariableRegister == stateNo ||
Sentence_IOAssignmentEoS_2 == stateNo ||
Sentence_AssignmentEoS_1 == stateNo ||
Sentence_BreakEoS_1 == stateNo) {
syntaxError(missingSemicolon, token);
struct Token fakeEoS = {token->line, EOS, NULL, ";"};
self->consumeToken(self, &fakeEoS);
self->consumeToken(self, token);
return;
}
wrapname(fatalError)(self, token); // 致命错误
}
}
进入函数后,首先用两句话获取当前状态栈栈顶的状态编号,这个编号在后面某些分支中可能会用到。接下来就是一堆很恶心的分支。第一个分支的条件成立的话,那么发生错误很可能是因为漏掉了条件外面的左括号,于是在分支中首先报错,接着故伎重演构造一个假的左括号哄一哄LR分析器,然后再继续分析。第二个分支与第一个类似。第三个分支与前两个不太一样,它验证当一个基本块返回后,紧接下来的不是一个反话括号,而是文件结束符号,所以这时就判定缺少一个反花括号。现在终于来到了最后一个,看起来肮脏腐臭的块(如果你有更好的实现方式,请不吝赐教~)。
本来这个块也得分开写的,然而这两种情况中有些状态是相同的,分开写就会很麻烦,所以最后我把它们整成这个样子了。第一分支语句条件中的那些状态都是表达式返回以后Goto的状态,因此都有可能有冗余反括号,在这个子块里面就检查当前这个符号是不是反括号,然后作出对应的错误处理,并返回;但是这些检查如果失败,那么检查会进入下一分支。而第二分支语句条件中的那些状态都是应该遇到<EOS>的状态,然而这里出错就说明它们没有遇到,所以塞一个假的分号去并返回。如果这些拦截都失效了,那么就只好认为这是一个致命错误了。
产生了一个致命错误,往往是因为程序员做了一些奇妙的事情,比如删除了多行注释的开头但是忘记删除其它部分。于是我们得采取一些极端的手段,不如干脆把接下来的所有终结符跳过——直到某个特殊的符号,比如句子结尾的分号,或者反花括号。这样就需要一个过滤器,当发生致命错误时,就替换掉LR分析器的consumeToken成员函数为这个东西
static void wrapname(skipToken)(void* self, struct Token* token)
{
if(EOS == token->type) {
((struct LRAnalyser*)self)->consumeToken = wrapname(consumeToken); // 重置
} else if(RBRACE == token->type || END == token->type) {
wrapname(consumeToken)(self, token); // 当前基本块结束,这样调用之后会让当前分析器弹出栈
}
}
那么,wrapname(fatalError)就可以这样来写
static void wrapname(fatalError)(struct LRAnalyser* self, struct Token* t)
{
syntaxError(unknownSyntaxError, t);
self->stateStack->pops(self->stateStack,
self->stateStack->height(self->stateStack) - 1,
NULL);
struct AbstractSyntaxNode* node;
while(0 < self->symbolStack->height(self->symbolStack)) {
node = (struct AbstractSyntaxNode*)
(self->symbolStack->pop(self->symbolStack));
node->delNode(node);
}
self->consumeToken = wrapname(skipToken);
self->consumeToken(self, t);
}
有一点重要的是,进入这个函数之后首先会重置LR分析器的状态栈和符号栈,这样避免以后遇到句子结束符之后重新开始分析时,使得当前栈里的东西影响到后续分析——反正现在已经有错误出现了,所以栈里面的东西都算是废品了。
最后还有一点需要修改的,那就是wrapname(consumeToken)
static void wrapname(consumeToken)(void* self, struct Token* token)
{
struct LRAnalyser* lra = (struct LRAnalyser*)self;
struct LRState* lrState = (struct LRState*)
(lra->stateStack->peek(lra->stateStack));
if(NULL != lrState->actions[token->type]) {
lrState->actions[token->type](lra, token);
} else {
struct AbstractSyntaxNode* ret;
struct LRState* topState = (struct LRState*)
(lra->stateStack->peek(lra->stateStack)); // pop -> peek
if((jerryLRStates + LRState0 == topState && RBRACE == token->type)
|| jerryLRStates + LRState1 == topState) {
ret = lra->symbolStack->pop(lra->symbolStack);
wrapname(cleanup)(lra);
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack->peek(analyserStack));
analyser->consumeNonTerminal(analyser, ret);
analyser = (struct SyntaxAnalyser*)
(analyserStack->peek(analyserStack));
analyser->consumeToken(analyser, token);
} else {
wrapname(errorHandler)(lra, token); // here goes to the error handler
}
}
}
有一个小地方,就是上面的注释有 pop -> peek 的那一行,以前是弹栈而现在只是看一看,这跟变量分析器那个改动很类似,因为现在揉进了容错处理,错误处理会尽可能地纠正它,所以如果这时弹栈是不明智的。
附 lr-analyser.c 修改后的代码,只包含分析器部分,分析预测表没有改变
#include<stdlib.h>
#include"datastruct.h"
#include"syntax-analyser.h"
#include"syntax-node.h"
#include"COOL/MemoryAllocationDebugger.h"
#include"COOL/Stack/Stack.h"
extern struct Stack* analyserStack;
extern struct SyntaxAnalyser* newVariableAnalyser(void);
extern struct SyntaxAnalyser* newOperationAnalyser(void);
extern void syntaxError(ErrMsg, struct Token*);
static ErrMsg unknownSyntaxError = "Unknown error.",
missingLParent = "Missing opening parenthese.",
missingRParent = "Missing close parenthese.",
excessiveRParent = "Excessive close parenthese.",
excessiveRBracket = "Excessive close bracket.",
missingSemicolon = "Missing Semicolon at the end of sentence.",
missingRBrace = "Missing close brace.";
#define wrapname(name) name ## _LRAnalyser
static void wrapname(consumeNonTerminal)(void*, struct AbstractSyntaxNode*);
static void wrapname(consumeToken)(void*, struct Token*);
static void wrapname(cleanup)(struct LRAnalyser*);
static void wrapname(errorHandler)(struct LRAnalyser*, struct Token*);
static void wrapname(fatalError)(struct LRAnalyser*, struct Token*);
static void wrapname(skipToken)(void*, struct Token*);
static void wrapname(cleanup)(struct LRAnalyser* self)
{
struct AbstractSyntaxNode* node;
self->symbolStack->finalize(self->symbolStack);
self->stateStack->finalize(self->stateStack);
analyserStack->pop(analyserStack);
revert(self);
}
static void wrapname(consumeToken)(void* self, struct Token* token)
{
struct LRAnalyser* lra = (struct LRAnalyser*)self;
struct LRState* lrState = (struct LRState*)
(lra->stateStack->peek(lra->stateStack));
if(NULL != lrState->actions[token->type]) {
lrState->actions[token->type](lra, token);
} else {
// printf("Interrupt LR\n");
struct AbstractSyntaxNode* ret;
struct LRState* topState = (struct LRState*)
(lra->stateStack->peek(lra->stateStack)); // pop to peek
if((jerryLRStates + LRState0 == topState && RBRACE == token->type)
|| jerryLRStates + LRState1 == topState) {
ret = lra->symbolStack->pop(lra->symbolStack);
wrapname(cleanup)(lra);
// printf("Cleaned...\n");
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack->peek(analyserStack));
analyser->consumeNonTerminal(analyser, ret);
analyser = (struct SyntaxAnalyser*)
(analyserStack->peek(analyserStack));
analyser->consumeToken(analyser, token);
} else {
wrapname(errorHandler)(lra, token);
}
}
}
static void wrapname(consumeNonTerminal)(void* self,
struct AbstractSyntaxNode* nonTerminal)
{
struct LRAnalyser* lra = (struct LRAnalyser*)self;
struct LRState* currentState = (struct LRState*)
(lra->stateStack->peek(lra->stateStack));
lra->symbolStack->push(lra->symbolStack, nonTerminal);
lra->stateStack->push(lra->stateStack,
currentState->gotoes[NR_GOTO_TABLE_COLUMN]);
// printf(" Delegate returns. Goto %d\n", currentState->gotoes[NR_GOTO_TABLE_COLUMN] - jerryLRStates);
}
struct SyntaxAnalyser* newLRAnalyser(void)
{
struct LRAnalyser* analyser = (struct LRAnalyser*)
allocate(sizeof(struct LRAnalyser));
analyser->symbolStack = newStack();
analyser->stateStack = newStack();
analyser->stateStack->push(analyser->stateStack, jerryLRStates);
analyser->consumeToken = wrapname(consumeToken);
analyser->consumeNonTerminal = wrapname(consumeNonTerminal);
return (struct SyntaxAnalyser*)analyser;
}
static void wrapname(errorHandler)(struct LRAnalyser* self, struct Token* token)
{
struct LRState* currentState = (struct LRState*)
(self->stateStack->peek(self->stateStack));
LRStateFamily stateNo = currentState - jerryLRStates;
if(IfElseBranch_1 == stateNo ||
WhileLoop_1 == stateNo) {
syntaxError(missingLParent, token);
struct Token fakeLParent = {token->line, LPARENT, NULL, "("};
self->consumeToken(self, &fakeLParent);
self->consumeToken(self, token);
} else if(IfElseBranch_3 == stateNo ||
WhileLoop_3 == stateNo) {
syntaxError(missingRParent, token);
struct Token fakeRParent = {token->line, RPARENT, NULL, ")"};
self->consumeToken(self, &fakeRParent);
self->consumeToken(self, token);
} else if(Sentence_LBraceBasicBlockRBrace_2 == stateNo
&& END == token->type) {
syntaxError(missingRBrace, token);
struct Token fakeRBrace = {token->line, RBRACE, NULL, "}"};
self->consumeToken(self, &fakeRBrace);
self->consumeToken(self, token);
} else {
if(VariableRegister_VariableInitialization_1 == stateNo ||
DeclarationVariableRegister == stateNo ||
Sentence_IOAssignmentEoS_2 == stateNo ||
Sentence_AssignmentEoS_1 == stateNo ||
IfElseBranch_4 == stateNo ||
WhileLoop_4 == stateNo) {
if(RPARENT == token->type) {
syntaxError(excessiveRParent, token);
return;
} else if(RBRACKET == token->type) {
syntaxError(excessiveRBracket, token);
return;
}
}
if(DeclarationVariableRegister == stateNo ||
Sentence_IOAssignmentEoS_2 == stateNo ||
Sentence_AssignmentEoS_1 == stateNo ||
Sentence_BreakEoS_1 == stateNo) {
syntaxError(missingSemicolon, token);
struct Token fakeEoS = {token->line, EOS, NULL, ";"};
self->consumeToken(self, &fakeEoS);
self->consumeToken(self, token);
return;
}
wrapname(fatalError)(self, token);
}
}
static void wrapname(fatalError)(struct LRAnalyser* self, struct Token* t)
{
syntaxError(unknownSyntaxError, t);
self->stateStack->pops(self->stateStack,
self->stateStack->height(self->stateStack) - 1,
NULL);
struct AbstractSyntaxNode* node;
while(0 < self->symbolStack->height(self->symbolStack)) {
node = (struct AbstractSyntaxNode*)
(self->symbolStack->pop(self->symbolStack));
node->delNode(node);
}
self->consumeToken = wrapname(skipToken);
self->consumeToken(self, t);
}
static void wrapname(skipToken)(void* self, struct Token* token)
{
if(EOS == token->type) {
((struct LRAnalyser*)self)->consumeToken = wrapname(consumeToken);
} else if(RBRACE == token->type || END == token->type) {
wrapname(consumeToken)(self, token);
}
}
#undef wrapname
分享到:
相关推荐
这个文本文件可能包含了额外的代码、注释或者对LR分析器更深入的讨论,例如优化技巧、错误处理策略或者是与其他解析技术的比较。 在学习和使用LR分析器时,关键在于理解其核心算法——如何通过分析表来决定何时移进...
在这个课设中,我们将实现一个带有错误处理功能的LR分析器,以处理输入的符号串并根据语法规则进行分析。 首先,程序中定义了一个二维数组`fenxi_list`,这个数组通常用来存储LR分析表的各个状态转移和动作。每个...
LR分析器是一种自底向上的解析技术,常用于编译器设计,用于将源代码解析成抽象语法树(AST),从而进行后续的编译或解释工作。 LR语法分析器的全称是“左到右扫描,右most归约”的分析器。"L"代表Left-to-right,...
LR分析器是一种自底向上的解析方法,它通过构建一个分析表来决定在何时进行何种动作,如接受、移进或归约。LR分析器分为几种类型,包括LR(0)、SLR(1)、LR(1)等,其中SLR(1)是LR(0)的增强版,加入了预测信息。 LR(0)...
LR 语法分析器的优点是可以对复杂的语法规则进行分析,并且可以处理 Ambiguous 语法。 在 LR 语法分析器中,需要定义一个分析表(AnalyzeChart),该表用于存储语法规则的分析结果。分析表的每一行对应一个语法规则...
LR0分析器是最简单的LR分析器,它不考虑看前一位信息,仅根据当前状态和输入符号决定下一步动作。LR0分析器的核心是构造LR0状态机,也称为项集构造。 4. **LR1分析器**: LR1分析器是在LR0基础上引入了看前一位...
LR分析器是编译原理中的一个重要概念,它主要用于解析上下文无关文法的语法结构。LR分析器采用自上而下的方式对输入的字符序列进行分析,从文法的起始符号开始,尝试构建出一个最右推导的逆过程。在LR分析过程中,它...
2. 对于任意给定的输入串(词法记号流)进行语法分析,要求采用LR分析器来完成。手工构造LR分析表,利用移进-归约分析算法(P69 图3.12)输出(P70 表3.8)对应的动作部分。如: 输入:id*+id/(id+id)# 输出:移进 ...
在"第四次上机"这个文件中,可能包含了LR算法的Java实现代码,可能涉及了LR分析表的生成、解析栈的操作以及错误处理的相关逻辑。通过阅读和理解这段代码,你可以深入掌握LR算法的工作原理,了解如何在实际项目中应用...
本实验旨在通过实践来理解和掌握LR分析器的基本原理和实现过程。下面将详细阐述LR分析器的工作原理、设计方法以及其实现的关键步骤。 LR分析器基于右递归的上下文无关文法,其名称中的“L”代表从左到右扫描输入,...
LR分析器的设计通常包括以下步骤: 1. **定义上下文无关文法**:首先,我们需要一个上下文无关文法(CFG)来描述目标语言的结构。这个文法由一组产生式组成,每条产生式描述了一个非终结符如何被其他非终结符或终结...
这通常涉及创建LR1分析表,定义状态转移函数,以及编写处理输入符号和错误处理的代码。此外,为了便于调试和测试,还需要设计合适的输入语句和测试用例。 4. LR1分析器的局限性:虽然LR1分析器可以处理更广泛的上...
这种方法增强了LR分析器的灵活性,但仍然不能处理所有无二义文法。 3. LR(1)分析: LR(1)分析比SLR(1)更进一步,允许分析器在做决策时看更多的输入符号(但仍然是有限的,K=1)。LR(1)分析器的项目集不仅包含结束...
LR分析器通过构建一个状态机,称为LR分析表,来进行解析。这个表由一系列状态和转移组成,每个状态代表了文法的一个部分,而转移则对应于文法的产生式。当解析器读取输入符号时,会根据当前状态和输入符号来确定下一...
LR(0)语法分析器是一种在编译原理中常见的解析技术,主要应用于处理上下文无关文法。这个程序是用C语言编写的,因此具备高度的可移植性和效率。 LR(0)分析器的核心思想是基于状态转移表,通过自底向上的方式逐层解析...
LR分析器的名字来源于“Left-to-right scanning”(从左向右读取输入)和“Rightmost derivation”(最右推导)。这里的“1”代表了该分析器具有一个向前看的符号(Lookahead symbol)。 LR(1)分析器的核心在于构造...
LR分析器特别适用于处理复杂的语言结构,如编程语言,它能够有效地构建出语法树或执行相应的语义动作。 #### LR分析器的基本概念与工作原理 1. **LR分析表**:是LR分析器的核心组件之一,由状态转换表(GOTO表)和...
《Python实现LR_0分析器(带UI界面)——深入解析与实践》 在编程语言的编译原理中,LR_0分析器是一种用于解析源代码的重要工具,它基于上下文无关文法(Context-Free Grammar, CFG)进行操作。本文将深入探讨如何...
LR分析器使用一个状态栈和一个输入符号序列来解析输入。每次读取输入符号后,分析器会根据当前栈顶状态和输入符号查询分析表,决定下一步操作(移进、归约、接受或报错)。分析表通常由Action表和Goto表组成,其中...
LR0分析器,全称为无回溯的零状态LR分析器,是LR(0)分析器的一种简化形式,主要用于解决上下文无关文法的解析问题。 LR0分析器的工作原理基于状态机模型,它通过构建一个有限的状态转移图(也称为LR0状态机或项集...