Я начал писать анализатор полиморфного рекурсивного спуска на C ++. Однако у меня есть проблема. Классы настроены так:
class Node {
public:
std::vector<Node*> children;
};
class NodeBinary : public Node {
public:
Node* left;
Node* right;
};
class NodeUnary : public Node {
public:
Node* operand;
};
class NodeVar : public Node {
public:
std::string string;
NodeVar(std::string str) : string(str) {};
};
class NodeNumber : public Node {
public:
signed long number;
NodeNumber(signed long n) : number(n) {};
};
// etc.
А потом классы вроде NodeDeclaration
, NodeCall
, NodeNot
, NodeAssignment
, NodePlus
, NodeMinus
, NodeIf
и т.д. унаследует либо от Node
или что-то менее общее, как NodeBinary
или же NodeUnary
,
Однако некоторые из них принимают более конкретные операнды. NodeAssignment
всегда принимает переменную и число / выражение. Поэтому мне придется переопределить Node * влево, NodeVar * влево и NodeExpr * вправо. Проблема приходит с такими вещами, как NodePlus
, Слева может быть NodeVar
или NodeExpr
! И у корневого узла есть похожая проблема: при синтаксическом анализе на верхнем уровне, чтобы добавить дочерние узлы в корневой каталог, как можно определить, является ли дочерний узел NodeExpr
, NodePlus
, NodeIf
так далее…?
Я мог бы иметь все узлы с перечислимым «типом», который говорит, что это за тип, но тогда какой смысл иметь красивое полиморфное дерево наследования?
Как эта проблема обычно решается ??
Если вы используете наследование классов для своих узлов AST, вам необходимо создать соответствующую иерархию наследования, как и в любом объектно-ориентированном проектировании.
Так, например, NodeAssignment
(что, вероятно, является специализацией NodeStatement
) должен содержать NodeLValue
(из которых NodeVariable
это специализация) и NodeValue
, Как обычно, LValues (то есть вещи, которые вы можете назначить) являются подмножеством значений, поэтому NodeLValue
будет специализация NodeValue
, И так далее. Ваш бинарный операторный узел будет содержать left
а также right
члены, оба из которых являются NodeValue
базовые объекты (я бы ожидал NodeValue
быть чисто виртуальным, с большим количеством конкретных специализаций.)
Если вы настаиваете на использовании парсера рекурсивного спуска, каждая функция синтаксического анализа должна возвращать соответствующий подкласс Node
, так что функция, которая анализирует левую часть присваивания, логически возвращает NodeLValue*
готовы вставить в NodeAssignment
конструктор. (Честно говоря, я бы отказался от слова Node
во всех этих именах классов. Поместите их все в пространство имен node::
и сэкономить немного печатать.)
Других решений пока нет …