上一次把宣告的說明符已經分析得很清楚,也就是把
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...