1. 程式人生 > >編譯原理 符合表和語法分析

編譯原理 符合表和語法分析

符號表和語義分析

一、語義分析的內容

1. 遇到名稱 (變數名,函式名) 定義時

檢查是否重定義。(redefined)

2. 遇到名稱使用時

檢查是否未定義。(undefined)

3. 型別檢查

表示式中的運算,賦值,函式呼叫中的引數,都要檢查型別是否匹配或相容。

二、符號表

為了實現語義分析,使用符號表。

當定義一個名稱時,需要查詢符號表,看該名稱是否重定義。若是,報錯 redefined;否則把該名稱插入到符號表中。

當使用一個名稱時,需要查詢符號表,看該名稱是否未定義。若是,報錯 undefined。

為簡單起見,用連結串列實現符號表。產品級編譯器中,通常使用hash表來實現符號表。

三、作用域 (scope)

為了減少名稱的衝突,程式語言中使用作用域的概念。作用域指名稱(變數名,函式名等)的有效引用範圍。

在不同作用域中,可定義相同的名稱。在內層作用域中,可定義與外層作用域相同的名稱。

四、C語言中的作用域

有三種:

1. 全域性作用域

1) 全域性變數

2) 函式

函式內不能定義函式,所有函式是全域性的。從定義處直到檔案結束的範圍內,函式有效、可以引用。

2. 函式作用域

包括函式引數和函式內定義的區域性變數。只在該函式範圍內有效。

3. 塊作用域。

在塊語句(花括號)內定義的變數。只在該塊語句範圍內有效。

示例。

1 #include <stdio.h>

  2                       // 進入全域性作用域:level = 0

  3 int a, b;

  4

  5 int add(int a, int b) { //從引數表開始,進入函式作用域:level = 1

  6     int c;

  7     c = a + b;

  8     return c;

  9 }                         // 退出函式作用域:level = 0

 10

 11 int main() {            // 從引數表開始,進入函式作用域:level = 1

 12     int a, c;

 13     a = 90;  b = 20;

 14     c = add(a, b) / b;

 15

 16     if ( a > b )

 17     {                  // 進入塊作用域:level = 2

 18         int c = a;

 19         while ( c > b )

 20             c = c - b;

 21         a = c;

 22     }                  // 退出塊作用域:level = 1

 23

 24     printf("a=%d  b=%d  c=%d\n", a, b, c);

 25 }                       // 退出函式作用域:level = 0

五、最內巢狀作用域規則

在外層作用域定義的名稱,在內層作用域裡可見,除非內層作用域裡定義了相同的名稱。

在使用一個名稱時,按照從內層到外層的順序,來查詢該名稱的定義之處。

滿足這種規則的作用域,稱靜態作用域,也稱詞法作用域。

六、符號表的實現

用連結串列實現。每次插入符號,插入到表頭。

設定一個全域性變數 level,記錄當前作用域的層次 (深度)。

用一例加以說明。

$ cat -n t4.cmm

     1  int a, b;

     2

     3  int add(int a, int b) {

     4      int c;

     5      c = a + b;

     6      return c;

     7  }

     8

     9  int main() {

    10      int a, y;

    11      a = 100;  b= 20;

    12      y = a / b;

    13      print y;

14  }

    1) 一開始,進入全域性作用域, level = 0;

    2) 分析到一個函式,從引數表開始,進入函式作用域,level++;

分析完第4行後,符號表:

3) 每退出一個函式,釋放該函式加入到符號表的符號,level--;

分析完第7行後,符號表:(一個函式結束後,)

4) 進入下一個函式

分析完第10行後,符號表: