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

[错误处理]表达式分析器的容错

 
阅读更多

    由于语法分析模块由多个不同的分析器协同完成,当一个分析器中发现语法错误,或者仅仅是该识别的非终结符识别完成,即将返回时,该分析器会将从这里开始的终结符都扔给委托它的分析器去处理。这会导致一个问题,那就是错误会从当前分析器扩散到调用栈中的其它分析器。比如输入:

    if ( x == 1 2 ) { // other codes ...

在没有任何保护措施的情况下,表达式分析器遇到“2”时,会认为表达式识别结束,然后返回一个表达式节点,表征

    x == 1

然后将“2”传入LR分析器。接着LR分析器一看也傻眼了,怎么来了个数字,于是也开始报错。而这一切的根源也许仅仅是因为多键入了一个空格。

 

    为了尽可能地防止错误扩散,需要改造分析器,使它们能够容忍错误。就表达式分析而言,如果发现有连续两个因子出现,或者连续两个不应连续出现的运算符(可以连续出现的情况如 1 * -1),那么仍然继续表达式的识别。或者说,需要因子时遇到了运算符,或者当需要运算符时遇到了因子,当这两种非致命伤出现时,要用一种手法迷惑分析器,让它认为还可以继续分析下去。在这里给出一种很简单的方法,就是在终结符流中插入一个伪造的终结符

        /* 需要运算符时遇到了因子 struct Token* token */
        struct Token fakeOp = {token->line, PLUS, NULL, "+"};
        // 报错
        self->consumeToken(self, &fakeOp);
        self->consumeToken(self, token);

        /* 需要因子时遇到了运算符 struct Token* token */
        struct Token fakeNum = {token->line, INTEGER, NULL, "0"};
        // 报错
        self->consumeToken(self, &fakeNum);
        self->consumeToken(self, token);

首先伪造一个终结符,传入分析器,这时分析器就认了这家伙,然后再将原来由词法分析模块传入真的终结符重新来一次,这样就达到了目的。

    此外,表达式分析还有一个大敌,就是左括号太多。这一点其实也不需要什么办法去容错,只用报个错,然后把滞留在符号栈里的那些左括号忽略掉就行了。

    最后是OperationAnalyser加入容错机制后需要修改后的wrapname(consumeFactor)wrapname(consumeOperator)两个函数。它们会用到宏包括

#define isFirstFactor(x) ( ( IDENT == (x) ) ||                     \
                           (( INTEGER <= (x) ) && ( REAL >= (x) )) )
#define isFirstOperator(x) (( PLUS <= (x) ) && ( OR >= (x) ))

最好将它们放到AcceptType枚举的附近,因为它们跟AcceptType枚举中某些常量的顺序关系非常密切,如果AcceptType有修改的需要,那么就得对应地修改这两个宏。

    这里是修改后的函数

static ErrMsg wantFactor = "Incorrect expression: should have been a factor",
            wantOperator = "Incorrect expression: should have been a operator",
           excessLParent = "Excessive opening parenthese.";

static void wrapname(consumeFactor)(struct OperationAnalyser* self,
                                    struct Token* token)
{
    if(NOT == token->type) {
        self->opStack->push(self->opStack,
                            newOperator(token->type,
                                        PRIORITY[token->type],
                                        unaryOperate));
        self->needFactor = 1;
    } else if(MINUS == token->type || PLUS == token->type) {
        self->opStack->push(self->opStack,
                            newOperator(token->type, 0, unaryOperate));
        self->needFactor = 1;
    } else if(IDENT == token->type) {
        struct SyntaxAnalyser* analyser = newVariableAnalyser();
        analyserStack->push(analyserStack, analyser);
        analyser->consumeToken(analyser, token);
    } else if(INTEGER == token->type) {
        self->numStack->push(self->numStack,
                             newIntegerNode(atoi(token->image)));
        self->needFactor = 0;
    } else if(REAL == token->type) {
        self->numStack->push(self->numStack, newRealNode(atof(token->image)));
        self->needFactor = 0;
    } else if(LPARENT == token->type) {
        self->opStack->push(self->opStack,
                            newOperator(token->type, 0x7fffffff, nullOperate));
        self->needFactor = 1;
    } else {
        struct AbstractSyntaxNode* ret = (struct AbstractSyntaxNode*)
                                         (self->numStack->peek(self->numStack));
        if(NULL == ret && 1 == self->opStack->height(self->opStack)) {
            self->numStack->pop(self->numStack); // 弹出 ret
            wrapname(cleanup)(self);
            struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
                                       (analyserStack->peek(analyserStack));
            analyser->consumeNonTerminal(analyser, ret);

            analyser = (struct SyntaxAnalyser*)
                       (analyserStack->peek(analyserStack));
            analyser->consumeToken(analyser, token);
        } else {
            /* 容错处理 */
            struct Token fakeNum = {token->line, INTEGER, NULL, "0"};
            fprintf(stderr, "Before `%s' ",
                    NULL == token->image ? "End of the file." : token->image);
            fprintf(stderr, "Error @ line %d\n"
                            "    %s\n",
                            token->line, wantFactor);
            self->consumeToken(self, &fakeNum);
            self->consumeToken(self, token);
        }
    }
}

