我正在编写一个脚本解释器,我首先需要标记一个包含源代码的字符串。为此,我确定了不同的东西:
- 标识符(变量名(;
- 符号(+,-等...,包括"字母"运算符,如"返回"(;
- 乱码值(真、假、1、3.14、"foo"(。
为了表示这一点,我想到了两种不同的方法:要么创建一个类层次结构:
class Token
{
public:
enum type_e { E_IDENTIFIER, E_SYMBOL, E_LITTERAL }
const type_e type;
};
class Identifier : public Token
{
public:
const string name
}
class Symbol : public Token
{
public:
const symbol_e symbol;
}
class Litteral : public Token
{
public:
const Value value;
}
我会以这种方式与向下投射一起使用:
bla bla parseStatement( bla bla )
{
// ...
Token * curTok = tokens[ curPos ];
if( curTok->type == E_SYMBOL && dynamic_cast< Symbol * >( curTok )->symbol == E_PLUS )
{
// ...
}
// ...
}
但有人告诉我,羽绒铸造意味着我的设计可能有缺陷。这也违背了多态性原则。
然后我想到了第二种方法,使用某种包含所有内容的变体类:
class Token
{
private:
type_e _type;
public:
type_e getType()
bool isIdentifier()
bool isSymbol()
bool isLitteral()
string getName() // If is an identifier, else exception
symbol_e getSymbol() // If is a symbol, else exception
Value getValue() // If is a litteral, else exception
}
我会这样使用:
bla bla parseStatement( bla bla )
{
// ...
Token curTok = tokens[ curPos ];
if( curTok.isSymbol() && curTok.getSymbol() == E_PLUS )
{
// ...
}
// ...
}
但在我看来,它并不干净。这基本上是同一件事,只是写起来短一点。
有人建议我使用访问者设计模式,但我找不到一种将这种模式用于我的问题的方法。我想将语法分析逻辑保留在令牌之外。逻辑将位于将操作这些令牌的语法分析类中。我还需要令牌属于同一类型或具有公共基类,以便我可以将它们存储在单个数组中。
你知道我该如何设计这个吗?谢谢:)
这是一个带有访客模式的版本
#include <iostream>
using namespace std;
class Value { };
class Identifier;
class Symbol;
class Literal;
class ParseVisitor {
public:
virtual void VisitAndParseFor(Identifier& d1) = 0;
virtual void VisitAndParseFor(Symbol& d2) = 0;
virtual void VisitAndParseFor(Literal& d1) = 0;
};
class Token {
public:
virtual void ParseWith(ParseVisitor& v) = 0;
};
class Identifier : public Token {
public:
virtual void ParseWith(ParseVisitor& v) {
v.VisitAndParseFor(*this);
}
const string name;
};
class Symbol : public Token {
public:
enum symbol_e {
E_PLUS, E_MINUS
};
virtual void ParseWith(ParseVisitor& v) {
v.VisitAndParseFor(*this);
}
symbol_e symbol;
};
class Literal : public Token {
public:
virtual void ParseWith(ParseVisitor& v) {
v.VisitAndParseFor(*this);
}
Value value;
};
// Implementing custom ParseVisitor
class Parser : public ParseVisitor {
virtual void VisitAndParseFor(Identifier& identifier) {
std::printf("Parsing Identifiern");
}
virtual void VisitAndParseFor(Symbol& symbol) {
std::printf("Parsing Symboln");
switch (symbol.symbol) {
case Symbol::symbol_e::E_PLUS: std::printf("Found plus symboln"); break;
case Symbol::symbol_e::E_MINUS: std::printf("Found minus symboln"); break;
}
}
virtual void VisitAndParseFor(Literal& literal) {
std::printf("Parsing Literaln");
}
};
int main() {
Parser p;
Identifier identifier;
Symbol symbol;
symbol.symbol = Symbol::symbol_e::E_PLUS;
Literal literal;
identifier.ParseWith(p);
symbol.ParseWith(p);
literal.ParseWith(p);
return 0;
}
如果在解析时需要上下文数据,则向Token::ParseWith
和ParseVisitor::VisitAndParseFor
添加参数。同样,如果需要将状态/数据传递回去,可以更改返回签名。