Clang Parser漫步——declaration-specifiers(二)
首先,這裡先給出C99中declaration-specifiers的EBNF語法:
declaration-specifiers:: [C99 6.7]n storage-class-specifier declaration-specifiers[opt]n type-specifier declaration-specifiers[opt]n [C99] function-specifier declaration-specifiers[opt]nnstorage-class-specifier:: [C99 6.7.1]n typedefn externn staticn auton registernnfunction-specifier: [C99 6.7.4]n[C99] inlinenntype-specifier::n charn wchar_tn booln shortn intn longn signedn unsignedn floatn doublen voidn [C99] _Booln [C99] _Complexn [C99] _Imaginary // Removed in TC2?n enum-specifiern struct-specifiern union-specifiern cv-qualifiernncv-qualifier::n constn volatilen restrictn
一、入口
現在可以按照上述的語法對declaration-specifiers進行解析了;入口函數在ParseDecl.cpp:1581行
接下來在一個while循環中使用switch語句去處理上述右遞歸文法。略過處理c++,objective-c語法的邏輯。unsigned double是非法的;nunsigned x = 4; //x的默認類型將會轉換為unsigned int.nunsigned unsigned x = 4; // 錯誤duplicate unsigned declaration specifiern
Clang將declaration-specifiers分為如下幾類,不同的類別是可以同時存在一個聲明中的,但同一組中只能有一個。
- TypeSpecifierType: char, wchar_t, _Bool, foat, double, void, enum, union, struct;
- TypeSpecifierWidth: short, long, long long;
- TypeSpecifierComplex: _Imaginary, _Complex;
- TypeSpecifierSign: unsigned, signed;
- TypeQualifier: const, restrict, volatile;
- StorageClassSpecifier: typedef, extern, static, auto, register;
處理identifier,這個地方會區分普通標識符還是typename:
將其作為typename,並進行名字查找:處理storage-class-specifier:處理function-specifiers,這些都是在C99中才加入的,如果你不需要處理C99的語法,可以跳過此類。[注意,virtual和explicit是C++的語法]二、解析Struct/Union-specifier
ParseClassSpecifier函數在ParseDeclCXX.cpp:839行。
產生式為:nstruct-or-union-specifier: [C99 6.7.2.1]n struct-or-union identifier[opt] { struct-contents }n struct-or-union identifiern[GNU] struct-or-union attributes[opt] identifier[opt] { struct-contents attributes[opt]nn[GNU] struct-or-union attributes[opt] identifiernn struct-or-union:n structn unionn
首先進行一個簡單的區分是struct or union or class[c++語法]
之後就是解析各種gnu和microsoft的擴展了,我們這時候可以跳過。判斷是否有tag名字或者簡單的模板id,不考慮c++模板。因為struct/union有匿名語法。判斷該struct/union X...是reference ? definition ? declaration。然後得到如果TUK表示定義,則會轉入解析struct/union body的函數,我們略過對c++語法的解析邏輯。後面的部分就是Clang的錯誤語法產生式,將某些常見的錯誤語法形式編碼到Parser中,為了更好的進行錯誤提示,可以了解下。
三、解析enum-specifier解析函數依舊在ParseDecl.cpp中,處於2766行。
產生式:nParseEnumSpecifiern enum-specifier: [C99 6.7.2.2]n enum identifier[opt] { enumerator-list }n[C99/C++]enum identifier[opt] { enumerator-list , }nn enum identifier [類型引用或者聲明]n
判斷是否有tag名字,否則就是一個匿名enum。
與之前解析struct-specifier類似,判斷該語法為何種形式, 定義或聲明或引用名字處理好了之後,調用語義動作生成一個EnumDecl。
然後就可以解析enum {....}了。最後進行語義判斷,是否有多個enum聲明,有個話將進行錯誤提示。
三、解析Enum聲明成員首先聲明一個作用域,用於檢查同名變數,注意對於C語言來說,enum中沒有一個單獨的作用域,enum體內所有的變數的作用域在該enum聲明所在的作用域,比如在頂層聲明一個enum,那麼enum常量所屬的作用域是最頂層作用域。
//對於如下代碼:nenum Xn{n XX = 0,n XZ,n};nnenum Yn{n XX = 0;n};nn// 將會提示redefinition of enumerator XX。n// 如果此處使用c++11 enum class語法,帶來的效果將會如Java中enum聲明一樣。n// 單獨形成一個作用域。nn// 如下代碼在Clang 3.9 中使用-std=c++11編譯通過。nenum class Xn{n XX = 0,n XZ,n};nnenum class Yn{n XX = 0,n};n
好了,回歸正題,在一個死循環中以此解析每個enum-decelarator,將結果保存在一個SmallVector中。
得到一個標識符,判斷是否有賦值表達式,有的話在3031行解析常量表達式。
每個enumerator-constant解析完成之後,調用語義動作生成一個EnumConstantDecl對象,並添加到集合中。當所有的成員解析完成之後,退出while循環,調用語義動作生成一個EnumDecl對象,對進行語義檢查。推薦閱讀:
※有沒有不適合使用flex/lex作為詞法分析器的語言?
※KLEE到底是靜態分析工具還是動態分析工具?
※(發散性問題,請隨意加上假設)一門編程語言同時擁有解釋器和編譯器的必要性有多大?
※從哪裡可以得到包含了Python3所支持的所有語法的測試用例?
※為什麼C++預處理期編譯期的支持比較薄弱?