运算. 首先是单目运算指令, 相对而言比较简单, 只有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, 而运算符为 || 时情况正好相反.