編譯器開發系列--Ocelot語言5.表達式的有效性檢查

本篇將對「1=3」「&5」這樣沒法求值的不正確的表達式進行檢查。java

將檢查以下這些問題。
●爲沒法賦值的表達式賦值(例:1 = 2 + 2)
●使用非法的函數名調用函數(例:"string"("%d\n", i))
●操做數非法的數組引用(例:1[0])
●操做數非法的成員引用(例:1.memb)
●操做數非法的指針間接引用(例:1->memb)
●對非指針的對象取值(例:*1)
●對非左值的表達式取地址node

具體例子以及問題的檢測方法如表10.1所示,其中包括了剛纔列舉的問題。express

非指針類型取值操做的檢查數組

    /*非指針類型取值操做的檢查
     * 表示取值運算符(*)的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;
    }

隱式的指針生成spa

單個數組類型或函數類型的變量表示數組或函數的地址。例如,假設變量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」的值都是相同的。對象

相關文章
相關標籤/搜索