Я написал анализатор LR (1), который может успешно анализировать строки на языке моей грамматики в конкретное синтаксическое дерево, но сейчас я пытаюсь построить абстрактное синтаксическое дерево.
Я использую дизайн наследования для моих узлов AST:
struct ASTNode {
virtual Type typeCheck() = 0;
}
struct IDNode : public ASTNode {
string name;
...
}
struct INTNode : public ASTNode {
int value;
...
}
struct BOPNode : public ASTNode {
ASTNode *pLeft;
ASTNode *pRight;
...
}
struct Add_BOPNode : public BOPNode {
...
}
struct ParamNode : public ASTNode {
string name;
ASTNode *pTypeSpecifier;
...
}
struct ParamListNode : public ASTNode {
vector<ParamNode*> params;
...
}
struct FuncDec : public ASTNode {
string functionName;
ASTNode *pFunctionBody;
ASTNode *pReturnType;
ASTNode *pParams;
...
}
Когда я выполняю сокращение в моем парсере LR (1), я генерирую новый узел в зависимости от правила, которое использовалось для сокращения. Это довольно просто для большинства узлов, но я не уверен в чистом способе реализации узла, который содержит список других узлов.
Используя ParamListNode сверху в качестве примера:
struct stack_item {
int state;
int token;
string data;
ASTNode *node;
};
/// rule = the number of the rule being reduced on
/// rhs = the items on the right-hand side of the rule
ASTNode* makeNode(int rule, vector<stack_item> rhs) {
switch(rule) {
/// <expr> ::= <expr> '+' <term>
case 1: return new Add_BOPNode(rhs[0].node, rhs[2].node);
/// <param> ::= IDENT(data) ':' <type>
case 2: return new ParamNode(rhs[0].data, rhs[2].node);
/// <param_list> ::= <param>
case 3: return new ParamList(rhs[0].node);
/// <param_list> ::= <param_list> ',' <param>
case 4: {
auto list = dynamic_cast<ParamListNode*>(rhs[0].node);
list->params.push_back(rhs[2].node);
return list;
}
...
}
}
Поскольку генерация узла требует подкласса ASTNode для возврата, я должен создать подкласс, который включает в себя вектор<> с каждым подузлом. Однако, так как не каждый узел должен быть структурой списка, я должен<> до подкласса, прежде чем я могу получить доступ к внутреннему списку.
Я чувствую, что должен быть более чистый способ обработки списка подузлов без необходимости полагаться на dynamic_cast<>.
Другой вопрос касается узла FuncDec. У него есть pParams, который должен быть ParamList (или вектором<Param *> напрямую), но для этого мне понадобится dynamic_cast<> входящий ASTNode в узел ParamList или Param. Опять же, я чувствую, что должен быть способ не использовать dynamic_cast<>, но я не могу думать об этом.
Кроме того, если у вас есть какие-либо другие предложения о том, как я могу лучше структурировать или реализовать что-либо, что было бы очень полезно 🙂
мой Генератор парсеров LRSTAR создает дерево абстрактного синтаксиса (AST), используя только один класс, Node. Каждый узел одинаков и содержит правило грамматики, указатель на токен (в таблице символов) и указатели на родительский, дочерний и следующий узлы. Следующий указатель позволяет вам иметь список узлов для родительского узла. Это работало хорошо в течение многих лет.
Во время обработки AST, это функция, связанная с узлом, которая занимается обработкой узла. Например, функция добавления будет делать что-то отличное от функции вычитания. Функции разные, а не разные классы.
Других решений пока нет …