好了,现在来尝试把那些该未实现的注释给实现掉了。当然有两个地方在上一节中本应该实现,它们是代码块1中的
// 将一个左括号压入栈顶 // 实现为 opana->opStack->push(opana->opStack, newOperator(LPARENT, 0x7fffffff, nullOperate));
和代码块3中的
self->opStack->push(self->opStack, newOperator(token->type, /* 未实现:对应的优先级数 */ -1, unaryOperate)); // 后来加入了优先数表,所以这里可以实现为 self->opStack->push(self->opStack, newOperator(token->type, PRIORITY[token->type], unaryOperate)); // 将 MINUS 或 PLUS 作为一元运算符入栈 // 实现为 self->opStack->push(self->opStack, newOperator(token->type, 0, unaryOperate)); // 正括号入栈 // 实现为 self->opStack->push(self->opStack, newOperator(token->type, PRIORITY[token->type], nullOperate));
接下来,先为测试OperationAnalyser做准备,再将那些功能逐个实现。看起来这似乎不容易,不同的语法分析器之间耦合度较紧。不过这个问题可以这样解决:
struct FakeDefaultAnalyser { memberSyntaxAnalyser }; struct FakeVariableAnalyser { memberSyntaxAnalyser }; struct SyntaxAnalyser* newFakeDefaultAnalyser(void); struct SyntaxAnalyser* newVariableAnalyser(void); // fake one. void FakeDefaultAnalyser_ConsumeNT(void*, struct AbstractSyntaxNode*); void FakeDefaultAnalyser_ConsumeT(void*, struct Token*); void FakeVariableAnalyser_ConsumeT(void*, struct Token*);
这些数据结构以及其对应的成员函数并不需要复杂的设计,即可帮助我们完成测试工作。首先,对于FakeVariableAnalyser,因为测试的目的在于检查OperationAnalyser在遇到标识符时会不会跳转到分析变量的分析器中,所以这个数据结构可以很简单地实现——我们可以假定它一旦读入一个标识符,就把这个标识符打包变成一个VariableNode然后返回给OperationAnalyser——而它的consumeNonTerminal函数甚至根本没必要实现;对应地,测试数据也没有必要太复杂,所以与之相关的函数可以这样实现:
struct SyntaxAnalyser* newVariableAnalyser(void) { struct SyntaxAnalyser* varAna = (struct SyntaxAnalyser*) allocate(sizeof(struct FakeVariableAnalyser)); varAna->consumeToken = FakeVariableAnalyser_ConsumeT; varAna->consumeNonTerminal = NULL; return varAna; } void FakeVariableAnalyser_ConsumeT(void* self, struct Token* tkn) { if(IDENT != tkn->type) { fprintf(treeout, "incorrect token passed to variable analyser.\n"); fprintf(treeout, " token image: %s\n", tkn->image); exit(1); } revert(analyserStack->pop(analyserStack)); struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*) (analyserStack->peek(analyserStack)); analyser->consumeNonTerminal(analyser, (struct AbstractSyntaxNode*) newVariableNode(tkn->image)); }
而那个FakeDefaultAnalyser,则是常驻分析器栈栈底的默认分析器。考虑到当OperationAnalyser将自己弹出分析器栈并返回时会有两个动作:将得到的算术节点扔给FakeDefaultAnalyser的consumeNonTerminal函数、将最后一个传入的符号扔给FakeDefaultAnalyser的consumeToken函数,所以这两个成员函数都得实现。参考实现如下:
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) { fprintf(treeout, "\noperation returned.\n"); if(NULL == node) { fprintf(treeout, "NULL returned.\n"); } else { node->printree(node, 1); 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("Token passed: %2d / %s\n", t->type, t->image); } }
将节点信息输出到由treeout指向的日志文件中。
数据结构有了,现在准备将词法分析模块包含进来以便测试。词法分析需要的接口函数可以这样弄:
struct Stack* analyserStack; // 分析器栈指针 char buffer[64]; struct Token token = { 0, SKIP, NULL, buffer }; // 存放词法分析得到的符号 // 下面变量将用来提供测试数据,在nextChar中会用到 char* testcase[]; int testsuit_nr = 0; int ch_index = 0; int nextChar(void) { int ch = (int)testcase[testsuit_nr][ch_index++]; if(0 == ch) { ch = -1; ch_index = 0; ++testsuit_nr; } return ch; } struct Token* firstToken(void) { printf("Test case %2d\n", testsuit_nr); analyserStack->push(analyserStack, newOperationAnalyser()); // 新建一个OperationAnalyser压到栈顶准备测试 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); } void error(int line, ErrMsg err) { fprintf(stderr, "Line %d Error: %s\n", line, err); }
最后弄个测试驱动器,一个main函数。你可以把这一大串都放到一个.c文件中,独立编译执行。
#include<stdio.h> /* 引入COOL的MAD来调试内存泄漏 */ /* 请将COOL整个目录放在JerryCompiler所在的目录下 */ #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; // 用以输出节点信息日志 // 变量和函数声明放这里,以免main中报错 int main() { treeout = fopen("testout", "w"); // 这个文件随便你用什么名字 analyserStack = newStack(); // 分析器栈底常驻一个默认分析器,专门用来输出结果 analyserStack->push(analyserStack, newFakeDefaultAnalyser()); // testcase数组最后一项应该手动置为NULL while(NULL != testcase[testsuit_nr]) { tokenize(); // showAlloc; /* 如果最后的内存泄漏查看发现有没被释放的堆空间,那么取消这一块注释,以便查看每一轮测试中的未释放空间 */ } printf("Test done.\n"); revert(analyserStack->pop(analyserStack)); analyserStack->finalize(analyserStack); showAlloc; // 查看内存泄漏 fclose(treeout); return 0; }
测试数据参考:
char* testcase[] = { "1+2 + 5 /6;", // 普通运算 "(1 + 2*(2.4+5))", // 括号 "-(0 == ((!5) < (9)) && 14 >= 100 - -3)", // 各种逻辑运算、负号 "!4 == 7 || 4 == 8 && 4 <= 0 || 1 != 2", "i * s + d / k;", // 变量跳转 "a = b = c", // 赋值运算的右结合 ";", // 空语句 "1+!s)", // 多余的右括号 "1+k!)", // 不正确的表达式 "1+)", "1+!", "(1+ m* 5", // 多余的左括号 NULL };
在最后实现OperationAnalyser模块之前,为了避免整个项目中“名称用完”的情况发生,这里把一些成员函数的名字作特定包装,以免与其它函数不慎重名而导致莫名其妙的编译错误:
// 各个函数的名字用wrapname这个宏来处理一次 #define wrapname(name) name ## _OperationAnalyser // 原 consumeToken_OpAna static void wrapname(consumeToken)(void*, struct Token*); // 原 consumeNonTerminal_OpAna static void wrapname(consumeNonTerminal)(void*, struct AbstractSyntaxNode*); // 原 consumeFactor static void wrapname(consumeFactor)(struct OperationAnalyser*, struct Token*); // 原 consumeOperator static void wrapname(consumeOperator)(struct OperationAnalyser*, struct Token*);
此外还引入一个函数,它将在当前OperationAnalyser完成任务(无论成功或失败)时将它弹出分析器栈,释放它所占据的全部空间:
static void wrapname(cleanup)(struct OperationAnalyser* self) { while(0 != self->opStack->getSize(self->opStack)) { revert(self->opStack->pop(self->opStack)); } self->opStack->finalize(self->opStack); struct AbstractValueNode* node; while(0 != self->numStack->getSize(self->numStack)) { node = (struct AbstractValueNode*)(self->numStack->pop(self->numStack)); node->delNode(node); } self->numStack->finalize(self->numStack); analyserStack->pop(analyserStack); revert(self); }
现在开始各个击破。先来弄遇到标识符跳转的那块分支,将一个VariableAnalyser加入栈顶并让它去分析:
if(IDENT == token->type) { struct SyntaxAnalyser* analyser = newVariableAnalyser(); analyserStack->push(analyserStack, analyser); analyser->consumeToken(analyser, token); }
紧接着是这一块代码中最后那个分支。其实这里也不一定是错误,比如刚才构造的测试数据中,如果直接读入分号,或者由于某种原因,OperationAnalyser得到的是一个空的运算,就不是一个错误。不过,假如得到的是空运算的话,那么当前OperationAnalyser的数栈和符号栈应该都处于刚刚初始化的状态,因此
else { struct AbstractSyntaxNode* ret = (struct AbstractSyntaxNode*) (self->numStack->pop(self->numStack)); if(NULL != ret || 1 != self->opStack->getSize(self->opStack)) { // 栈内容已经改变,出错 printf("ERR #0\n"); // TODO } 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(consumeOperator)中最后两个分支中未实现的部分。其中当前符号为反括号的那一分支(条件为RPARENT == token->type)中,当反括号多1导致常驻栈底的那个正括号被配对的情况中(条件为0 == self->opStack->getSize(self->opStack),右方还标出“注2”的那句),其实并不需要报错,而完全可以把这个多出来的反括号扔给下一个语法分析器去做:
if(0 == self->opStack->getSize(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 { 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->getSize(self->opStack)) { printf("ERR #1\n"); // TODO } 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); }
前面直到while循环是不断弹出操作符栈栈顶进行运算;后面那句
topOp->operate(topOp, NULL);
看起来很莫名其妙?想想这时topOp是什么。它必然是一个左括号,因此它进行运算仅仅是释放它自己的内存空间(不直接写成释放空间的原因是,考虑到代码的封装性,我们应该假设自己不知道这些Operator是怎么来的,只知道它们应该都被运算掉);另外,加入输入是正确的,那么这个左括号应该是常驻栈顶的正括号,而它是被pop出来的,这也就意味着这样一来符号栈就应该是空的了,如果这是符号栈里面还有东西(if(0 != self->opStack->getSize(self->opStack))),那就意味着左括号数量多了,因此出错。
现在就可以开始测试了。测试的结果有一些会从标准输出打印出来,如那些“ERR #”之类的信息,而语法树信息则会输出到treeout指向的文件中。Enjoy~
语法树信息的格式
输出到文件里面的信息看起来不是那么友好。也许注意一下它们的缩进会有所帮助,如
1 + 2 + 5 / 6
对应的输出为
operation returned.
binary operation - operator : + ~~ left operand:
binary operation - operator : + ~~ left operand:
integer 1
** right operand:
integer 2
** right operand:
binary operation - operator : / ~~ left operand:
integer 5
** right operand:
integer 6
第一行“operation returned.”是在FakeDefaultAnalyser_ConsumeNT中输出的,而后面的那一串都是在printBinaryOperation中输出的。注意到第2行和第6行缩进相同,这表示它们是一个节点的两个部分。第2行之后缩进增加的那些部分是该BinaryOperationNode节点的leftOperand所指向的子树,而第6行之后那些部分则是其rightOperand指向的子树。所以这表示出来的是这样一棵树:
+
/ \
+ / <---这个斜杠是除号
/ \ / \
1 2 5 6
此外还有UnaryOperationNode,它的形式如
unary operation - operator : %运算符% ~~ operand:
运算数节点信息
See also:
indent printBinaryOperationNode printUnaryOperationNode 函数,syntax-node.c 文件
/* syntax-analyser.h */ #ifndef _SYNTAX_ANALYSER_H #define _SYNTAX_ANALYSER_H #include"datastruct.h" struct OperationAnalyser* newOperationAnalyser(void); /* 暂时将.c文件用.h文件来包含,而不是用makefile */ #include"operation-analyser.c" #endif /* _SYNTAX_ANALYSER_H */
/* operation-analyser.c */ #include<stdlib.h> #include"datastruct.h" #include"syntax-analyser.h" #include"syntax-node.h" #ifndef _DEBUG_MODE #define _DEBUG_MODE #endif /* _DEBUG_MODE */ #include"COOL/MemoryAllocationDebugger.h" #include"COOL/Collection/Stack/Stack.h" extern struct Stack* analyserStack; extern struct SyntaxAnalyser* newVariableAnalyser(void); struct Operator { void (*operate)(struct Operator*, struct Stack*); AcceptType op; int priority; int rightCombination; }; static const int RIGHT_COMB[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; static struct Operator* newOperator(AcceptType op, int priority, void (*operate)(struct Operator*, struct Stack*)); static void nullOperate(struct Operator*, struct Stack*); static void unaryOperate(struct Operator*, struct Stack*); static void binaryOperate(struct Operator*, struct Stack*); static struct Operator* newOperator(AcceptType op, int priority, void (*operate)(struct Operator*, struct Stack*)) { struct Operator* oper = (struct Operator*)allocate(sizeof(struct Operator)); oper->op = op; oper->priority = priority; oper->rightCombination = RIGHT_COMB[op]; oper->operate = operate; return oper; } static void nullOperate(struct Operator* oper, struct Stack* numStack) { revert(oper); } static void unaryOperate(struct Operator* oper, struct Stack* numStack) { struct AbstractValueNode* value; value = (struct AbstractValueNode*)(numStack->pop(numStack)); numStack->push(numStack, newUnaryOperationNode(oper->op, value)); revert(oper); } static void binaryOperate(struct Operator* oper, struct Stack* numStack) { struct AbstractValueNode* left,* right; right = (struct AbstractValueNode*)(numStack->pop(numStack)); left = (struct AbstractValueNode*)(numStack->pop(numStack)); numStack->push(numStack, newBinaryOperationNode(oper->op, left, right)); revert(oper); } #define wrapname(name) name ## _OperationAnalyser static void wrapname(consumeToken)(void*, struct Token*); static void wrapname(consumeNonTerminal)(void*, struct AbstractSyntaxNode*); static void wrapname(consumeFactor)(struct OperationAnalyser*, struct Token*); static void wrapname(consumeOperator)(struct OperationAnalyser*, struct Token*); static void wrapname(cleanup)(struct OperationAnalyser*); static const int PRIORITY[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 1, 1, 5, 3, 3, 3, 3, 3, 3, 5, 6, 4, 0, 0, 0x7fffffff, 0, 0, 0, 0, 0 }; static void (*OPER_FUNCS[])(struct Operator*, struct Stack*) = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, binaryOperate, binaryOperate, binaryOperate, binaryOperate, binaryOperate, binaryOperate, binaryOperate, binaryOperate, binaryOperate, binaryOperate, binaryOperate, binaryOperate, binaryOperate, unaryOperate }; struct OperationAnalyser* newOperationAnalyser(void) { struct OperationAnalyser* opana = (struct OperationAnalyser*) allocate(sizeof(struct OperationAnalyser)); opana->needFactor = 1; opana->numStack = newStack(); opana->opStack = newStack(); opana->opStack->push(opana->opStack, newOperator(LPARENT, 0x7fffffff, nullOperate)); opana->consumeToken = wrapname(consumeToken); opana->consumeNonTerminal = wrapname(consumeNonTerminal); return opana; } static void wrapname(consumeToken)(void* self, struct Token* token) { struct OperationAnalyser* opana = (struct OperationAnalyser*)self; if(opana->needFactor) { // printf("... Passing Factor ... %s\n", token->image); wrapname(consumeFactor)(opana, token); } else { // printf("... Passing Operator ... %s\n", token->image); wrapname(consumeOperator)(opana, token); } } 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->pop(self->numStack)); if(NULL != ret || 1 != self->opStack->getSize(self->opStack)) { printf("ERR #0\n"); } 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); } } static void wrapname(consumeOperator)(struct OperationAnalyser* self, struct Token* token) { int priority = PRIORITY[token->type]; if(0 < priority && priority < PRIORITY[LPARENT]) { 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) { // printf("Operating ... %s\n", OPERATORS[topOp->op]); 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) { // printf("Operating ... %s\n", OPERATORS[topOp->op]); 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->getSize(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 { 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->getSize(self->opStack)) { printf("ERR #1\n"); } 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); } } static void wrapname(consumeNonTerminal)(void* self, struct AbstractSyntaxNode* node) { struct OperationAnalyser* opana = (struct OperationAnalyser*)self; opana->numStack->push(opana->numStack, node); opana->needFactor = 0; } static void wrapname(cleanup)(struct OperationAnalyser* self) { while(0 != self->opStack->getSize(self->opStack)) { revert(self->opStack->pop(self->opStack)); } self->opStack->finalize(self->opStack); struct AbstractValueNode* node; while(0 != self->numStack->getSize(self->numStack)) { node = (struct AbstractValueNode*)(self->numStack->pop(self->numStack)); node->delNode(node); } self->numStack->finalize(self->numStack); analyserStack->pop(analyserStack); revert(self); } #undef wrapname
/* test-op-ana.c */ #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 FakeVariableAnalyser { memberSyntaxAnalyser }; struct SyntaxAnalyser* newFakeDefaultAnalyser(void); struct SyntaxAnalyser* newVariableAnalyser(void); // fake one. void FakeDefaultAnalyser_ConsumeNT(void*, struct AbstractSyntaxNode*); void FakeDefaultAnalyser_ConsumeT(void*, struct Token*); void FakeVariableAnalyser_ConsumeT(void*, struct Token*); struct Stack* analyserStack; int testsuit_nr = 0; int ch_index = 0; char buffer[64]; struct Token token = { 0, SKIP, NULL, buffer }; char* testcase[] = { "1+2 + 5 /6;", // 普通运算 "(1 + 2*(2.4+5))", // 括号 "-(0 == ((!5) < (9)) && 14 >= 100 - -3)", // 各种逻辑运算、负号 "!4 == 7 || 4 == 8 && 4 <= 0 || 1 != 2", "i * s + d / k;", // 变量跳转 "a = b = c", // 赋值运算的右结合 ";", // 空语句 "1+!s)", // 多余的右括号 "1+k!)", // 不正确的表达式 "1+)", "1+!", "(1+ m* 5", // 多余的左括号 NULL }; int main() { treeout = fopen("testout", "w"); analyserStack = newStack(); analyserStack->push(analyserStack, newFakeDefaultAnalyser()); while(NULL != testcase[testsuit_nr]) { tokenize(); // showAlloc; } printf("Test done.\n"); revert(analyserStack->pop(analyserStack)); analyserStack->finalize(analyserStack); showAlloc; 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; } struct SyntaxAnalyser* newVariableAnalyser(void) { struct SyntaxAnalyser* varAna = (struct SyntaxAnalyser*) allocate(sizeof(struct FakeVariableAnalyser)); varAna->consumeToken = FakeVariableAnalyser_ConsumeT; varAna->consumeNonTerminal = NULL; return varAna; } void FakeDefaultAnalyser_ConsumeNT(void* self, struct AbstractSyntaxNode* node) { fprintf(treeout, "\noperation returned.\n"); if(NULL == node) { fprintf(treeout, "NULL returned.\n"); } else { node->printree(node, 1); node->delNode(node); } } void FakeVariableAnalyser_ConsumeT(void* self, struct Token* tkn) { if(IDENT != tkn->type) { fprintf(treeout, "incorrect token passed to variable analyser.\n"); fprintf(treeout, " token image: %s\n", tkn->image); exit(1); } revert(analyserStack->pop(analyserStack)); struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*) (analyserStack->peek(analyserStack)); analyser->consumeNonTerminal(analyser, (struct AbstractSyntaxNode*) newVariableNode(tkn->image)); } void FakeDefaultAnalyser_ConsumeT(void* self, struct Token* t) { if(NULL == t->image) { printf("Token passed: %2d / NULL image\n", t->type); } else { printf("Token passed: %2d / %s\n", t->type, t->image); } } int nextChar(void) { int ch = (int)testcase[testsuit_nr][ch_index++]; if(0 == ch) { ch = -1; ch_index = 0; ++testsuit_nr; } return ch; } struct Token* firstToken(void) { printf("Test case %2d\n", testsuit_nr); analyserStack->push(analyserStack, newOperationAnalyser()); 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); }