[语义分析]语法树节点波澜再起[编译时值及常数折叠]

    上一次构建语法树相关的节点,是在语法分析刚刚开始的时候,这一次又需要对它们动一动手脚,加入一些语义分析需要的成员函数。

    这一次添加的主要是针对AbstractValueNode 及其子类(以后还会讨论关于指令生成相关的函数),现在需要把它变成这个样子

#define memberAbstractValueNode        \
    memberAbstractSyntaxNode           \
    int (*staticInt)(void*, int*);     \
    int (*staticReal)(void*, double*); \
    AcceptType (*typeOf)(void*);
struct AbstractValueNode {
	memberAbstractValueNode
};

增加的函数中,前两个是用来在编译时确定该节点的整数或实数值的。它返回0时,表示可以取得编译时值,而后面的指针参数在返回后,指向的内存将存放该编译时值;否则返回1。在这里,我们认为VariableNode 不可以(即使刚刚赋过常数数值)在编译时确定其值,IntegerNodeRealNode 可以确定编译时值,而BinaryOperationNodeUnaryOperationNode 则看其子节点是否都能够确定编译时值。

    那么,对于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)。因此,在求值之前,得进行类型判断,必要时还得进行类型提升。以BinaryOperationNodestaticInt 函数实现为例

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 里面更好一些……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值