C語言解譯器的實現–運算式解析(四)

來源:互聯網
上載者:User

1. BNF定義

2.運算式解析

3. 尾碼運算式

4.尾碼運算式到中間代碼

5.中間代碼的表示

1. BNF定義

   雖然不想多提理論知識,但是有些東西還是避免不了。在解析運算式的時候,我們必須知道它的BNF定義,這樣解析起來就非常方便了。所謂的BNF定義,相信大家看一眼就知道了:
   exp_additive           -> exp_multiplicative ( "+"|"-" ) exp_multiplicative
   exp_multiplicative   -> exp_cast ( "*"|"/"|"%" ) exp_cast
   exp_cast                 -> ...
   意思是:
   加法運算式可以表示為  “乘法運算式 + 乘法運算式”
   乘法運算式可以表示為  “類型轉換運算式 *或/或% 類型轉換運算式”
   ...
  
   知道了整個C語言的BNF定義,我們就可以很簡單的按照這個定義來解析了。整個C的BNF定義可以查看以下的連結:
   http://lists.canonical.org/pipermail/kragen-hacks/1999-October/000201.html

  
2. 運算式解析

   知道了上面的BNF定義,那麼我們的解析代碼就可以這麼寫:
   void exp_additive(){
       char op;
       exp_multiplicative();
       while(
           (op = OPERATOR( '+' )) ||
            op = OPERATOR( '-' )) ){
            get_token();
            exp_multiplicative();
            ...
       }
   }

    void exp_multiplicative(){
        char op;
        exp_cast();
        while(
            (op = OPERATOR( '*' )) ||
            (op = OPERATOR( '/' )) ||
            (op = OPERATOR( '%' )) ){
            get_token();
            exp_cast();
            ...
        }
    }
    過程是這樣的:
    a. 調用exp_additive時,先調用exp_multiplicative
    b. 然後判斷後面是否是 + 或 -,如果是,再次調用exp_multiplicative
    這樣就完成了加法運算式的解析。如果非要問為什麼這麼寫就能解析出運算式,那麼我們可以舉個例子:
        a = a * b + c * d;
    那麼,他的文法樹應該是這樣的:

    
    (圖4.2 文法樹)
    我們向下遞迴調用的過程,其實就是構造這個文法樹的過程。但是我們不會真的建立出這個文法樹,而是儲存了一個與它等價的一種形式--尾碼運算式,其實尾碼運算式就是文法樹的後續遍曆。

 

3. 尾碼運算式

   什麼是尾碼運算式?我們還是從例子出發,上面的運算式,轉化成尾碼運算式就是這樣子的:
        a a b * c d * + =
   為什麼要寫成這種奇怪的形式?我們不是吃飽了撐著,從左往右分別查看這個運算式您就知道原因了。
        a 
        a 
        b 
        *  得到*號,那麼拿前面的兩個變數a b求和
        c
        d
        *  得到*號,那麼拿前面的兩個變數c d求和
        +  的到+號,擷取前面的兩個變數 a*b  c*d 的結果,求和
        =  得到=號,將前面的結果賦給a
   為了產生尾碼運算式,我們要改造上面的解析函數。
   void exp_additive(){
       char op;
       exp_multiplicative();
       while(
           (op = OPERATOR( '+' )) ||
            op = OPERATOR( '-' )) ){
            get_token();
            exp_multiplicative();
            EXP_OPR( op );           <--將運算子入棧
       }
   }

    void exp_multiplicative(){
        char op;
        exp_cast();
        while(
            (op = OPERATOR( '*' )) ||
            (op = OPERATOR( '/' )) ||
            (op = OPERATOR( '%' )) ){
            get_token();
            exp_cast();
            EXP_OPR( op );           <--將運算子入棧
        }
    }
    那麼解析完成以後,我們的棧中就會形成尾碼運算式了。有了運算式的尾碼形式,我們就可以很輕鬆的產生尾碼運算式的中間代碼了。
  

4.尾碼運算式到中間代碼

  首先我們先說明一下我們的中間代碼是怎樣的一種形式,這裡暫且叫它為三元運算式,是因為這個種中間代碼的形式是固定的。例如,緊接上節的例子,運算式 a = a * b + c * d;的中間代碼最終應該是這樣子的:
  @1 = a * b;
  @2 = c * d;
  @3 = @1 + @2;
  @4 = a  = @3;
  其中以@開頭的都是我們為之產生的中間變數。產生上述的中間代碼後,將會對我們後續的解析提供很大的協助,應為它結構固定,所以我們不用再去解析來源程式,而是通過這個中間代碼產生最終的執行代碼。這裡先聲明下,我所說的執行代碼,不是真正意義上得可執行代碼,而是能夠被我的軟體解析的命令序列。其實它已經非常接近彙編代碼。但是我們的目標是解析執行,並不產生彙編代碼,所以產生簡單的命令序列已經可以完成目標了。
 
  我們前面解析運算式,產生尾碼形式,為的就是生產這種中間運算式。運算式"a = a * b + c * d;"的尾碼形式是"a a b * c d * + =;" 我們要根據這個尾碼形式產生中間代碼的過程如下:
    

   
   
5.中間代碼的表示

  typedef struct _code code_t;
  typedef struct _code * pcode_t;
  struct _code{
      char opr;
      struct{
          int  i, n, t;
      }lab;
      v_t var[4];
      code_t * next;
  };
  它是一個鏈表,每個節點儲存了一個形如"@1 = a * b;"的中間代碼。其中,opr表示運算子"*";lab表示該節點為一個LAB,留到後面章節講解;var表示運算變數,如上面運算式的"@1, a, b"。
  這樣子,當一個運算式解析完成後,會產生一個鏈表,表示該運算式的中間代碼。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.