[命令生成]構文による命令生成[4]
12041 ワード
演算まず単眼演算命令ですが、相対的に簡単で、3つのケースしかありません
両目演算は、それを前提に3つの分岐(うん、3つ)に分割して行います.
命令生成メンバー関数ではスケジューリングするだけでよい
賦値演算関数insAssignment参考実現.左右の値の種類が一致するかどうかに注意する必要がある.
通常の演算も同様である、タイプに注意が必要である.異なるのは、付与演算では、左右の値タイプが一致しない場合、右の値タイプが左に変換されます.しかし、通常の演算では、タイプは精度の高い方にアップする(INTEGER->REAL).したがって、タイプ不一致の場合の処理方式と付与演算とは著しく異なる.
最後に条件短絡である.考え方の参考はこの文章である.いくつかの設計では、論理値は分岐文と循環文の出口と密接に関連している.しかし、Jerry言語で論理値を求める場合、コンテキストが欠けているため(このようなことをあまり複雑にしないでください)、Jerryが実装されるときは、構文ノードに対応する命令の実行が終了したときにスタックの上に真値または偽値(1または0)を置くだけです.
興味深いことに、&&と||の構造はこのように類似しており、両者が生成する命令は2つしか異なる:演算子が&&の場合、短絡条件はスタックトップが偽であり、式結果は0であり、演算子が|の場合、状況は正反対である.
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); // , ...
}
興味深いことに、&&と||の構造はこのように類似しており、両者が生成する命令は2つしか異なる:演算子が&&の場合、短絡条件はスタックトップが偽であり、式結果は0であり、演算子が|の場合、状況は正反対である.