C語言解譯器的實現–文法解析(五)

來源:互聯網
上載者:User
1.代碼塊

  代碼塊是由多個運算式組成的一組代碼。它可以看成是以下的形式:
  {
      exp1
      exp2
      ...
  }
  它由"{"開始,由"}"結束,中間包含多條運算式,或者是控制語句。如果不是以"{"開始,那麼,一個代碼塊就是一條運算式。在上面的章節,我們已經介紹過了,每個運算式會產生一個中間代碼。它是一個鏈表 struct _code * ,而一個代碼塊,是由多個運算式組成的,所以我們將每個運算式的中間代碼鏈表連到一起就成了代碼塊的中間代碼了。
  如果代碼塊中包含控制語句,那麼,我們必須做一些處理,即在代碼鏈表中插入跳躍陳述式,和跳轉位置(Lab)。

 
2.控制語句

2.1 C語言中,控制語句有這些:
  a. if( exp ) stmt else stmt
  b. do stmt while( exp )
  c. while( exp ) stmt
  d. for( exp1; exp2; exp3 ) stmt
  e. switch( exp ) stmt
  f. goto lab
  其中,stmt表示一個代碼塊。我們如何為這些代碼產生中間代碼呢?這裡還要說明的是跳躍陳述式。比如一個if語句:
      if( exp ) stmt1 else stmt2
  那麼,它的意思是,當 exp == 0 時,跳轉到stmt2位置;當exp != 0的時候不做跳轉,但是stmt1執行完成後要跳轉到stmt2的後面。所以,這中間涉及了兩個東西:跳躍陳述式 和 跳轉的位置。跳躍陳述式我們用三種命令表示:JE、JNE、JMP,即不等於跳轉,等於跳轉,無條件跳轉。 跳轉的位置我們用Lab表示,即在代碼鏈表中插入一個標籤,供跳躍陳述式尋找要跳轉的位置。
  還是上面的if語句,它產生後的代碼應該是這樣的:
  
  A.  if( exp ) stmt1 else stmt2 -->
        exp
        JE L1
        stmt1
        JMP L2
      L1: 
        stmt2
      L2: 
    
  其中,L1 L2分別佔用代碼鏈表的一個節點,在code_t結構體中,用lab域表示。

2.2 控制語句中的break和continue.
  在一些控制語句中,他們支援break和continue,即如果在代碼塊總出現break,那麼他應該跳轉到代碼塊的外面,如果是continue,那麼跳轉到條件陳述式繼續執行。例如下面的do while語句:
  
  B.  do stmt while( exp ) -->
      L1:
        stmt     <-- 如果這裡出現break,那麼JMP L3; 如果出現continue, 那麼JMP L2
      L2:
        exp
        JNE L1
      L3:
  因為在解析stmt的時候,L1,L2和L3都已經固定好了,所以,在處理break和continue的時候,跳轉的LAB都已經明確,可以用參數將L2和L3傳遞個stmt()函數,stmt函數中解析break和continue的時候,僅僅是添加一條跳躍陳述式。

2.3 其他控制語句的代碼形式
  
  C. while( exp ) stmt -->
        JMP L2
     L1:
        stmt
     L2:
        exp
        JNE L1
     L3:
    
  D. for( exp1; exp2; exp3 ) stmt -->
        exp1
        JMP L3
     L1:
        stmt
     L2:
        exp3
     L3:
        exp2
        JNE goto L1
     L4:
  
  E. switch( exp ){
     case 1: stmt1
     case i: stmti
     default: stmt
  ...
     }
    
         exp
         selete i and jmp(L1..Ln,L) 
     Li: stmti
     L:  stmt
     LL:
     
     selete i and jmp(L1..Ln,L) 表示 如果exp的結果是i,那麼跳轉到Li,否則跳轉到L。switch語句跟別的控制語句不一樣,其他的控制語句在還沒解析代碼塊的時候,我們就已經知道應該建立幾個Lab了,所以我們可以事先建立好Lab,然後在適當的位置插入JMP語句,這個JMP語句中跳轉到的Lab這時候已經確定了。但是對於switch語句,我們事先不知道case在什麼地方,所以不知道"selete i and jmp(L1..Ln,L)"應該對應什麼代碼。所以,我們必須解析完stmt(代碼塊)之後才能產生代碼。 具體的做法是在解析代碼快的時候記錄下所以的Lab,解析完成後再做相應的處理,即構造"selete i and jmp(L1..Ln,L)"代碼,將它串連到中間代碼的前面。
     
  F. goto Lab -->
     JMP Lab
     
     在解析goto的時候,必須將"Lab"名稱轉換成我們的Lab的表示形式。

 
3.局部變數的生命週期

  在一個函數中定義的變數稱之為局部變數,但是局部變數有自己的生命週期,即在自己的代碼塊中定義的,那麼它只對這個代碼塊的代碼可見。例如有下面的代碼:
    {
        int a;
        {
            int a;
        }
        printf("%d\n", a);
    }
  那麼第二個a對printf語句處是不可見的。為了表示變數的生命週期,我們為每個變數加入了begin和end域,用來儲存該變數對[begin,end]區間的代碼是可見的。所以,這裡begin,和end怎麼解析是個問題,begin不難,在解析定義的時候就可以確定,但是end確實比較難,因為必須在一個代碼塊中結束後(即解析到"}"後),才知道end的值。所以為了確定end的值,棧在這裡又被徵用了。
   
    {                          <-- 代碼塊開始,建立一個stack1
        int a;                 <-- 解析完a,將a壓入stack1, 此時 a.begin已經確定
        {                      <--     遇到"{" 遞迴調用解析函數,建立一個stack2
            int a;             <--     解析完a,將a壓入stack2, 此時 a.begin已經確定
        }                      <--     遇到"}",表示該代碼塊完成,將a從stack2中pop出來,設定a.end !
                                       此時,遞迴調用結束。返回到上一個代碼塊處理函數。
        printf("%d\n", a);     <--
    }                          <-- 到"}",表示該代碼塊完成,將a從stack1中pop出來,設定a.end !
   
    經過上面的過程,第一個a和第二個a的begin和end值都被確定。在代碼的處理過程中,我們根據變數名尋找變數時,必鬚根據當前代碼的位置,來判斷位置是否屬於[begin,end]區間,而不僅僅是判斷變數名。

4.函數解析

  一個函數包括這幾個部分:
  a. 傳回值類型
  b. 形參列表
  c. 局部變數
  d. 代碼塊
  例如下面的函數:
    int add( int a, int b )
    {
        int c;
        c = a + b;
        return c;
    }
  那麼它的傳回值類型是int, 參數列表是a、b,局部變數有c, 執行代碼是 " c = a + b; return c; " 。仔細觀察,它其實是由函式宣告和一個代碼塊組成的。所以解析這個函數也很簡單,其實就是解析聲明,得到函數名,參數列表和傳回值類型。然後執行上一章節描述的解析代碼塊函數,得到該函數的中間代碼鏈。

5.附

  比如有如下的代碼:
  int main( int argc, char **argv ){
      int a, b;
      b = 1;
      for( a=0; a<10; a++ ){
          b *= 2;
      }
      return b;
  }
  那麼這個函數所對應的中間代碼是這樣的:
  fun: main 2-args: argc argv b a
       @0 = b = 1
       @1 = a = 0
       JMP 7
    LAB_5:
       @4 = b *= 2
    LAB_6:
       @3 = a ++
    LAB_7:
       @2 = a < 10
       JNE 5
    LAB_8:
       @5 = 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.