我编写了一个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的子类,因此我必须创建一个包含向量&lt;&gt;的子类。与每个子节点。但是,由于并非每个节点都需要是列表结构,因此我必须使用dynamic_cast&lt;&gt;在我可以访问内部列表之前到子类。
我觉得应该有一种更简洁的方法来处理子节点列表,而不必依赖dynamic_cast&lt;&gt;。
另一个问题是关于FuncDec节点。它有pParams,它应该是ParamList(或者矢量&lt; Param *&gt;直接),但为了做到这一点,我必须使用dynamic_cast&lt;&gt;传入的ASTNode到ParamList或Param节点。同样,我觉得应该有一种不使用dynamic_cast&lt;&gt;的方法,但我想不出一个。
此外,如果您对如何更好地构建或实施任何非常感激的内容有任何其他建议:)
答案 0 :(得分:1)
我的LRSTAR Parser Generator只使用一个类Node创建一个抽象语法树(AST)。每个节点都相同,包含语法规则,指向令牌的指针(在符号表中),以及指向父节点,子节点和下一节点的指针。下一个指针允许您拥有父节点的节点列表。多年来这种方法运作良好。
在处理AST期间,它是与负责节点处理的节点相关联的功能。例如,add函数将执行与subtract函数不同的操作。功能不同,而不是具有不同的类。