在實現lexer和parser的過程中,要定義token的類型。《編程語言實現模式》中使用的是java代碼,直接定義static int TEXT = 1;並且可以在lexer擴展的時候繼承到實際使用的lexer里面。但C++定義類的static成員時,必須在外面單獨的進行初始化,對這一點不是很爽,在C++11中,實現了enum class,
先看看這個enum class能不能滿足要求,例如
enum class TokenType{ _EOF, TEXT }; TokenType a = TokenType::TEXT;
那么就可以方便地使用TokenType::TEXT指代類型,只有同是TokenType,才能進行比較。如果要進行擴展,如下
enum class ExprTokenType:public TokenType{ NUMBER, OP };
希望新的類型ExprTokenType在TokenType的基礎上,添加兩種類型,既然是class,繼承應該沒問題的吧?但編譯的時候就報錯了。只好放棄這種方式了。
后來想到下面的實現,
class TokenType{ public: TokenType(){ nameMap = {"_EOF","TEXT"}; } string Name(int x){ return nameMap.at(x); } public: const static int _EOF = 0; // const static 成員 可以在這里直接初始化, // 如果只是const修飾,const成員需要在構造函數的初始化列表里面進行賦值 // public成員, 可以像 enum class類似地,使用TokenType.TEXT訪問 const static int TEXT = 1; protected: vector<string> nameMap;// 可以根據 類型 得到其名稱,便於調試, }TokenType; class ExprTokenType:public TokenType{ public: ExprTokenType(){ nameMap.push_back("NUMBER"); // 為新添加的類型添加對應的名稱, nameMap.push_back("OP"); } public: const static int NUMBER = 2; const static int OP = 3; }ExprTokenType; // 這里定義一個同名的實例,以后就可以直接使用 類名.類型, // 如果不這樣做的話,或許把它所有的內容都聲明為static,也是同樣的。
期望的功能都實現了,只是這與enum class不一樣,不是類型安全的。不過沒關系,能用就已經很好了。
下面是測試代碼,
int main(){ cout << "---test enum type---" << endl; int a = TokenType::TEXT; cout << "TokenType a:" << a << endl; cout << "ToeknType a:" << TokenType.Name(a) << endl; int b = ExprTokenType::OP; cout << "ExprTokenType b:" << b << endl; cout << "ExprToeknType b:" << ExprTokenType.Name(b) << endl; b = ExprTokenType::TEXT; cout << "ExprTokenType b:" << b << endl; cout << "ExprToeknType b:" << ExprTokenType.Name(b) << endl; if(a==b) cout << "test done." << endl; return 0; }
需要注意的是,編譯的時候需要添加C++11的支持,g++ -std=c++0x .....
應該有更好的形式來實現吧,希望能與大家交流。