? ? ? ? 到這里,我們已經研究了詞法分析中表達式和語句的描述和實現。這一部分,我們接著剖析聲明這一塊內容。
3.3 聲明(declaration)
3.3.1 基本數據類型
? ? ? ? 還是先從最簡單的基本數據類型開始。對于如下的C語言代碼:
int a;
char* b;
int** c;
int add(int x, int y);
int printf(char * format, ...)
由于C語言是強類型語言,聲明必然是以類型符開始的。歸納上面幾條聲明的特點,就得到了關于類型符的描述:
type_specifier : INT
| CHAR
? ? ? ? 類型符后面的則稱之為變量。第一條聲明,變量就是一個單獨的標志符;緊接著后面的兩條聲明由于有指針的存在,需要用到前面遞歸的描述方法,直到遇到標志符為止。于是,聲明的變量的描述就可以表述為:
declarator : ID
| * declarator
對于最后兩條函數聲明,括號是主要的特征。同時,又有含參數和不含參數的聲明形式,可以擴展標志符的表示范圍,得到下面的描述:
declarator : direct_declarator
| * declarator
direct_declarator : ID
| direct_declarator ( parameter_type_list )
| direct_declarator ( )
最終,上面的5條聲明,包括變量或者函數,可以統一描述為:
variable_declaration : type_specifier declarator
和前面的表達式的描述方法非常類似,關鍵是要知道如何歸納抽象。需要說明的是,parameter_type_list
就是對函數參數的表示方法,本質上也是變量的聲明,只是可以用逗號隔開,進行多次聲明:
parameter_type_list : variable_declaration
| parameter_type_list, variable_declaration
? ? ? ? 這樣,我們便得到了聲明的最終表達形式:
declaration : variable_declaration ;
? ? ? ? 對于C語言中的基本數據類型的分析就到此為止。接著,我們繼續分析C語言中另外三種非常重要并且也非常常用的數據類型:數組,結構體和枚舉。
3.3.2 數組(Array)
? ? ? ? 這里,我們只討論最簡單的一維數組。在C語言中,我們這樣聲明數組:
int arr[3];
int arr[] = {1, 2};
? ? ? ? 先不考慮賦值操作(后面我們統一分析),有沒有發現,數組的聲明和函數的聲明結構很類似,唯一的區別是圓括號和方括號。因此,只需要補充上面的描述:
direct_declarator : ID
| direct_declarator ( parameter_type_list )
| direct_declarator ( )
| direct_declarator [ const_expression ]
| direct_declarator [ ]
這里,我們引入了const_expression
,顧名思義,就是值為常數的表達式,可以簡單描述為:
const_expression : primary_expression
因為數組定義必須首先分配固定大小的內存,我們會在生成匯編代碼的時候詳細地研究這一塊內容。
3.3.3 結構體(Structure)
? ? ? ? 結構體也是一種特殊的數據類型,主要的聲明方式為:
struct Point {
int x;
int y;
};
struct Point point;
可以發現,結構體的聲明和基本數據結構的聲明類似,如果對應起來,就是這里是以struct 結構體名字
開頭,然后是變量名。也就是說,我們只需要再單獨定義一種針對結構體的類型符即可,得到:
type_specifier : INT
| CHAR
| struct_specifier
仔細分析結構體大括號的內部結構,其實就是由普通的數據類型的聲明或者可能存在的嵌套的結構體聲明組成的集合。因此,可以描述為:
struct_specifier : struct STRUCT_ID
| struct STRUCT_ID { struct_declaration_list }
struct_declaration_list : struct_declaration
| struct_declaration_list struct_declaration
struct_declaration : type_specifier declarator ;
為了和普通變量名的區別開來,這里引入了STRUCT_ID
來單獨表示結構體的名字,它和變量名是有區別的。
3.3.4 枚舉(Enumerate)
? ? ? ? 熟悉了結構體的描述,枚舉也就大同小異了,我們常用的聲明方式如下:
enum COLOR { RED, BLUE = 2, YELLOW };
enum { Mon, Wed, Fri } week;
enum COLOR color;
? ? ? ? 仿照結構體的描述,更新后的類型符為:
type_specifier : INT
| CHAR
| struct_specifier
| enum_specifier
對于枚舉聲明中大括號內部的結構,再結構體的描述基礎上,稍微變通一下,就可以得到下面的描述:
enum_specifier : enum ENUM_ID
| enum ENUM_ID { enum_declarator_list }
enum_declarator_list : ENUM_MEMBER_ID
| enum_declarator_list, ENUM_MEMBER_ID
同樣的,引入ENUM_ID
來單獨表示枚舉名。由于枚舉成員變量需要是一個常值,再引入ENUM_MEMBER_ID
進行表示。
? ? ? ? 至此,我們將表達式、語句和聲明用我們自己規定的結構進行了抽象描述,完成了語法分析第一步的工作。再進入第二步內容的時候,我們看一看有沒有什么遺漏的地方。當時存在,比如說在聲明變量的同時進行初始化,也就是常說的: 定義。
3.3.5 變量定義(Definition)
? ? ? ? 在C語言中,允許我們采用如下的方式進行變量的聲明:
int a = 1, *b;
char str[] = {'a', 'b', 'c'};
struct Point point = {0, 0};
即聲明的同時進行變量的定義,也就是需要增加對變量進行賦值的描述。同時可以采用逗號分隔,聲明多個變量,這種情況前面我們已經處理過了,只需要轉換成_list
形式即可。于是,補充之后對描述為:
declaration : type_specifier init_declarator_list ;
init_declarator_list : init_declarator
| init_declarator_list, init_declarator
init_declarator : declarator
| declarator = initializer
initializer : expression
| { initializer_list }
initializer_list : initializer
| initializer_list, initializer
? ? ? ? 看上去還是很好理解的,那么函數呢?C語言中,函數的定義為:
int printf(char * format, ...)
{
...
}
熟能生巧,一步到位:
function_definition : type_specifier declarator compound_statement
? ? ? ? 至此,我們將會處理到的C語言結構已經全部用抽象的結構描述完畢。但是,對于一個完整的C編譯器來說,還有很多內容沒有涉及到。本質上來說,這些忽略的內容也是很好用抽象結構表示的,只是越往后,它們涉及的工作量將會非常多。因此,這里將其省略,只保留了相對來說易于理解和處理的部分,便于抓住對編譯器的宏觀認識和理解。
? ? ? ? 這些描述目前都還只是零散的表示。為方便后續處理,需要將這些內容全部聯系起來,我們將在下一部分詳細研究。
實現簡易的C語言編譯器(part 0)
實現簡易的C語言編譯器(part 1)
實現簡易的C語言編譯器(part 2)
實現簡易的C語言編譯器(part 3)
實現簡易的C語言編譯器(part 4)
實現簡易的C語言編譯器(part 6)
實現簡易的C語言編譯器(part 7)
實現簡易的C語言編譯器(part 8)
實現簡易的C語言編譯器(part 9)
實現簡易的C語言編譯器(part 10)
實現簡易的C語言編譯器(part 11)