由于语法分析模块由多个不同的分析器协同完成,当一个分析器中发现语法错误,或者仅仅是该识别的非终结符识别完成,即将返回时,该分析器会将从这里开始的终结符都扔给委托它的分析器去处理。这会导致一个问题,那就是错误会从当前分析器扩散到调用栈中的其它分析器。比如输入:
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,以后需要将它们抽取出来专门处理。
分享到:
相关推荐
4. **错误处理**:在遇到不符合规则的输入时,词法分析器需要能够进行容错处理,例如,当遇到非法字符或未闭合的字符串或注释时,需要给出适当的错误提示。 在这个项目中,`cifa.cpp`很可能是词法分析器的源代码...
本篇文档描述了一个词法分析器的设计与实现,重点在于如何通过正则表达式、DFA(确定有限自动机)对源代码进行解析,生成词法单元序列、常量符号表和变量符号表,并处理可能出现的错误。 首先,词法分析器需满足...
也可能探讨了性能优化,如增量解析、流式解析,以及错误处理和容错机制,以确保在面对不规范的HTML时,解析器仍能尽可能地正确工作。 在实际项目中,这样的HTML分析器可以用于各种用途,如网页抓取、内容提取、自动...
词法分析器的设计通常基于正则表达式或有限状态自动机(Finite State Automata, FSA)等理论。在实际实现过程中,开发者需要定义各种词法规则,并通过相应的算法来匹配这些规则。例如,一个简单的词法分析器可能包括...
这通常需要一个表达式解析器,例如使用递归下降解析或者词法分析器、语法分析器(如ANTLR库)来解析字符串形式的数学表达式,然后通过计算引擎执行表达式得到结果。 4. **协议设计**:在RPC中,客户端和服务器之间...
- **总体架构**:该类C编译器被设计为一个分层的系统,主要包括词法分析器、语法分析器和语义分析器三个主要组件。 - **词法分析器**:负责将源代码分解成一系列有意义的单词,即标识符、关键字、运算符和常量等。 ...
8. 错误处理和容错性:解析过程中可能会遇到格式错误或其他问题,好的解析器应该具有良好的错误处理机制,能够优雅地处理异常情况。 9. 并行处理:在处理大量数据时,利用多线程或多进程并行解析可以显著提高效率。...
6. **错误处理与容错**:设计合理的错误处理机制,以应对网页内容的多样性。 通过以上步骤,设计并实现的Python程序可以自动化地从Wiki百科中抽取数学公式,这对于教育、科研以及数据分析等领域都有实际的应用价值...
4. **错误处理**:高容错性意味着计算器应该能够处理无效的输入,如未闭合的括号、除以零等错误。通过添加适当的错误检查代码,我们可以在检测到错误时向用户提供清晰的错误提示,而不是程序崩溃。 5. **数学运算**...
总之,"CS236_W15:使用MapReduce的天气分析器"项目是一个实践性的学习任务,旨在让学生掌握如何利用Java和Hadoop MapReduce处理大规模天气数据,进行统计分析和挖掘。通过这个项目,不仅可以提升编程能力,还能深入...
7. **错误处理**:HTML文档可能存在语法错误或不规范的情况,解析器需要有良好的容错机制,尽可能地解析出有效内容。 8. **性能优化**:由于HTML文档可能很大,解析器的设计需要考虑性能,避免不必要的内存消耗和...
2. **分析器**:详细说明了 Solr 中可用的不同类型的分析器,包括标准分析器、简单分析器和自定义分析器等。 3. **关于分词器**:解释了分词器的作用和工作原理,并列举了几种常用的分词器类型。 4. **关于过滤...
6. **错误处理**:由于HTML文档可能存在不规范的情况,因此在过滤过程中,需要考虑异常处理和容错机制,确保程序的稳定性。 在提供的压缩包文件"易语言HTML源码过滤"中,可能包含了一个或多个示例程序,演示了如何...
错误处理与容错性** BeautifulSoup4的设计目标之一就是处理不规范的HTML,因此它在遇到语法错误时具有一定的容错性。它可以尝试修复破损的标签结构,使得开发者能从不完美的网页中提取信息。 **7. 应用场景** ...
10. **错误处理与容错**:理解Spark的容错机制,如何捕获和处理错误,以及如何配置检查点和故障恢复策略。 11. **监控与调试**:通过Spark的Web UI、日志和metrics系统监控应用程序性能,定位和解决问题。 通过...
3. **容错与重试机制**:Benthos具有内置的错误处理策略,包括重试、死信队列和幂等性处理,确保数据不丢失。 4. **持久化存储**:Benthos支持将数据流持久化到本地磁盘或云存储,即使在服务中断后也能恢复处理状态...
此外,利用文本编辑器(如Notepad++)或IDE(如Eclipse、IntelliJ IDEA)的搜索和正则表达式功能,也能快速定位关键信息。 文件列表中的`ha.cfg`可能是高可用性(High Availability, HA)配置文件,用于设置...
7. 错误处理和容错性:在处理大数据时,必须考虑错误处理和系统的容错性,以应对可能出现的异常情况。 8. 数据安全:保护数据的安全性和隐私,遵循数据合规性原则。 综上所述,"yeslist_fill_gallon"虽然不是一个...
3. **错误处理**:HTMLParser对HTML的非标准写法有很好的容错性,它能够处理不闭合的标签、缺失的引号等常见问题,确保在不规范的HTML文档中也能正常工作。 4. **标签和属性处理**:HTMLParser提供了丰富的API来...
7. **错误处理**:`lxml` 提供了丰富的错误处理机制,可以捕获并处理解析、验证等过程中的错误。 在实际应用中,`lxml` 常用于 web 抓取、数据提取、XML 数据交换、文档生成等领域。其丰富的功能和高效性能使得它...