static void wrapname(consumeOperator)(struct OperationAnalyser* self,
                                      struct Token* token)
{
    int priority = PRIORITY[token->type];
    if(0 < priority && priority < PRIORITY[LPARENT]) {
        /* token 是运算符 */
        int push = 0;
        struct Operator* topOp = (struct Operator*)
                                         (self->opStack->peek(self->opStack));
        push |= (priority < topOp->priority);
        push |= (priority == topOp->priority && topOp->rightCombination);
        while(!push) {
            topOp = (struct Operator*)(self->opStack->pop(self->opStack));
            topOp->operate(topOp, self->numStack);
            topOp = (struct Operator*)(self->opStack->peek(self->opStack));
            push |= (priority < topOp->priority);
            push |= (priority == topOp->priority && topOp->rightCombination);
        }
        self->opStack->push(self->opStack, newOperator(token->type,
                                                  priority,
                                                  OPER_FUNCS[token->type]));
        self->needFactor = 1;
    } else if(RPARENT == token->type) {
        struct Operator* topOp = (struct Operator*)
                                         (self->opStack->pop(self->opStack));
        while(nullOperate != topOp->operate) {
            topOp->operate(topOp, self->numStack);
            topOp = (struct Operator*)(self->opStack->pop(self->opStack));
        }
        topOp->operate(topOp, self->numStack);
        self->needFactor = 0;
        if(0 == self->opStack->height(self->opStack)) {
            struct AbstractSyntaxNode* ret = (struct AbstractSyntaxNode*)
                                          (self->numStack->pop(self->numStack));
            wrapname(cleanup)(self);
            struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
                                         (analyserStack->peek(analyserStack));
            analyser->consumeNonTerminal(analyser, ret);

            analyser = (struct SyntaxAnalyser*)
                                         (analyserStack->peek(analyserStack));
            analyser->consumeToken(analyser, token);
            return;
        }
    } else if (isFirstFactor(token->type)) {
        /* 容错处理 */
        struct Token fakeOp = {token->line, PLUS, NULL, "+"};
        fprintf(stderr, "Before `%s' ",
                NULL == token->image ? "End of the file." : token->image);
        fprintf(stderr, "Error @ line %d\n"
                        "    %s\n",
                        token->line, e);
        self->consumeToken(self, &fakeOp);
        self->consumeToken(self, token);
    } else {
        struct AbstractSyntaxNode* ret;
        struct Operator* topOp = (struct Operator*)
                                         (self->opStack->pop(self->opStack));
        while(LPARENT != topOp->op) {
            topOp->operate(topOp, self->numStack);
            topOp = (struct Operator*)(self->opStack->pop(self->opStack));
        }
        topOp->operate(topOp, NULL); // 左括号
        ret = (struct AbstractSyntaxNode*)(self->numStack->pop(self->numStack));

        if(0 != self->opStack->height(self->opStack)) {
            fprintf(stderr, "Error @ line %d\n"
                            "    %s\n",
                            token->line, excessLParent);
        }
        wrapname(cleanup)(self);
        struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
                                          (analyserStack->peek(analyserStack));
        analyser->consumeNonTerminal(analyser, ret);

        analyser = (struct SyntaxAnalyser*)(analyserStack->peek(analyserStack));
        analyser->consumeToken(analyser, token);
    }
}

