变量这玩意儿本身很简单,无非是一个标识符开头,然后若干对中间夹着一个算数运算的正反方括号,而分析算术运算节点的任务又可以委托给专门的分析器来做,因此识别变量的分析器本身其实没什么需要做的。下面是它的数据结构。
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);
}
本文介绍了一个简单的变量解析器的设计与实现过程,通过特定的数据结构和成员函数实现了对变量标识符及算术运算的有效识别。
1190

被折叠的 条评论
为什么被折叠?



