上一次构建语法树相关的节点,是在语法分析刚刚开始的时候,这一次又需要对它们动一动手脚,加入一些语义分析需要的成员函数。
这一次添加的主要是针对AbstractValueNode 及其子类(以后还会讨论关于指令生成相关的函数),现在需要把它变成这个样子
#define memberAbstractValueNode \ memberAbstractSyntaxNode \ int (*staticInt)(void*, int*); \ int (*staticReal)(void*, double*); \ AcceptType (*typeOf)(void*); struct AbstractValueNode { memberAbstractValueNode };
增加的函数中,前两个是用来在编译时确定该节点的整数或实数值的。它返回0时,表示可以取得编译时值,而后面的指针参数在返回后,指向的内存将存放该编译时值;否则返回1。在这里,我们认为VariableNode 不可以(即使刚刚赋过常数数值)在编译时确定其值,IntegerNode 及RealNode 可以确定编译时值,而BinaryOperationNode 和UnaryOperationNode 则看其子节点是否都能够确定编译时值。
那么,对于VariableNode、 IntegerNode和 RealNode ,可以这样实现它们对应的成员函数
// VariableNode 什么都可以
int cantRetrieveStaticInt(void* self, int* value)
{
return -1;
}
int cantRetrieveStaticReal(void* self, double* value)
{
return -1;
}
// IntegerNode
int scanIntNode4Int(void* self, int* value)
{
*value = ((struct IntegerNode*)self)->intValue;
return 0;
}
int scanIntNode4Real(void* self, double* value)
{
*value = (double)(((struct IntegerNode*)self)->intValue);
return 0;
}
// RealNode
int scanRealNode4Int(void* self, int* value)
{
*value = (int)(((struct RealNode*)self)->realValue);
return 0;
}
int scanRealNode4Real(void* self, double* value)
{
*value = ((struct RealNode*)self)->realValue;
return 0;
}
记得在newXXXNode 系列函数中对这些函数进行绑定。剩下是运算节点,可能还好点,UnaryOperationNode 这样基本上能行:
int scanUnaryOp4Int(void* self, int* value) { struct UnaryOperationNode* node = (struct UnaryOperationNode*)self; // 判断子节点 if (0 != node->operand->staticInt(node->operand, value)) { return -1; } // 运算,PLUS省略 if (MINUS == node->op) { *value = -*value; } else if (NOT == node->op) { *value = !*value; } return 0; } int scanUnaryOp4Real(void* self, double* value) { struct UnaryOperationNode* node = (struct UnaryOperationNode*)self; // 判断子节点 if (0 != node->operand->staticReal(node->operand, value)) { return -1; } // 运算,PLUS省略 if (MINUS == node->op) { *value = -*value; } else if (NOT == node->op) { // 报错:实型数不能用来作为条件 } return 0; }
但是对于BinaryOperationNode ,情况就比较挫了,为每一个运算符准备一个条件分支,这种事情就像看中国足球,看个一场两场还没问题,多了会让人疯掉的。我们得找个好一点的方式。
不妨先考虑一下这样一个函数指针
int (*intOperation)(int, int);
它计算两个整数(运算符不是其参数),返回它们的结果,如果我们有一堆这样的函数,将它们放在一个映射表里面,那么求BinaryOperationNode的 编译时值就好得多,如
int scanBinaryOp4Int(void* self, int* value) { struct BinaryOperationNode* node = (struct BinaryOperationNode*)self; int leftVal, rightVal, leftRetrievable = node->leftOperand->staticInt(node->leftOperand, &leftVal), rightRetrievable = node->rightOperand->staticInt(node->rightOperand, &rightVal); if (0 == leftRetrievable && 0 == rightRetrievable) { // TODO: 在这里插入语句 // 根据映射表,由 self->op 得到函数指针,调用运算并得到结果 return 0; } else { return -1; } }
而组织映射表最简单的方法是──数组。当然,数组就要很小心顺序了,这里我们对着AcceptType 的顺序来编写
typedef enum { END, IDENT, ELSE, IF, WHILE, READ, WRITE, BREAK, INTEGER_TYPE, REAL_TYPE, INTEGER, REAL, PLUS, MINUS, MULTIPLY, DIVIDE, ASSIGN, LT, LE, EQ, NE, GT, GE, AND, OR, NOT, COMMA, EOS, LPARENT, RPARENT, LBRACKET, RBRACKET, LBRACE, RBRACE, SKIP } AcceptType; // 增加几个宏 // 测试运算符类型 #define isArithOperator(x) (PLUS <= (x) && (x) < ASSIGN) #define isCompareOperator(x) (LT <= (x) && (x) <= GE) #define isLogicOperator(x) (AND <= (x) && (x) <= NOT) // 因为数组要从0开始,那么我们就用运算符减去这些 偏移量 #define ARITH_OPERATOR_OFFSET (PLUS) #define COMPARE_OPERATOR_OFFSET (LT) #define LOGIC_OPERATION_OFFSET (AND) // 整数运算函数入口地址数组 int (*intOperations[])(int, int) = { intPlus, intMinus, intMult, intDiv, NULL, intLT, intLE, intEQ, intNE, intGT, intGE, logicAnd, logicOr }; // 实型运算函数入口 // 算术运算 double (*realArith[])(double, double) = { realPlus, realMinus, realMult, realDiv }; // 比较运算,因为比较运算的返回值是整数,所以得与算术运算分开 static int (*realCompare[])(double, double) = { realLT, realLE, realEQ, realNE, realGT, realGE }; // 这些东西的实现很枯燥……见文章最后
那么之前那个代码块中的TODO就可以这样实现了
*value = intOperations[node->op - ARITH_OPERATOR_OFFSET](leftVal, rightVal);
当然,如果这么做,则可以规避显而易见的除0错误
if (DIVIDE == node->op && 0 == rightVal) { // 报错:除0错误 } else { *value = intOperations[node->op - ARITH_OPERATOR_OFFSET](leftVal, rightVal); }
不过这还远远没有结束。在编译时确定一个运算节点的常数值,还得考虑到这个节点的类型,比如由
4 * 2.3
得到的语法树节点,如果要求其编译时整数值,那么不可以取两个子节点的整数值再予以相乘(那样会得到8,而不是期望的9)。因此,在求值之前,得进行类型判断,必要时还得进行类型提升。以BinaryOperationNode 的staticInt 函数实现为例
int scanBinaryOp4Int(void* self, int* value) { struct BinaryOperationNode* node = (struct BinaryOperationNode*)self; // 编译时不进行赋值操作 if (ASSIGN == node->op) { return -1; } // 利用子节点的typeOf函数,求左右子节点的类型,typeOf函数以后再详述,现在假定已经实现 AcceptType leftType = node->leftOperand->typeOf(node->leftOperand), rightType = node->rightOperand->typeOf(node->rightOperand); // 左右子节点是否获得求编译时值 int leftRetrievable, rightRetrievable; if (INTEGER == leftType && INTEGER == rightType) { // 左右都是整数的情况 int leftVal, rightVal; leftRetrievable = node->leftOperand->staticInt(node->leftOperand, &leftVal); rightRetrievable = node->rightOperand->staticInt(node->rightOperand, &rightVal); if (0 == leftRetrievable && 0 == rightRetrievable) { if (DIVIDE == node->op && 0 == rightVal) { semanticErr(div0Err, node->line); } else { *value = intOperations[node->op - ARITH_OPERATOR_OFFSET] (leftVal, rightVal); } return 0; } else { return -1; } } else { // 如果有一边不是整数,那么 // 首先看是不是逻辑运算,如果是,那么报错 if (isLogicOperator(node->op)) { // 报错:实型不可以作为逻辑值 return -1; } double leftVal, rightVal; leftRetrievable = node->leftOperand->staticReal(node->leftOperand, &leftVal); rightRetrievable = node->rightOperand->staticReal(node->rightOperand, &rightVal); if (0 == leftRetrievable && 0 == rightRetrievable) { if (isCompareOperator(node->op)) { // 如果是比较运算,那么调用比较运算数组中的对应函数 *value = realCompare[node->op - COMPARE_OPERATOR_OFFSET] (leftVal, rightVal); } else { // 否则调用算术运算数组中的对应函数,并将结果转为整数赋予value指向的地址 *value = (int)(realArith[node->op - ARITH_OPERATOR_OFFSET] (leftVal, rightVal)); } return 0; } else { return -1; } } }
外层判断类型,内层进行运算。这里又提出另一个问题,就是类型如果多了怎么办?(也许需要Mixin)欢迎大家就此问题提出宝贵建议,谢谢。
获取其实型值基本上照猫画虎就可以了
int scanBinaryOp4Real(void* self, double* value) { struct BinaryOperationNode* node = (struct BinaryOperationNode*)self; if (ASSIGN == node->op) { return -1; } AcceptType leftType = node->leftOperand->typeOf(node->leftOperand), rightType = node->rightOperand->typeOf(node->rightOperand); int leftRetrievable, rightRetrievable; if (INTEGER == leftType && INTEGER == rightType) { // 都是整数的情况,没什么好说的 int leftVal, rightVal; leftRetrievable = node->leftOperand->staticInt(node->leftOperand, &leftVal); rightRetrievable = node->rightOperand->staticInt(node->rightOperand, &rightVal); if (0 == leftRetrievable && 0 == rightRetrievable) { *value = (double)(intOperations[node->op - ARITH_OPERATOR_OFFSET] (leftVal, rightVal)); return 0; } else { return -1; } } else { if (isLogicOperator(node->op)) { semanticErr(takeRealAsCondition, node->line); return -1; } double leftVal, rightVal; leftRetrievable = node->leftOperand->staticReal(node->leftOperand, &leftVal); rightRetrievable = node->rightOperand->staticReal(node->rightOperand, &rightVal); if (0 == leftRetrievable && 0 == rightRetrievable) { if (isCompareOperator(node->op)) { // 现在是这里转型为实型 *value = (double)(realCompare [node->op - COMPARE_OPERATOR_OFFSET](leftVal, rightVal)); } else { *value = realArith[node->op - ARITH_OPERATOR_OFFSET] (leftVal, rightVal); } return 0; } else { return -1; } } }
附:运算函数实现
static int intPlus(int a, int b) { return a + b; } static int intMinus(int a, int b) { return a - b; } static int intMult(int a, int b) { return a * b; } static int intDiv(int a, int b) { return a / b; } static int intLT(int a, int b) { return a < b; } static int intLE(int a, int b) { return a <= b; } static int intEQ(int a, int b) { return a == b; } static int intNE(int a, int b) { return a != b; } static int intGT(int a, int b) { return a > b; } static int intGE(int a, int b) { return a >= b; } static int logicAnd(int a, int b) { return a && b; } static int logicOr(int a, int b) { return a || b; } static double realPlus(double a, double b) { return a + b; } static double realMinus(double a, double b) { return a - b; } static double realMult(double a, double b) { return a * b; } static double realDiv(double a, double b) { return a / b; } static int realLT(double a, double b) { return a < b; } static int realLE(double a, double b) { return a <= b; } static int realEQ(double a, double b) { return a == b; } static int realNE(double a, double b) { return a != b; } static int realGT(double a, double b) { return a > b; } static int realGE(double a, double b) { return a >= b; }
判别除0错误也许放在intDiv 里面更好一些……