有了型別系統和符號管理的基礎知識之後,接下來就可以分析宣告檢查部分的**了。
源**經過預編譯後生成.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 從屬...