1

我目前正在为有关该主题的个人教育编写编译器前端,并且遇到了一个关于我通过运算符重载在 C++ 中处理 BNF 定义的方式的问题。

目前我的设置如下:

规则.h:

class Rule
{
public:
    ChainRule operator>>(Rule& right);
    OrRule operator|(Rule& right);
    KleeneRule operator*();
    OptionalRule Rule::operator+();

    virtual bool parse(TokenList::iterator& begin, TokenList::iterator end) = 0;
};

规则.cpp:

ChainRule Rule::operator>>(Rule& right) {
    return ChainRule(this, &right);
}

OrRule Rule::operator|(Rule& right) {
    return OrRule(this, &right);
}

KleeneRule Rule::operator*() {
    return KleeneRule(this);
}

OptionalRule Rule::operator+() {
    return OptionalRule(this);
}

ChainRule、OrRule、KleeneRule、OptionalRule 和 EmptyRule 的定义很简单,如下所示:

class ChainRule : public Rule
{
private:
    Rule* next;
    Rule* _this;

public:
    ChainRule();
    ChainRule(Rule* _this, Rule* right);

    bool parse(TokenList::iterator& begin, TokenList::iterator end) override;
};

Rule 的每个子类显然都定义了 parse() 的合理实现。使用这些类,我可以如下定义我的语法:

OrRule assignment_exp   = logical_or_exp
                        | unary_exp >> StringRule("=") >> assignment_exp
                        ;   

现在问题来了:每个重载的运算符按值返回一个新对象。这意味着每当我使用 operator>> 或 operator| 从 Rule 类中,一旦我从对 operator>> 或 operator| 的调用返回,这些指针将是垃圾。因为堆栈已被清理并且对象已经消失。

我也不能在我的 Rule 子类的构造函数中使用按值传递,因为这不允许我定义递归语法。

所以我没有按值传递对象的选项,也没有按指针传递对象的选项。谁能指出一个不会强迫我像这样定义我的语法的解决方案?

StringRule s = StringRule("=");
OrRule assignment_exp;
ChainRule temp1 = s >> assignment_exp;
ChainRule temp2 = unary_exp >> temp1;
assignment_exp = logical_or_exp | temp2;

PS 我知道各种解析器生成器和 Boost.Spirit,但我的目标是编写自己的解析器。

4

2 回答 2

1

您可以在堆上分配返回对象(通过工厂)并将它们作为引用返回。工厂可以跟踪它们,因此您不会泄漏。就语法而言,它与按值返回它们时的工作方式相同。

于 2012-11-21T23:13:58.067 回答
1

您可以通过用包装器对象替换您的Rule*(存在无法为它们重载运算符的问题)来解决此问题。即将ChainRule包含RuleRef next而不是Rule * next等,并且所有运算符都将定义为RuleRefRuleRef将仅包含 a Rule*,并且可以从 a 构造Rule*。为了使内存处理更容易,您可以从智能指针类继承。

于 2012-11-21T23:26:41.583 回答