[语法分析]识别变量的分析器
变量这玩意儿本身很简单,无非是一个标识符开头,然后若干对中间夹着一个算数运算的正反方括号,而分析算术运算节点的任务又可以委托给专门的分析器来做,因此识别变量的分析器本身其实没什么需要做的。下面是它的数据结构。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 ## _VariableAnalyservoid 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;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);}
页:
[1]