C語言解釋器的實現--表達式解析(四)


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"
  這樣子,當一個表達式解析完成后,會生成一個鏈表,表示該表達式的中間代碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM