`
NeuronR
  • 浏览: 59796 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

[错误处理]LR分析器的错误处理

 
阅读更多

    当一个分析器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,比如fakeShiftfakeReduce之类的。不过坦白说,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-fenxi.rar_LR_LR 分析器_LR分析器_lr 分析

    这个文本文件可能包含了额外的代码、注释或者对LR分析器更深入的讨论,例如优化技巧、错误处理策略或者是与其他解析技术的比较。 在学习和使用LR分析器时,关键在于理解其核心算法——如何通过分析表来决定何时移进...

    带出错处理的LR分析器总控程序的实现

    在这个课设中,我们将实现一个带有错误处理功能的LR分析器,以处理输入的符号串并根据语法规则进行分析。 首先,程序中定义了一个二维数组`fenxi_list`,这个数组通常用来存储LR分析表的各个状态转移和动作。每个...

    编译原理实验LR语法分析器

    LR分析器是一种自底向上的解析技术,常用于编译器设计,用于将源代码解析成抽象语法树(AST),从而进行后续的编译或解释工作。 LR语法分析器的全称是“左到右扫描,右most归约”的分析器。"L"代表Left-to-right,...

    LR.zip_LR分析_LR分析器

    LR分析器是一种自底向上的解析方法,它通过构建一个分析表来决定在何时进行何种动作,如接受、移进或归约。LR分析器分为几种类型,包括LR(0)、SLR(1)、LR(1)等,其中SLR(1)是LR(0)的增强版,加入了预测信息。 LR(0)...

    LR语法分析器

    LR 语法分析器的优点是可以对复杂的语法规则进行分析,并且可以处理 Ambiguous 语法。 在 LR 语法分析器中,需要定义一个分析表(AnalyzeChart),该表用于存储语法规则的分析结果。分析表的每一行对应一个语法规则...

    LR1分析器代码实现

    LR0分析器是最简单的LR分析器,它不考虑看前一位信息,仅根据当前状态和输入符号决定下一步动作。LR0分析器的核心是构造LR0状态机,也称为项集构造。 4. **LR1分析器**: LR1分析器是在LR0基础上引入了看前一位...

    LR分析器总控程序的实现

    LR分析器是编译原理中的一个重要概念,它主要用于解析上下文无关文法的语法结构。LR分析器采用自上而下的方式对输入的字符序列进行分析,从文法的起始符号开始,尝试构建出一个最右推导的逆过程。在LR分析过程中,它...

    LR分析器,带纠错功能

    2. 对于任意给定的输入串(词法记号流)进行语法分析,要求采用LR分析器来完成。手工构造LR分析表,利用移进-归约分析算法(P69 图3.12)输出(P70 表3.8)对应的动作部分。如: 输入:id*+id/(id+id)# 输出:移进 ...

    LR算法解析及错误处理

    在"第四次上机"这个文件中,可能包含了LR算法的Java实现代码,可能涉及了LR分析表的生成、解析栈的操作以及错误处理的相关逻辑。通过阅读和理解这段代码,你可以深入掌握LR算法的工作原理,了解如何在实际项目中应用...

    编译原理LR简单语法分析器

    本实验旨在通过实践来理解和掌握LR分析器的基本原理和实现过程。下面将详细阐述LR分析器的工作原理、设计方法以及其实现的关键步骤。 LR分析器基于右递归的上下文无关文法,其名称中的“L”代表从左到右扫描输入,...

    LR语法分析器 编译原理课程设计 源码

    LR分析器的设计通常包括以下步骤: 1. **定义上下文无关文法**:首先,我们需要一个上下文无关文法(CFG)来描述目标语言的结构。这个文法由一组产生式组成,每条产生式描述了一个非终结符如何被其他非终结符或终结...

    编译原理课程设计报告\LR1语法分析器自动识别.rar

    这通常涉及创建LR1分析表,定义状态转移函数,以及编写处理输入符号和错误处理的代码。此外,为了便于调试和测试,还需要设计合适的输入语句和测试用例。 4. LR1分析器的局限性:虽然LR1分析器可以处理更广泛的上...

    编译原理的LR分析法

    这种方法增强了LR分析器的灵活性,但仍然不能处理所有无二义文法。 3. LR(1)分析: LR(1)分析比SLR(1)更进一步,允许分析器在做决策时看更多的输入符号(但仍然是有限的,K=1)。LR(1)分析器的项目集不仅包含结束...

    LR(1).rar_LR_LR 1_LR(1) java_LR分析_LR分析器

    LR分析器通过构建一个状态机,称为LR分析表,来进行解析。这个表由一系列状态和转移组成,每个状态代表了文法的一个部分,而转移则对应于文法的产生式。当解析器读取输入符号时,会根据当前状态和输入符号来确定下一...

    LR(0)语法分析器程序

    LR(0)语法分析器是一种在编译原理中常见的解析技术,主要应用于处理上下文无关文法。这个程序是用C语言编写的,因此具备高度的可移植性和效率。 LR(0)分析器的核心思想是基于状态转移表,通过自底向上的方式逐层解析...

    LR(1).rar_LR c++_LR分析器_LR文法_LR(1)分析器

    LR分析器的名字来源于“Left-to-right scanning”(从左向右读取输入)和“Rightmost derivation”(最右推导)。这里的“1”代表了该分析器具有一个向前看的符号(Lookahead symbol)。 LR(1)分析器的核心在于构造...

    用c++实现LR语法分析器

    LR分析器特别适用于处理复杂的语言结构,如编程语言,它能够有效地构建出语法树或执行相应的语义动作。 #### LR分析器的基本概念与工作原理 1. **LR分析表**:是LR分析器的核心组件之一,由状态转换表(GOTO表)和...

    python实现LR_0分析器(带ui界面)

    《Python实现LR_0分析器(带UI界面)——深入解析与实践》 在编程语言的编译原理中,LR_0分析器是一种用于解析源代码的重要工具,它基于上下文无关文法(Context-Free Grammar, CFG)进行操作。本文将深入探讨如何...

    一种LR语法分析中的错误恢复方法

    LR分析器使用一个状态栈和一个输入符号序列来解析输入。每次读取输入符号后,分析器会根据当前栈顶状态和输入符号查询分析表,决定下一步操作(移进、归约、接受或报错)。分析表通常由Action表和Goto表组成,其中...

    LR0语法分析器.zip

    LR0分析器,全称为无回溯的零状态LR分析器,是LR(0)分析器的一种简化形式,主要用于解决上下文无关文法的解析问题。 LR0分析器的工作原理基于状态机模型,它通过构建一个有限的状态转移图(也称为LR0状态机或项集...

Global site tag (gtag.js) - Google Analytics