LCC編譯器的源程式分析 10 宣告型別

2021-08-22 08:57:59 字數 3454 閱讀 2891

上一次把宣告的說明符已經分析得很清楚,也就是把

c的變數和函式宣告都已經了解了。最後還剩下乙個問題沒有解決,這個問題就是宣告後面的

id是變數呢?還是函式?或者是指標?為了識別後面的

id,下面來看乙個例子。如下的語句:

typedef unsigned int size_t;

這是第一行處理的**,它通過函式

specifier

處理後,已經就把

typedef

、unsigned

、int

處理完成,還剩下

size_t

沒有處理。從函式返回

specifier

後,接著幾次遞迴呼叫才把它處理完成,現在就來看看分析這句語句的函式呼叫關係。如下所示:

#001program

#002decl(dclglobal)

#003 specifier

#004 dclr

#005 dclr1

程式先呼叫函式

program

,接著呼叫

decl

函式,緊跟著呼叫

specifier

和dclr

,最後呼叫

dclr1

來分析,才完成這句語句的處理。其實像語句(

typedef unsigned int size_t

)只要調到

specifier

就已經差不多了,後面的

dclr

和dclr1

大部分都是處理指標和函式的宣告。

通過上面的分析,知道這幾個函式的呼叫關係,心裡已經有底了,但還需要更加深入去體會**,才能真正地理解它。由於在

dclr

函式第一行就遞迴呼叫

dclr1

,因此需要先分析函式

dclr1

,再回來分析

dclr

函式。

#001static type dclr1(char **id, symbol **params, int abstract)

#002

#012 else

#013

#016

#017 t = gettok();

#018 break; 第

3行是定義返回型別。 第

5行是通過識別當前的記號來處理後面的宣告變數。比如在上面例子裡的

t,就已經是

size_t

,而size_t

的記號是

id。因此第7行到

18行,就是處理

id情況。 第

10行是儲存返回的

id字串。 第

14行是出錯的提示。 第

17行是取下乙個記號。

下面的**都處理更複雜的宣告,現在先把它們放下,後面通過例子來解釋就更加容易理解了。

#019case '*':

#020 t = gettok();

#021 if (t == const || t == volatile)

#022

#032 else

#033 ty = dclr1(id, params, abstract);

#034

#035 ty = tnode(pointer, ty);

#036 break;

#037case '(':

#038 t = gettok();

#039 if (abstract

#040 && (t == register || istypename(t, tsym) || t == ')'))

#041

#053 else

#054

#062 break;

#063case '[': break;

#064default:return ty;

#065}

#066

#067while (t == '(' || t == '[')

#068

#089 break;

#090 case '[': //

資料宣告。

#091 t = gettok();

#092

#102 }

#103 else

#104 expect(']');

#105

#106 ty = tnode(array, ty);

#107 ty->size = n;

#108 }

#109 break;

#110 default:

#111 assert(0);

#112 }

#113}

#114return ty;

#115}

上面的函式分析例子

size_t的id

後,就直在第

114行返回型別

ty,但

ty是空的,因為它沒有其它復合的型別在裡面。

通過上面函式

dclr1

的處理,就把

id識別出來,那麼這句語句(

typedef unsigned int size_t;

)已經完全分析完成,也就是完成語法的分析。

接著下來就返回到呼叫函式

dclr

,它的**如下:

#001//

基本宣告分析函式。

#002static type dclr(type basety, char **id, symbol **params, int abstract)

#003

#026

#027}

#028

#029if (aflag >= 2 && basety->size > 32767)

#030 warning("more than 32767 bytes in `%t'/n", basety);

#031

#032return basety;

#033}

#034 第

4行裡從函式

dclr1

返回來的

ty是空的,因此

for迴圈是不會執行的。 第

11行到第

13行是處理指標型別。 第

14行到第

17行是處理函式型別宣告。 第

18行是處理陣列型別宣告。 第

21行是處理常量和不能刪除型別宣告。 在第

32行裡把傳送入來的型別直接返回了。

通過上面兩個函式的分析,肯定是完成了所有宣告部份的處理,並且把宣告的型別屬性儲存在

basety

返回。如果還不理解宣告的型別屬性,就可以看看前面的文章,已經介紹了型別初始化的型別結構定義,當然初始化的型別都是基本型別,但在這裡是會有擴充套件型別的,主要儲存在型別結構的

type

欄位裡。

時間又到了,下次再仔細地分析怎麼樣把分析出來的型別與

id儲存到符號表裡。

LCC編譯器的源程式分析 9 宣告分析

在語法分析裡,最主要的組成部份是宣告分析,並且這是 c語言編譯器最複雜的組成部分。由於任何變數都需要宣告,那麼怎麼樣知道這個變數宣告是合法的呢?現在帶著這個問題去分下面的 為了理解 的工作,先來看前面的例子裡的第一行有效 typedef unsigned int size t 在這句語句裡,使用型別...

LCC編譯器的源程式分析 14 結構型別的宣告

以前都是簡單型別的識別和語法分析,現在來分析結構的宣告,它是比較複雜的一種資料型別,但結構在編寫程式中使用是非常多的。由於程式的方程式就是 資料結構 演算法 程式現在物件導向的方程式是 資料結構 演算法 物件 物件 物件 程式 由上面的公式,就可以看出程式中的資料結構是非常重要的,無論是物件導向的程...

LCC編譯器的源程式分析 17 引數變數的宣告

函式裡的引數變數個數不固定,因此也需要檢查這些引數的名稱是否相同,還需要檢查型別的合法性。現在就來分析上次提到的函式 dclparam 它的 如下 001 引數型別宣告處理 002 static symbol dclparam int sclass,char id,type ty,coordinat...