首先回顾一下符号表提供的接口
AcceptType typeOf(struct VariableNode* var);
int staticOffset(struct VariableNode* var, int* offsets);
前面一个获取类型在这里并不是特别需要——那是在加载变量值的时候考虑的。至于 staticOffset 这个函数,功能已经非常强大了,我们现在要做的就是,计算运行时才能确定的地址。先搭个架子,明确一下我们要干的主要工作
struct List* addrOfVar(void* node)
{
struct VariableNode* self = (struct VariableNode*)node;
struct List* insList = (struct List*)newArrayList();
// 获取维数
int addr, nrDim = self->dimInfor->length(self->dimInfor);
struct IntParamInstruction* loadBase = (struct IntParamInstruction*)
allocate(sizeof(struct IntParamInstruction));
loadBase->code = CONST_INT;
appendIns(insList, loadBase);
if (0 == nrDim) { // 普通变量, 静态时就能确定地址.
loadBase->param = staticOffset(self, NULL);
} else {
// 是数组, 这里是我们要做的
}
return insList;
}
当然首先得用符号表的接口,像这样
int* dims = (int*)allocate(nrDim * sizeof(int)); // 申请一块空间, 用来存放数组大小信息
int i;
struct IntParamInstruction* loadOffset;
struct NoParamInstruction* cast,* mult,* add;
struct AbstractValueNode* value;
addr = staticOffset(self, dims); // 取~
loadBase->param = addr;
// 这里增加指令
revert(dims); // 记得干掉这些空间. PS: GCC似乎是支持在栈中直接写变长数组的, 如 int dim[nrDim];
剩下的就是一个筛选过程了,筛选传入的数组中那些值为 -1 的项目,不过呢,因为我们没有限定数组下标必须是整数(声明时必须是,但是引用时可以是实型量),因此我们还要承担变量转型的责任(注意到上面的代码中,声明的无参数指令中有一个叫做 cast)
for (i = 0; i < nrDim; ++i) {
if (-1 != dims[i]) { // 这一维是变量~
loadOffset = (struct IntParamInstruction*)
allocate(sizeof(struct IntParamInstruction));
loadOffset->code = CONST_INT;
loadOffset->param = dims[i]; // 取数组这一维的偏移量(还记得吧, 是偏移量哦)
appendIns(insList, loadOffset);
value = (struct AbstractValueNode*)
(self->dimInfor->peekAt(self->dimInfor, i));
appendInsList(insList, value->createInstruction(value)); // 下标的指令
if (REAL == value->typeOf(value)) { // 如果是实型, 转型
cast = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
cast->code = REAL_2_INT;
appendIns(insList, cast);
}
mult = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
mult->code = INT_MULTIPLY; // 将偏移量和下标相乘
appendIns(insList, mult);
add = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
add->code = INT_PLUS; // 累加
appendIns(insList, add);
}
}
这样就完成了变量取址。还原一个完整的函数
struct List* addrOfVar(void* node)
{
struct VariableNode* self = (struct VariableNode*)node;
struct List* insList = (struct List*)newArrayList();
int addr, nrDim = self->dimInfor->length(self->dimInfor);
struct IntParamInstruction* loadBase = (struct IntParamInstruction*)
allocate(sizeof(struct IntParamInstruction));
loadBase->code = CONST_INT;
appendIns(insList, loadBase);
if (0 == nrDim) {
loadBase->param = staticOffset(self, NULL);
} else {
int* dims = (int*)allocate(nrDim * sizeof(int));
int i;
struct IntParamInstruction* loadOffset;
struct NoParamInstruction* cast,* mult,* add;
struct AbstractValueNode* value;
addr = staticOffset(self, dims);
loadBase->param = addr;
for (i = 0; i < nrDim; ++i) {
if (-1 != dims[i]) {
loadOffset = (struct IntParamInstruction*)
allocate(sizeof(struct IntParamInstruction));
loadOffset->code = CONST_INT;
loadOffset->param = dims[i];
appendIns(insList, loadOffset);
value = (struct AbstractValueNode*)
(self->dimInfor->peekAt(self->dimInfor, i));
appendInsList(insList, value->createInstruction(value));
if (REAL == value->typeOf(value)) {
cast = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
cast->code = REAL_2_INT;
appendIns(insList, cast);
}
mult = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
mult->code = INT_MULTIPLY;
appendIns(insList, mult);
add = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
add->code = INT_PLUS;
appendIns(insList, add);
}
}
revert(dims);
}
return insList;
}
分享到:
相关推荐
采用递归下降语法制导翻译法,对算术表达式、赋值语句进行语义分析并生成四元式序列。 实验的输入和输出 输入是语法分析提供的正确的单词串,输出为三地址指令形式的四元式序列。 例如:对于语句串 begin a:=2+3*4 x...
语法制导翻译和中间代码生成是编译器设计的关键步骤,它们在编译过程中起着至关重要的作用。首先,我们要回顾编译过程,它通常包括词法分析、语法分析、语义分析和代码生成等阶段。语义分析位于语法分析之后,主要...
例如,当解析到C语言中的if语句时,语法制导翻译会生成对应的条件判断和分支指令。 5. **编译原理实验**: 在学习和实践中,编译原理的实验通常涉及实现上述组件。学生可能需要编写词法分析器、语法分析器,并应用...
《编译原理语法制导翻译》 在计算机科学领域,编译原理是研究如何将高级编程语言转换为机器可理解的指令集的关键分支。语法制导翻译是编译器设计中的一种重要技术,它结合了语法分析和语义处理,以确保程序的正确性...
4. **语法制导翻译**:基于抽象语法树,编译器进行语义分析,检查程序的逻辑正确性。此阶段可能会执行类型检查和常量折叠等操作,并可能引入一些优化。同时,它会根据语法规则生成相应的中间代码。 5. **中间代码...
总结来说,"语义分析器--语法制导翻译绘制图形"项目提供了一个实践编译器设计的平台,涵盖了语义分析、语法制导翻译和图形绘制等多个重要概念。通过深入研究这个项目,开发者可以深化对编译原理的理解,掌握C++编程...
二、语法制导的三地址码 三地址码是一种简单的中间表示(IR)形式,它以指令的形式表示计算过程,每个指令通常包含三个地址:操作符、操作数1和操作数2。例如,"x = y + z" 可以转化为 "x = add(y, z)"。这种表示...
"基于语法制导翻译的表达式转换编译器"是一种利用特定规则集(语法规则)来指导源代码转换过程的编译器设计方法。本项目专注于中缀表达式到后缀表达式的转换,这是编译器前端词法分析、语法分析和语义分析的一个典型...
在树的每个节点上,编译器执行与该节点对应的语法制导规则,这可能包括计算值、生成目标代码指令、存储或传递数据等。这种做法使得编译器可以精确地按照源代码的语法结构生成目标代码,从而确保程序的正确性。 语法...
中间代码生成_表示形式、语法制导方法》主要探讨了编译器设计中的关键环节——中间代码生成,这是编译过程中的一个重要阶段,目的是为了便于优化和移植。本节内容主要包括三元式、逆波兰式、抽象语法树(AST)以及四...
它主要涉及三个核心过程:词法分析、语法分析和代码生成,而四元式在这个过程中起到重要作用。 首先,词法分析(Lexical Analysis)是编译器的第一步,它的任务是对源代码进行扫描,将源代码分解成一系列有意义的、...
语法制导翻译与中间代码生成是编译器设计中的关键步骤,主要涉及程序的静态语义审查、类型检查、控制流分析以及中间代码生成。在这个过程中,编译器不仅要确保程序符合语法规则,还要对其含义进行深入理解,以便生成...
三地址代码生成器就是在这个阶段介入,它遍历AST,为每个节点生成对应的三地址指令。 在Turbo C3.0这样的编译器环境下,开发者可以实现自己的三地址代码生成器。虽然Turbo C3.0是一款较老的编译器,但其C语言编译器...
编译器通常分为几个阶段:词法分析、语法分析、语义分析和代码生成。三地址代码通常在语义分析后的优化阶段产生,作为代码生成的中间步骤。 二、三地址代码 三地址代码是一种低级的、三元操作符形式的IR。每个指令...
《编译原理 实验报告》 实验一主要涵盖了词法分析...词法分析器通过正规式和状态图解析源代码,而语法制导定义和三地址代码生成则涉及到语义分析和代码生成。理解这些概念对于深入学习编译原理和开发编译器至关重要。
采用递归下降语法制导翻译法,对算术表达式、赋值语句进行语义分析并生成四元式序列。 三、实验的结果验证 1.输入是语法分析后提供的正确的单词串,输出为三地址指令形式的四元式序列。 给出语句串: begin_a:=2+3*...
本项目“基于C语言实现简单语言递归下降语法制导翻译”深入探讨了这一主题,主要关注如何使用C语言来设计和实现一个编译器的核心部分——语义分析和中间代码生成。 首先,我们要理解什么是递归下降解析。递归下降是...
采用递归下降语法制导翻译法,对算术表达式、赋值语句进行语义分析并生成四元式序列。 实验的输入和输出 输入是语法分析提供的正确的单词串,输出为三地址指令形式的四元式序列。 例如:对于语句串 begin a:=2+3*4;x...