上一次构建语法树相关的节点,是在语法分析刚刚开始的时候,这一次又需要对它们动一动手脚,加入一些语义分析需要的成员函数。
这一次添加的主要是针对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 里面更好一些……
本文介绍如何在编译阶段确定语法树节点的整数或实数值,包括变量、整数、实数节点的处理,以及如何通过递归和类型转换处理二元运算节点。

被折叠的 条评论
为什么被折叠?



