ucc編譯器分析與總結 3 宣告檢查

2021-09-25 10:17:18 字數 3343 閱讀 2568

有了型別系統和符號管理的基礎知識之後,接下來就可以分析宣告檢查部分的**了。

源**經過預編譯後生成.i檔案,這時候**主要分為函式語句和宣告語句兩部分,其實函式只不過是一種特殊的宣告語句,比宣告語句多了{}裡面的內容。函式語句和非函式語句是分開檢查的:

if

(p->kind == nk_function)

else

下面先來分析全域性定義的宣告檢查,也就是checkglobaldeclaration()函式裡的內容。

一開始呼叫checkdeclarationspecifiers(decl->specs)進行宣告型別的檢查,除了型別檢查外還會檢查一些修飾符如const、static等等。檢查之後得到的型別將會賦值給specs->ty,如果有修飾詞還需要再組合成乙個新的型別,基本**如下

static

void

checkdeclarationspecifiers

(astspecifiers specs)

//type-qualifier: const, volatile

tok =

(asttoken) specs->tyquals;

while

(tok)

//type-specifier: int,double, struct ..., union ..., ...

p = specs->tyspecs;

while

(p)else

if(p->kind == nk_enumspecifier)

//列舉型別

else

if(p->kind == nk_typedefname)

//typedef重定義型別

else

else

break

;case tk_char:

ty =

t(char)

; tycnt++

;break

;case tk_int:

ty =

t(int)

; tycnt++

;break;.

..}}

p = p->next;}.

..//組合修飾符型別並返回

specs->ty =

qualify

(qual, ty)

;return

;}

碰到結構體型別時,會在checkstructorunionspecifier()函式中繼續深入處理,結構體有4種型別,分別是

struct data1 // 有結構體名但無「大括號」

struct // 無結構體名但有「大括號」

struct data2 // 有結構體名也有「大括號」

struct // 無結構體名也無「大括號」 ,語法分析時已報錯

如果結構體有名字,那麼會先檢查符號表中是否已經儲存了該名字,如果沒有,那麼新建乙個結構體型別並儲存到符號表中,**如下

tag =

lookuptag

(stspec->id);if

(tag ==

null

)else

if(tag->ty->categ != categ)

接下來會對結構體的成員變數逐一進行宣告檢查

while

(stdecl)

和普通宣告語句的檢查類似,先檢查宣告型別,再檢查宣告識別符號

checkdeclarationspecifiers

(stdecl->specs);.

../** struct data

*/while

(stdec)

static

void

checkstructdeclarator

(type rty, aststructdeclarator stdec, type fty)

//...

addfield

(rty, id, fty, bits)

;}

這個由遞迴呼叫checkdeclarator()函式來完成,在構建語法樹時可能會將基本標識符合陣列指標等型別復合在一起,所以最後碰到nk_namedeclarator結點終止遞迴,**如下

static

void

checkdeclarator

(astdeclarator dec)

}

這裡以陣列為例,先遞迴呼叫checkdeclarator()檢查子結點,檢查完畢後再檢查陣列長度,最後把該結點插入到tydrvlist鍊錶的頭部,**如下

//  int arr[4];

static

void

checkarraydeclarator

(astarraydeclarator arrdec)

*/if(arrdec->expr)

}alloc

(arrdec->tydrvlist)

; arrdec->tydrvlist->ctor = array_of;

arrdec->tydrvlist->len = arrdec->expr ? arrdec->expr->val.i[0]

:0; arrdec->tydrvlist->next = arrdec->dec->tydrvlist;

arrdec->id = arrdec->dec->id;

}

指標結點和函式結點也是類似的,下圖說明了int *arr1[5]和int (*arr2)[5]的解析過程

在構建了tydrvlist鍊錶之後,derivetype()函式會遍歷tydrvlist鍊錶並與宣告型別組合成乙個復合型別,基類指向宣告型別如下圖所示

在構建完型別後,會檢查符號表中有沒有該識別符號,如果沒有則把識別符號名稱和型別一起新增到符號表中

/**

check for global variables

*/if(

(sym =

lookupid

(initdec->dec->id))==

null

)

ucc編譯器(x86移植)

之前寫過一篇ucc的文章,也就是這一篇。這篇文章對ucc的流程說了挺多,但是怎麼把ucc移植到新的cpu上面,卻沒有說很多,後來自己又看了一下 發現還是有不少新的收穫。emit.c檔案是真正的後端入口,所有的彙編檔案的整理 組織部分都是這裡完成的。當然這部分只是框架的內容,告訴我們乙個大概,全域性變...

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

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

編譯器 詞法分析

總結 詞法分析 字串流 mov sum,x 執行加法運算 單詞流 mov sum,x 屬性字流 token type instr token type ident token type comma token type ident語法分析token currtoken getnexttoken 從屬...