C++ - 为此找到合适的设计



我正在编写一个脚本解释器,我首先需要标记一个包含源代码的字符串。为此,我确定了不同的东西:

  • 标识符(变量名(;
  • 符号(+,-等...,包括"字母"运算符,如"返回"(;
  • 乱码值(真、假、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::ParseWithParseVisitor::VisitAndParseFor添加参数。同样,如果需要将状态/数据传递回去,可以更改返回签名。

相关内容

  • 没有找到相关文章

最新更新