В настоящее время я пишу интерфейс для личного обучения по этой теме и столкнулся с проблемой, касающейся способа обработки определения BNF в C ++ посредством перегрузки операторов.
В настоящее время моя установка выглядит следующим образом:
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
;
Теперь вот проблема: каждый перегруженный оператор возвращает новый объект по значению. Это означает, что всякий раз, когда я использую 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;
Постскриптум Я знаю о различных генераторах синтаксических анализаторов и Boost.Spirit, но моя цель — написать собственный анализатор.
Вы можете разместить возвращаемые объекты в куче (через фабрику) и вернуть их как ссылки. Фабрика может отслеживать их, чтобы вы не пропустили. Что касается синтаксиса, он будет работать так же, как и при возврате их по значению.
Вы можете обойти эту проблему, заменив Rule*
(у которых есть проблема, что вы не можете перегрузить операторы для них) с объектами-обертками. То есть ChainRule
будет содержать RuleRef next
вместо Rule * next
и т. д., и все операторы будут определены для RuleRef
, RuleRef
будет просто содержать Rule*
и быть конструируемым из Rule*
, Чтобы упростить обработку памяти, вы можете унаследовать класс интеллектуальных указателей.