运算. 首先是单目运算指令, 相对而言比较简单, 只有3种情况
struct List* insUnaryOperationNode(void* node)
{
struct UnaryOperationNode* self = (struct UnaryOperationNode*)node;
AcceptType type = self->typeOf(self);
// 如果是常数
if (INTEGER == type) {
int intVal;
if (0 == self->staticInt(self, &intVal)) {
struct List* ret = (struct List*)newArrayList();
struct IntParamInstruction* ins = (struct IntParamInstruction*)
allocate(sizeof(struct IntParamInstruction));
ins->code = CONST_INT;
ins->param = intVal;
ret->add(ret, ins);
return ret;
}
} else {
double realVal;
if (0 == self->staticReal(self, &realVal)) {
struct List* ret = (struct List*)newArrayList();
struct RealParamInstruction* ins = (struct RealParamInstruction*)
allocate(sizeof(struct RealParamInstruction));
ins->code = CONST_REAL;
ins->param = realVal;
ret->add(ret, ins);
return ret;
}
}
// 先取得子表达式的指令
struct List* operandIns = self->operand->createInstruction(self->operand);
// 运算指令
struct NoParamInstruction* op = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
if (MINUS == self->op) { // 求相反数, 思路是变成 0 - 该数
struct List* ins = (struct List*)newArrayList();
if (INTEGER == type) {
op->code = INT_MINUS;
struct IntParamInstruction* load0 = (struct IntParamInstruction*)
allocate(sizeof(struct IntParamInstruction));
load0->code = CONST_INT;
load0->param = 0;
appendIns(appendInsList(appendIns(ins, load0), operandIns), op);
} else {
op->code = REAL_MINUS;
struct RealParamInstruction* load0 = (struct RealParamInstruction*)
allocate(sizeof(struct RealParamInstruction));
load0->code = CONST_REAL;
load0->param = 0.0;
appendIns(appendInsList(appendIns(ins, load0), operandIns), op);
}
return ins;
} else if (NOT == self->op) { // 逻辑求反, 思路是变成 0 == 该数
if (REAL == self->operand->typeOf(self->operand)) { // Oops~
// 报错
revert(op);
return operandIns;
}
struct IntParamInstruction* load0 = (struct IntParamInstruction*)
allocate(sizeof(struct IntParamInstruction));
load0->code = CONST_INT;
load0->param = 0;
op->code = INT_EQ;
return appendIns(appendIns(operandIns, load0), op);
} else { // 加号可以无视了
revert(op);
return operandIns;
}
}
双目运算, 之前提到过, 会将其拆分成3个分支(嗯, 也是3个)来进行, 它们是
static struct List* insNormalBinaryOp(struct BinaryOperationNode*);
static struct List* insAssignment(struct BinaryOperationNode*);
static struct List* insLogicShortcut(struct BinaryOperationNode*);
那么在指令产生成员函数中只需进行调度即可
struct List* insBinaryOperationNode(void* node)
{
struct BinaryOperationNode* self = (struct BinaryOperationNode*)node;
AcceptType type = self->typeOf(self);
// 如果是常数
if (INTEGER == type) {
int intVal;
if (0 == self->staticInt(self, &intVal)) {
struct List* ret = (struct List*)newArrayList();
struct IntParamInstruction* ins = (struct IntParamInstruction*)
allocate(sizeof(struct IntParamInstruction));
ins->code = CONST_INT;
ins->param = intVal;
ret->add(ret, ins);
return ret;
}
} else {
double realVal;
if (0 == self->staticReal(self, &realVal)) {
struct List* ret = (struct List*)newArrayList();
struct RealParamInstruction* ins = (struct RealParamInstruction*)
allocate(sizeof(struct RealParamInstruction));
ins->code = CONST_REAL;
ins->param = realVal;
ret->add(ret, ins);
return ret;
}
}
if (ASSIGN == self->op) { // 是等号
return insAssignment(self);
} else if (isArithOperator(self->op) || isCompareOperator(self->op)) {
// isArithOperator 和 isCompareOperator 是两个宏
// 进行普通二目运算
return insNormalBinaryOp(self);
} else {
// 逻辑运算 && 和 || 没有对应的指令, 需要进行条件短路来实现.
return insLogicShortcut(self);
}
}
赋值运算函数 insAssignment 参考实现. 需要注意左右值类型是否匹配.
static struct List* insAssignment(struct BinaryOperationNode* self)
{
struct List* leftIns,* rightIns;
// Oops~
if (NULL == (leftIns = self->leftOperand->addressOf(self->leftOperand))) {
// 报错: 左值不合法
return (struct List*)newArrayList();
}
AcceptType leftType = self->leftOperand->typeOf(self->leftOperand),
rightType = self->rightOperand->typeOf(self->rightOperand);
rightIns = self->rightOperand->createInstruction(self->rightOperand);
appendInsList(leftIns, rightIns);
if (leftType == rightType) { // 左右值类型相同, 只需简单增加一条赋值指令
struct NoParamInstruction* ins = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
if (INTEGER == leftType) {
ins->code = INT_ASSIGN;
} else {
ins->code = REAL_ASSIGN;
}
appendIns(leftIns, ins);
} else { // 类型不同
// 类型转换指令
struct NoParamInstruction* cast = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
// 运算指令
struct NoParamInstruction* ins = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
if (INTEGER == leftType) {
cast->code = REAL_2_INT;
ins->code = INT_ASSIGN;
} else {
cast->code = INT_2_REAL;
ins->code = REAL_ASSIGN;
}
appendIns(leftIns, cast);
appendIns(leftIns, ins);
}
return leftIns;
}
普通运算也一样, 需要注意类型. 不同的是, 在赋值运算中, 如果左右值类型不匹配, 右值类型会被转换为左值; 但是在普通运算中, 类型会向精确度高的一边提升(INTEGER -> REAL). 因此在类型失配时处理方式与赋值运算会显著不一样.
static struct List* insNormalBinaryOp(struct BinaryOperationNode* self)
{
AcceptType leftType = self->leftOperand->typeOf(self->leftOperand),
rightType = self->rightOperand->typeOf(self->rightOperand);
struct List* leftIns,* rightIns;
leftIns = self->leftOperand->createInstruction(self->leftOperand);
rightIns = self->rightOperand->createInstruction(self->rightOperand);
if (leftType == rightType) {
struct NoParamInstruction* ins = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
if (INTEGER == leftType) {
ins->code = getCodeByIntOp(self->op);
} else {
ins->code = getCodeByRealOp(self->op);
}
appendIns(appendInsList(leftIns, rightIns), ins);
} else {
struct NoParamInstruction* cast = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
struct NoParamInstruction* ins = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
ins->code = getCodeByRealOp(self->op);
cast->code = INT_2_REAL;
if (INTEGER == leftType) { // 转换整型那一边.
appendInsList(appendIns(leftIns, cast), rightIns);
} else {
appendIns(appendInsList(leftIns, rightIns), cast);
}
appendIns(leftIns, ins);
}
return leftIns;
}
最后是条件短路. 思路参考可以这篇文章. 在一些设计中, 逻辑值与分支和循环语句的出口密切相关. 不过在Jerry语言中求逻辑值时我们缺乏上下文(我们也别把这种事情弄得太复杂), 所以Jerry实现时仅仅是在语法节点对应的指令执行结束时在栈顶放上一个真值或者假值(1 或者 0)
static struct List* insLogicShortcut(struct BinaryOperationNode* self)
{
AcceptType leftType = self->leftOperand->typeOf(self->leftOperand),
rightType = self->rightOperand->typeOf(self->rightOperand);
if (REAL == leftType || REAL == rightType) { // Oops~
// 报错
return (struct List*)newArrayList();
}
struct List* leftIns,* rightIns;
leftIns = self->leftOperand->createInstruction(self->leftOperand);
rightIns = self->rightOperand->createInstruction(self->rightOperand);
struct JumpInstruction* shortcut = (struct JumpInstruction*)
allocate(sizeof(struct JumpInstruction));
struct JumpInstruction* jumpout = (struct JumpInstruction*)
allocate(sizeof(struct JumpInstruction));
struct IntParamInstruction* putResult = (struct IntParamInstruction*)
allocate(sizeof(struct IntParamInstruction));
struct NoParamInstruction* outlet = (struct NoParamInstruction*)
allocate(sizeof(struct NoParamInstruction));
/* +-----------------------------+
| 左子树指令
+-----------------------------+
| 条件跳转 (shortcut 指令) -> putResult 指令
+-----------------------------+
| 右子树指令
+-----------------------------+
| 跳出 (jumpout 指令) -> outlet 指令
+-----------------------------+
| 栈顶置结果 (putResult 指令)
+-----------------------------+
| 出口 (outlet 伪指令)
+-----------------------------+
指令结构如上图 */
putResult->code = CONST_INT;
jumpout->code = JMP;
jumpout->targetIns = (struct AbstractInstruction*)outlet;
outlet->code = NOP;
shortcut->targetIns = (struct AbstractInstruction*)putResult;
if (AND == self->op) { // 运算符不同, 产生的指令差异仅此两处
shortcut->code = JMP_NOT_TOP;
putResult->param = 0;
} else {
shortcut->code = JMP_IF_TOP;
putResult->param = 1;
}
return appendIns(appendIns(appendIns(appendInsList(appendIns(
leftIns,
shortcut),
rightIns),
jumpout),
putResult),
outlet); // 确实, 这里看起来很令人心神不宁...
}
有趣的是, && 和 || 的结构是如此相似, 以至于两者产生的指令只有两处不同: 当运算符为 && 时, 短路条件为栈顶为假, 而表达式结果是 0, 而运算符为 || 时情况正好相反.
分享到:
相关推荐
采用递归下降语法制导翻译法,对算术表达式、赋值语句进行语义分析并生成四元式序列。 实验的输入和输出 输入是语法分析提供的正确的单词串,输出为三地址指令形式的四元式序列。 例如:对于语句串 begin a:=2+3*4 x...
语法制导翻译和中间代码生成是编译器设计的关键步骤,它们在编译过程中起着至关重要的作用。首先,我们要回顾编译过程,它通常包括词法分析、语法分析、语义分析和代码生成等阶段。语义分析位于语法分析之后,主要...
例如,当解析到C语言中的if语句时,语法制导翻译会生成对应的条件判断和分支指令。 5. **编译原理实验**: 在学习和实践中,编译原理的实验通常涉及实现上述组件。学生可能需要编写词法分析器、语法分析器,并应用...
《编译原理语法制导翻译》 在计算机科学领域,编译原理是研究如何将高级编程语言转换为机器可理解的指令集的关键分支。语法制导翻译是编译器设计中的一种重要技术,它结合了语法分析和语义处理,以确保程序的正确性...
4. **语法制导翻译**:基于抽象语法树,编译器进行语义分析,检查程序的逻辑正确性。此阶段可能会执行类型检查和常量折叠等操作,并可能引入一些优化。同时,它会根据语法规则生成相应的中间代码。 5. **中间代码...
总结来说,"语义分析器--语法制导翻译绘制图形"项目提供了一个实践编译器设计的平台,涵盖了语义分析、语法制导翻译和图形绘制等多个重要概念。通过深入研究这个项目,开发者可以深化对编译原理的理解,掌握C++编程...
二、语法制导的三地址码 三地址码是一种简单的中间表示(IR)形式,它以指令的形式表示计算过程,每个指令通常包含三个地址:操作符、操作数1和操作数2。例如,"x = y + z" 可以转化为 "x = add(y, z)"。这种表示...
"基于语法制导翻译的表达式转换编译器"是一种利用特定规则集(语法规则)来指导源代码转换过程的编译器设计方法。本项目专注于中缀表达式到后缀表达式的转换,这是编译器前端词法分析、语法分析和语义分析的一个典型...
在树的每个节点上,编译器执行与该节点对应的语法制导规则,这可能包括计算值、生成目标代码指令、存储或传递数据等。这种做法使得编译器可以精确地按照源代码的语法结构生成目标代码,从而确保程序的正确性。 语法...
4. **四元式**:四元式比三元式增加了运算结果的记录,如`(op, arg1, arg2, result)`,使得每个操作更为独立,更接近于汇编语言和机器指令,便于生成目标代码。四元式的顺序决定了计算顺序,简化了计算流程。 5. **...
它主要涉及三个核心过程:词法分析、语法分析和代码生成,而四元式在这个过程中起到重要作用。 首先,词法分析(Lexical Analysis)是编译器的第一步,它的任务是对源代码进行扫描,将源代码分解成一系列有意义的、...
语法制导翻译与中间代码生成是编译器设计中的关键步骤,主要涉及程序的静态语义审查、类型检查、控制流分析以及中间代码生成。在这个过程中,编译器不仅要确保程序符合语法规则,还要对其含义进行深入理解,以便生成...
4. **优化**:生成的三地址代码可能包含冗余计算或不必要的临时变量。通过诸如删除公共子表达式、常量折叠和复用临时变量等优化技术,可以提高代码效率。 5. **目标代码生成**:最后,三地址代码生成器需要将三地址...
4. **三地址代码生成**:遍历AST,将其转换为三地址代码,这个过程涉及到对表达式的求值和控制流的处理。 5. **优化**:对生成的三地址代码进行优化,如消除冗余计算、代码移动等,提高目标代码的效率。 6. **代码...
《编译原理 实验报告》 实验一主要涵盖了词法分析...词法分析器通过正规式和状态图解析源代码,而语法制导定义和三地址代码生成则涉及到语义分析和代码生成。理解这些概念对于深入学习编译原理和开发编译器至关重要。
采用递归下降语法制导翻译法,对算术表达式、赋值语句进行语义分析并生成四元式序列。 三、实验的结果验证 1.输入是语法分析后提供的正确的单词串,输出为三地址指令形式的四元式序列。 给出语句串: begin_a:=2+3*...
本项目“基于C语言实现简单语言递归下降语法制导翻译”深入探讨了这一主题,主要关注如何使用C语言来设计和实现一个编译器的核心部分——语义分析和中间代码生成。 首先,我们要理解什么是递归下降解析。递归下降是...
采用递归下降语法制导翻译法,对算术表达式、赋值语句进行语义分析并生成四元式序列。 实验的输入和输出 输入是语法分析提供的正确的单词串,输出为三地址指令形式的四元式序列。 例如:对于语句串 begin a:=2+3*4;x...