这里的错误报告部分写得有点不太好,都是硬邦邦的fprintf,以后需要将它们抽取出来专门处理。

分享到:
评论
3 楼 lwwin 2009-03-02  
哦对,我才发现,这样才是对的,先查看一下,如果是语法错误还需要继续处理的^^
2 楼 NeuronR 2009-03-02  
lwwin 写道

应该有二个地方不是很明白:

36. if(NULL == ret || 1 == self-&gt;opStack-&gt;height(self-&gt;opStack)) {
这个条件在以前是:
NULL != ret || 1 != self-&gt;opStack-&gt;height(self-&gt;opStack)
因为修改以后并不是等价的,现在只需要满足一个条件就可以了么?

37. self-&gt;numStack-&gt;pop(self-&gt;numStack); // 弹出 ret
这个之前没有,是新增加的么?能不能解释一下?

PS:我现在有点慌,说不定什么地方不一致就要调好久...

36
我错了,现在改了。
这是我改来该去的疏漏,原来的条件被颠倒过来了,虽然说本没这个必要。

37
以前在上一行,ret是pop的,现在是peek的(34行的那句)。
现在peek而不直接pop,因为如果这时发生错误,那么又得把ret压回去,然后再压入那个fakeNum;而如果没有错误发生,即该返回一个NULL,那么这时才弹出ret并返回。
1 楼 lwwin 2009-03-02  
应该有二个地方不是很明白:

36. if(NULL == ret || 1 == self->opStack->height(self->opStack)) {
这个条件在以前是:
NULL != ret || 1 != self->opStack->height(self->opStack)
因为修改以后并不是等价的,现在只需要满足一个条件就可以了么?

37. self->numStack->pop(self->numStack); // 弹出 ret
这个之前没有,是新增加的么?能不能解释一下?

PS:我现在有点慌,说不定什么地方不一致就要调好久...

相关推荐

    编译原理C语言实现词法分析程序带容错处理

    4. **错误处理**:在遇到不符合规则的输入时,词法分析器需要能够进行容错处理,例如,当遇到非法字符或未闭合的字符串或注释时,需要给出适当的错误提示。 在这个项目中,`cifa.cpp`很可能是词法分析器的源代码...

    词法分析器描述文档1

    本篇文档描述了一个词法分析器的设计与实现,重点在于如何通过正则表达式、DFA(确定有限自动机)对源代码进行解析,生成词法单元序列、常量符号表和变量符号表,并处理可能出现的错误。 首先,词法分析器需满足...

    HTML分析器——htmlparser1-4_20

    也可能探讨了性能优化,如增量解析、流式解析,以及错误处理和容错机制,以确保在面对不规范的HTML时,解析器仍能尽可能地正确工作。 在实际项目中,这样的HTML分析器可以用于各种用途,如网页抓取、内容提取、自动...

    简单的词法分析器 对基本的词法进行分析

    词法分析器的设计通常基于正则表达式或有限状态自动机(Finite State Automata, FSA)等理论。在实际实现过程中,开发者需要定义各种词法规则,并通过相应的算法来匹配这些规则。例如,一个简单的词法分析器可能包括...

    C++ Rpc远程计算

    这通常需要一个表达式解析器,例如使用递归下降解析或者词法分析器、语法分析器(如ANTLR库)来解析字符串形式的数学表达式,然后通过计算引擎执行表达式得到结果。 4. **协议设计**:在RPC中,客户端和服务器之间...

    类C语言编译原理课设报告

    - **总体架构**:该类C编译器被设计为一个分层的系统,主要包括词法分析器、语法分析器和语义分析器三个主要组件。 - **词法分析器**:负责将源代码分解成一系列有意义的单词,即标识符、关键字、运算符和常量等。 ...

    dat_parse 上午2.30.04.zip

    8. 错误处理和容错性:解析过程中可能会遇到格式错误或其他问题,好的解析器应该具有良好的错误处理机制,能够优雅地处理异常情况。 9. 并行处理:在处理大量数据时,利用多线程或多进程并行解析可以显著提高效率。...

    基于Python的Wiki百科网页分析毕业设计报告.doc

    6. **错误处理与容错**:设计合理的错误处理机制,以应对网页内容的多样性。 通过以上步骤,设计并实现的Python程序可以自动化地从Wiki百科中抽取数学公式,这对于教育、科研以及数据分析等领域都有实际的应用价值...

    C# 计算器(支持算式输入)

    4. **错误处理**:高容错性意味着计算器应该能够处理无效的输入,如未闭合的括号、除以零等错误。通过添加适当的错误检查代码,我们可以在检测到错误时向用户提供清晰的错误提示,而不是程序崩溃。 5. **数学运算**...

    CS236_W15:使用 MapReduce 的天气分析器

    总之,"CS236_W15:使用MapReduce的天气分析器"项目是一个实践性的学习任务,旨在让学生掌握如何利用Java和Hadoop MapReduce处理大规模天气数据,进行统计分析和挖掘。通过这个项目,不仅可以提升编程能力,还能深入...

    HTML网页内容解析器源码

    7. **错误处理**:HTML文档可能存在语法错误或不规范的情况,解析器需要有良好的容错机制,尽可能地解析出有效内容。 8. **性能优化**:由于HTML文档可能很大,解析器的设计需要考虑性能,避免不必要的内存消耗和...

    apache-solr-ref-guide-7.4(官方英文-文字版本)

    2. **分析器**:详细说明了 Solr 中可用的不同类型的分析器,包括标准分析器、简单分析器和自定义分析器等。 3. **关于分词器**:解释了分词器的作用和工作原理,并列举了几种常用的分词器类型。 4. **关于过滤...

    易语言HTML过滤

    6. **错误处理**:由于HTML文档可能存在不规范的情况,因此在过滤过程中,需要考虑异常处理和容错机制,确保程序的稳定性。 在提供的压缩包文件"易语言HTML源码过滤"中,可能包含了一个或多个示例程序,演示了如何...

    beautifulsoup4-4.1.1

    错误处理与容错性** BeautifulSoup4的设计目标之一就是处理不规范的HTML,因此它在遇到语法错误时具有一定的容错性。它可以尝试修复破损的标签结构,使得开发者能从不完美的网页中提取信息。 **7. 应用场景** ...

    High Performance Spark

    10. **错误处理与容错**:理解Spark的容错机制,如何捕获和处理错误,以及如何配置检查点和故障恢复策略。 11. **监控与调试**:通过Spark的Web UI、日志和metrics系统监控应用程序性能,定位和解决问题。 通过...

    开源项目-jeffail-benthos.zip

    3. **容错与重试机制**:Benthos具有内置的错误处理策略,包括重试、死信队列和幂等性处理,确保数据不丢失。 4. **持久化存储**:Benthos支持将数据流持久化到本地磁盘或云存储,即使在服务中断后也能恢复处理状态...

    websphere 分析工具

    此外,利用文本编辑器(如Notepad++)或IDE(如Eclipse、IntelliJ IDEA)的搜索和正则表达式功能,也能快速定位关键信息。 文件列表中的`ha.cfg`可能是高可用性(High Availability, HA)配置文件,用于设置...

    yeslist_fill_gallon:是名单挑战,加满一加仑水

    7. 错误处理和容错性:在处理大数据时,必须考虑错误处理和系统的容错性,以应对可能出现的异常情况。 8. 数据安全:保护数据的安全性和隐私,遵循数据合规性原则。 综上所述,"yeslist_fill_gallon"虽然不是一个...

    htmlparser

    3. **错误处理**:HTMLParser对HTML的非标准写法有很好的容错性,它能够处理不闭合的标签、缺失的引号等常见问题,确保在不规范的HTML文档中也能正常工作。 4. **标签和属性处理**:HTMLParser提供了丰富的API来...

    PyPI 官网下载 | lxml-2.2.5.tar.gz

    7. **错误处理**:`lxml` 提供了丰富的错误处理机制,可以捕获并处理解析、验证等过程中的错误。 在实际应用中,`lxml` 常用于 web 抓取、数据提取、XML 数据交换、文档生成等领域。其丰富的功能和高效性能使得它...

Global site tag (gtag.js) - Google Analytics