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

2021-04-13 00:48:41 字數 4837 閱讀 2871

在語法分析裡,最主要的組成部份是宣告分析,並且這是

c語言編譯器最複雜的組成部分。由於任何變數都需要宣告,那麼怎麼樣知道這個變數宣告是合法的呢?現在帶著這個問題去分下面的**。

為了理解**的工作,先來看前面的例子裡的第一行有效**:

typedef unsigned int size_t;

在這句語句裡,使用型別定義關鍵字來宣告了乙個無符號整數的型別

size_t

,為了識別這句語句的語法,那麼最開始的是型別關鍵字,它相當於儲存型別。接著是無符號、整型,最後才是標識

id。其實上面這句語句也可能有這種形式,如下:

typedef int size_t;

那麼上面這句就上面那句少了乙個無符號的說明。

要分析這種宣告,那麼就需要乙個函式來把這些屬性整理出來,並斷它的合法性。

lcc裡的宣告分析是在函式

decl

裡呼叫函式

specifier

來實現的:

#001 //說明

#002 static type specifier(int *sclass)

#003    

#012  第

2行裡輸入了獲取宣告的儲存型別引數

sclass。

乙個宣告變數有可能出現的說明符就有以下幾種:

儲存型別、常量說明、符號說明、型別大小、型別、是否刪除。

比如像這些語句:

auto int a;

register int iloop;

auto unsigned int nret;

static unsigned int g_nret;

const unsigned int cnstret; 第

4行裡就是定義上面幾種說明的型別變數儲存。 第

5行裡定義了返回型別儲存變數。 第

7行是把所有型別說明初始化為

0值,表示沒有出現這種說明。 第

8行到第

11行是把儲存型別設定為自動型別。由於在

c語言裡,所有變數宣告如果沒有明確地指明儲存型別時,預設就是自動型別

auto。

下面就通過迴圈來分析出

6種說明:

#013  for (;;)

#014  

#054               p = &size;

#055               t = gettok();     

#056               break;

上面識別

long

型別和long long的64

位的型別。

#057         case short:   

#058               p = &size;

#059               t = gettok();     

#060               break;

#061         case void:

#062         case char:

#063         case int:

#064         case float:

#065         case double:  

#066               p = &type;

#067               ty = tsym->type;

#068               t = gettok();     

#069               break;

上面這些都是簡單型別的識別。

#070         case enum:    

#071               p = &type;

#072               ty = enumdcl();   

#073               break;

上面是列舉型別識別,呼叫函式

enumdcl

進行型別的分配。後面再仔細地討論怎麼樣處理列舉型別的成品。

#074         case struct:

#075         case union:   

#076               p = &type;

#077               ty = structdcl(t);

#078               break;

上面結構定義和聯合的識別,這也是比較複雜的型別,所以也呼叫

structdcl

來進一步處理結構體。

#079         case id:

#080               if (istypename(t, tsym) && type == 0

#081                    && sign == 0 && size == 0)

#082              

#098 

#099                    p = &type;

#100                    t = gettok();

#101               }

#102               else

#103                  

#106               break;

當把所有的說明符分析完成後,最後肯定是剩下乙個

id,如果不是就有可能出錯的。 在第

80行到第

101行裡處理

id是自己定義的型別處理,比如用

typedef

定義的id

型別,就需要在那裡識別出型別的屬性。

如果這個

id是變數,就會在第

104行設定為空,並且在後面的第

111行到第

114行裡跳出來

for迴圈。

#107         default:

#108               p = null;

#109         }

#110 

#111         if (p == null)

#112            

#115 

#116         if (*p)

#117            

#120 

#121         *p = tt;

#122  }

上面在第

113行裡跳出了

for迴圈,意味著整宣告已經分析完成,接著就是把所有分析出來的說明符組成屬性結構,儲存了到相應的符號表裡。

#123 

#124  if (sclass)

#125         *sclass = cls;

#126 

#127  if (type == 0)

#128  

#132  第

124行到第

125行儲存儲存型別返回給呼叫函式。 第

127行到第

131行是設定型別為預設值。

#133  if (size == short     && type != int

#134          || size == long+long && type != int

#135         || size == long      && type != int && type != double

#136         || sign && type != int && type != char)

#137     

#140  第

133行到第

136行裡都判斷宣告組合是否合法,如果不合法的組合,就需要提示出錯。

#141  if (type == char && sign)

#142     

#145  else if (size == short)

#146  

#149  else if (size == long && type == double)

#150     

#153  else if (size == long+long)

#154  

#159  else if (size == long)

#160     

#163  else if (sign == unsigned && type == int)

#164     

#167  第

141行到第

166行就是根據符號和型別來判斷宣告的型別,由於型別的大小不同,符號位不同,而組成不同的型別。這些型別都是

c編譯器預先定義好的。不知道你還記起最開始的型別初始化沒有?如果不記起,就回過頭去看看。

#168  if (cons == const)

#169         ty = qual(const, ty);

#170 

#171  if (vol == volatile)

#172         ty = qual(volatile, ty);

#173 

#174  return ty;

#175 } 第

168行把型別新增常量屬性。 第

171行是型別新增不可刪除屬性。 第

174行就把宣告的型別返回給呼叫函式。

通過上面的**,就可以把

c裡的宣告分析出來,如果不合法的宣告就會出錯。如果合法的宣告,就返回相應的型別屬性。只要有了宣告的型別屬性,那麼乙個變數的定義就完全了解了,也就是說它的語義已經確定下來。只剩下了乙個問題沒有解決?這個問題是什麼呢?下一次再告訴你吧,這一次分析的**夠長的了。

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

上一次把宣告的說明符已經分析得很清楚,也就是把 c的變數和函式宣告都已經了解了。最後還剩下乙個問題沒有解決,這個問題就是宣告後面的 id是變數呢?還是函式?或者是指標?為了識別後面的 id,下面來看乙個例子。如下的語句 typedef unsigned int size t 這是第一行處理的 它通過...

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

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

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

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