NeuronR 发表于 2013-2-4 20:11:39

[语法分析]识别变量的分析器

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