变量这玩意儿本身很简单,无非是一个标识符开头,然后若干对中间夹着一个算数运算的正反方括号,而分析算术运算节点的任务又可以委托给专门的分析器来做,因此识别变量的分析器本身其实没什么需要做的。下面是它的数据结构。
struct VariableAnalyser { memberSyntaxAnalyser struct VariableNode* var; // 用来存放识别的变量 };
下面是它的各成员函数的实现,这里用了一种有趣的实现方式。
/* variable-analyser.c */ #include<stdlib.h> #include"datastruct.h" #include"syntax-analyser.h" #include"syntax-node.h" #include"COOL/MemoryAllocationDebugger.h" extern struct Stack* analyserStack; extern struct SyntaxAnalyser* newOperationAnalyser(void); #define wrapname(name) name ## _VariableAnalyser void wrapname(consumeIdentifier)(void*, struct Token*); void wrapname(consumeLBracket)(void*, struct Token*); void wrapname(consumeRBracket)(void*, struct Token*); void wrapname(consumeIdentifier)(void* self, struct Token* t) { ((struct VariableAnalyser*)self)->var = newVariableNode(t->image); ((struct VariableAnalyser*)self)->consumeToken = wrapname(consumeLBracket); } void wrapname(consumeLBracket)(void* self, struct Token* t) { if(LBRACKET == t->type) { analyserStack->push(analyserStack, newOperationAnalyser()); } else { // printf("Varaible analyser returns.\n"); struct VariableNode* var = ((struct VariableAnalyser*)self)->var; revert(analyserStack->pop(analyserStack)); struct SyntaxAnalyser* analyser = analyserStack->peek(analyserStack); analyser->consumeNonTerminal(analyser, (struct AbstractSyntaxNode*)var); analyser = analyserStack->peek(analyserStack); analyser->consumeToken(analyser, t); } } void wrapname(consumeRBracket)(void* self, struct Token* t) { if(RBRACKET == t->type) { ((struct VariableAnalyser*)self)->consumeToken = wrapname(consumeLBracket); } else { printf("Expecting `]', but %s\n", t->image); // TODO 错误处理 } } void wrapname(consumeNonTerminal)(void* self, struct AbstractSyntaxNode* op) { struct VariableAnalyser* varAna = (struct VariableAnalyser*)self; varAna->var->dimInfor->enqueue(varAna->var->dimInfor, op); varAna->consumeToken = wrapname(consumeRBracket); } struct SyntaxAnalyser* newVariableAnalyser(void) { struct VariableAnalyser* varAna = (struct VariableAnalyser*) allocate(sizeof(struct VariableAnalyser)); varAna->consumeToken = wrapname(consumeIdentifier); varAna->consumeNonTerminal = wrapname(consumeNonTerminal); return (struct SyntaxAnalyser*)varAna; } #undef wrapname
你一定还记得在OperationAnalyser的实现中,为了区分当前需要的是一个运算符还是一个数,它的consumeToken函数指向一个分流函数,该分流函数根据当前OperationAnalyser的needFactor成员判断是该调用consumeFactor还是consumeOperator,那是一种不好的实现:本应该可以用多态来实现的部分,却滥用分支语句,这在设计模式上是有问题的。现在VariableAnalyser纠正了这一点。在一个VariableAnalyser刚刚新建时,它的consumeToken函数指向wrapname(consumeIdentifier)函数,然后在该函数内将自身的consumeToken函数改指向wrapname(consumeLBracket)函数。其它的函数中也可以看到类似的小动作。这些小动作使设计更清晰了。
附:一个用来测试语法分析模块的驱动器
当然,这并不意味着语法分析结束了,还有错误处理没有完成呢。
这个是语法分析器头文件。记得把分析器结构定义放进 datastruct.h 中。
/* syntax-analyser.h */ #ifndef _SYNTAX_ANALYSER_H #define _SYNTAX_ANALYSER_H #include"datastruct.h" struct SyntaxAnalyser* newOperationAnalyser(void); void initialLRStates(void); void destructLRStates(void); struct SyntaxAnalyser* newLRAnalyser(void); struct SyntaxAnalyser* newVariableAnalyser(void); #ifdef _DEBUG_MODE #include"operation-analyser.c" #include"lr-analyser.c" #include"variable-analyser.c" #endif /* _DEBUG_MODE */ #endif /* _SYNTAX_ANALYSER_H */
下面这个东西脱胎于OperationAnalyser的测试驱动器。不过也没有处理行号,你可以结合词法分析的测试来进行修改。
#include<stdio.h> #ifndef _DEBUG_MODE #define _DEBUG_MODE #endif /* _DEBUG_MODE */ #include"COOL/MemoryAllocationDebugger.h" #include"datastruct.h" #include"syntax-node.h" #include"syntax-analyser.h" #include"dfa.h" #include"dfa.c" FILE* treeout; struct FakeDefaultAnalyser { memberSyntaxAnalyser }; struct SyntaxAnalyser* newFakeDefaultAnalyser(void); void FakeDefaultAnalyser_ConsumeNT(void*, struct AbstractSyntaxNode*); void FakeDefaultAnalyser_ConsumeT(void*, struct Token*); struct Stack* analyserStack; // 分析器栈 FILE* input; // 输入文件 // 存放终结符 char buffer[64]; struct Token token = { 0, SKIP, NULL, buffer }; int main() { treeout = fopen("syntax.xml", "w"); // 输出为xml input = fopen("testin", "r"); analyserStack = newStack(); analyserStack->push(analyserStack, newFakeDefaultAnalyser()); initialLRStates(); tokenize(); destructLRStates(); printf("Test done.\n"); revert(analyserStack->pop(analyserStack)); analyserStack->finalize(analyserStack); showAlloc; fclose(input); fclose(treeout); return 0; } struct SyntaxAnalyser* newFakeDefaultAnalyser(void) { struct SyntaxAnalyser* defAna = (struct SyntaxAnalyser*) allocate(sizeof(struct FakeDefaultAnalyser)); defAna->consumeToken = FakeDefaultAnalyser_ConsumeT; defAna->consumeNonTerminal = FakeDefaultAnalyser_ConsumeNT; return defAna; } void FakeDefaultAnalyser_ConsumeNT(void* self, struct AbstractSyntaxNode* node) { printf("\nReturned.\n"); if(NULL == node) { fprintf(treeout, "NULL returned.\n"); } else { fprintf(treeout, "<Jerry>\n"); node->printree(node, 1); fprintf(treeout, "</Jerry>\n"); node->delNode(node); } } void FakeDefaultAnalyser_ConsumeT(void* self, struct Token* t) { if(NULL == t->image) { printf("Token passed: %2d / NULL image\n", t->type); } else { printf("Error before %s\n", t->image); exit(1); } } int nextChar(void) { return fgetc(input); } struct Token* firstToken(void) { analyserStack->push(analyserStack, newLRAnalyser()); return &token; } struct Token* nextToken(void) { if(SKIP != token.type) { struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*) (analyserStack->peek(analyserStack)); analyser->consumeToken(analyser, &token); } return &token; } void eofToken(void) { nextToken(); struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*) (analyserStack->peek(analyserStack)); struct Token endToken = {0, END, NULL, NULL}; analyser->consumeToken(analyser, &endToken); }