我目前正在编写关于该主题的个人教育的编译器前端,并且我遇到了一个问题,即我通过运算符重载在C ++中处理BNF定义的方式。
目前我的设置如下:
Rule.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;
};
Rule.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
;
现在问题是:每个重载的运算符都按值返回一个新对象。这意味着每当我使用运算符>>或操作员|从Rule类开始,一旦我从调用返回到运算符>>这些指针就会变成垃圾。或操作员|因为堆栈已被清理,物体消失了。
我也不能在我的Rule子类的构造函数中使用pass by值,因为这不允许我定义递归语法。
所以我没有选择按值传递对象,也没有通过指针传递对象的选项。任何人都可以指出一个解决方案,不会强迫我像这样定义我的语法吗?
StringRule s = StringRule("=");
OrRule assignment_exp;
ChainRule temp1 = s >> assignment_exp;
ChainRule temp2 = unary_exp >> temp1;
assignment_exp = logical_or_exp | temp2;
P.S。我知道各种解析器生成器和Boost.Spirit,但我的目标是编写自己的解析器。
答案 0 :(得分:1)
您可以在堆上分配返回对象(通过工厂)并将它们作为引用返回。工厂可以跟踪它们,这样你就不会泄漏。就语法而言,它将与按值返回时相同。
答案 1 :(得分:1)
您可以通过使用包装器对象替换Rule*
(具有不能为其重载运算符的问题)来解决此问题。即ChainRule
将包含RuleRef next
而不是Rule * next
等,并且将为RuleRef
定义所有运算符。 RuleRef
只会包含Rule*
,并且可以从Rule*
构建。为了使内存处理更容易,您可以从智能指针类继承。