在实现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 .....
应该有更好的形式来实现吧,希望能与大家交流。