编译器开发系列--Ocelot语言5.表达式的有效性检查

本篇将对“1=3”“&5”这样无法求值的不正确的表达式进行检查。

将检查如下这些问题。
●为无法赋值的表达式赋值(例:1 = 2 + 2)
●使用非法的函数名调用函数(例:"string"("%d\n", i))
●操作数非法的数组引用(例:1[0])
●操作数非法的成员引用(例:1.memb)
●操作数非法的指针间接引用(例:1->memb)
●对非指针的对象取值(例:*1)
●对非左值的表达式取地址

具体例子以及问题的检测方法如表10.1所示,其中包括了刚才列举的问题。

非指针类型取值操作的检查

    /*非指针类型取值操作的检查
     * 表示取值运算符(*)的DereferenceNode的处理。
     * 该方法检查取值运算符的操作数的类型是否为指针。
     */
    // #@@range/DereferenceNode{
    public Void visit(DereferenceNode node) {
    	/*
    	 * 首先,通过super.visit(node) 调用基类Visitor 的方法遍历操作数(node.expr())
		(即检查操作数)。
    	 */
        super.visit(node);
        /*
         * 接着,调用操作数node.expr() 的isPointer 方法,检查操作数的类型是否是指针,
			即检查是否可以进行取值。如果无法取值,则调用undereferableError 方法输出编译错误。
         */
        if (! node.expr().isPointer()) {
            undereferableError(node.location());
        }
        /*
         * 最后,调用handleImplicitAddress 方法对数组类型和函数类型进行特别处理。该处
			理还和接下来AddressNode 的处理相关,
         */
        handleImplicitAddress(node);
        return null;
    }

获取非左值表达式地址的检查

    /*获取非左值表达式地址的检查
     * 检查操作数是否为左值。表示地址运算符的AddressNode 的处理
     */
    // #@@range/AddressNode{
    public Void visit(AddressNode node) {
        super.visit(node);
        /*
         * 首先对node.expr() 调用isLvalue 方法,检查&expr 中的expr 是否是可以进行取
			址操作的表达式。
			ExprNode#isLvalue 是检查该节点的表达式是否能够获取地址的方法。
         */
        if (! node.expr().isLvalue()) {
            semanticError(node.location(), "invalid expression for &");
        }
        /*
         * 剩余的语句用于确定AddressNode 的类型。通常node.expr().isLoadable() 会
			返回true,即执行else 部分的处理。&expr 的类型是指向expr 类型的指针,因此指向
			node.expr().type() 的指针类型可以作为节点整体的类型来使用。
         */
        Type base = node.expr().type();
        /*
         * 在将puts 的类型设置为指向函数的指针的同时,还必须将&puts 的类型也设置为指向函
			数的指针。
			node.expr() 的类型是数组或函数的情况下进行特别处理,使得&puts 的类型
			和puts 的类型相一致。
         */
        if (! node.expr().isLoadable()) {
            // node.expr.type is already pointer.
            node.setType(base);
        }
        else {
            node.setType(typeTable.pointerTo(base));
        }
        return null;
    }

隐式的指针生成

单个数组类型或函数类型的变量表示数组或函数的地址。例如,假设变量puts 的类型为函数类型(一般称为函数指针),那么puts 和&puts 得到的值是相同的。

    /*
     * handleImplicitAddress 方法将数组类型或函数类型转换为了指向
		数组或函数类型的指针,即隐式地生成指针类型。
     */
    private void handleImplicitAddress(LHSNode node) {
        if (! node.isLoadable()) {
            Type t = node.type();
            if (t.isArray()) {
                // int[4] ary; ary; should generate int*
                node.setType(typeTable.pointerTo(t.baseType()));
            }
            else {
                node.setType(typeTable.pointerTo(t));
            }
        }
    }

puts 是指向函数的指针,因此它的取值运算*puts 的结果是函数类型,但这样又会隐式地转换为指向函数的指针。*puts 还是指向函数的指针,因此仍然可以进行取值运算,仍然会转换为指向函数的指针。像这样可以无限重复下去。所以C 语言中“&puts”“puts”“*puts”“**puts”“***puts”的值都是相同的。

 

posted @ 2016-12-21 19:39  是非猫  阅读(430)  评论(0编辑  收藏  举报