变量分析器本身很简单,它的错误处理不需要弄得太复杂,只对正反方括号做做验证,其它的丢给表达式分析器或者LR分析器去弄就行了。(下面一段话中的括号均指方括号。)
大致的构想是这样的:当需要反括号时,没碰到反括号,直接提示错误,然后装作啥也没发生一样重新看看这个终结符;而遇到除了括号以外的其它符号时,就终止识别(此时变量识别结束,返回)。实现如下:
void wrapname(consumeLBracket)(void* self, struct Token* t) { if(LBRACKET == t->type) { analyserStack->push(analyserStack, newOperationAnalyser()); } else { /* 返回 */ struct VariableNode* var = ((struct VariableAnalyser*)self)->var; revert(analyserStack->pop(analyserStack)); struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*) (analyserStack->peek(analyserStack)); analyser->consumeNonTerminal(analyser, (struct AbstractSyntaxNode*)var); analyser = (struct SyntaxAnalyser*) (analyserStack->peek(analyserStack)); analyser->consumeToken(analyser, t); } } void wrapname(consumeRBracket)(void* self, struct Token* t) { struct VariableAnalyser* varAna = (struct VariableAnalyser*)self; // 先认为没有错误 varAna->consumeToken = wrapname(consumeLBracket); // 再来验证符号 if(RBRACKET != t->type) { /* 符号不对 */ varAna->consumeToken(varAna, t); // 继续分析 } }
上次说了,如果用一堆printf来弄这些错误处理不太方便,所以这回来把错误提示的接口抽出来,以方便日后维护。对于语法分析,错误提示需要关心的是错误的类型和在哪一个终结符附近出的错。因此接口函数可以这样写
void syntaxError(ErrMsg, struct Token*);
实现则可以这样
void syntaxError(ErrMsg e, struct Token* t) { failed = 1; if(END == t->type) { fprintf(stderr, "Syntax error at the end of the file.\n" " %s\n", e); } else { fprintf(stderr, "Syntax error @ line: %d token: `%s'\n" " %s\n", t->line, t->image, e); } }
那么刚才那些错误提示就可以这样搞
static ErrMsg missingRBracket = "Missing close bracket."; void wrapname(consumeLBracket)(void* self, struct Token* t) { if(LBRACKET == t->type) { analyserStack->push(analyserStack, newOperationAnalyser()); } else { /* 返回 */ struct VariableNode* var = ((struct VariableAnalyser*)self)->var; revert(analyserStack->pop(analyserStack)); struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*) (analyserStack->peek(analyserStack)); analyser->consumeNonTerminal(analyser, (struct AbstractSyntaxNode*)var); analyser = (struct SyntaxAnalyser*) (analyserStack->peek(analyserStack)); analyser->consumeToken(analyser, t); } } void wrapname(consumeRBracket)(void* self, struct Token* t) { struct VariableAnalyser* varAna = (struct VariableAnalyser*)self; varAna->consumeToken = wrapname(consumeLBracket); if(RBRACKET != t->type) { syntaxError(missingRBracket, t); // and here varAna->consumeToken(varAna, t); } }
再附上之前OperationAnalyser中错误处理的部分,请定位原来的那些输出语句,然后将其改为这些玩意儿。
/* wrapname(consumeOperator) 中 */ // ... 一些 if else 分支过后 else if (isFirstFactor(token->type)) { struct Token fakeOp = {token->line, PLUS, NULL, "+"}; syntaxError(wantOperator, token); // 0.0 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)) { syntaxError(excessLParent, token); // :-p } 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); } // ... /* wrapname(consumeFactor) 中 */ // ... 一些 if 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); 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"}; syntaxError(wantFactor, token); // =,= self->consumeToken(self, &fakeNum); self->consumeToken(self, token); } // ...