use*_*129 30 c compiler-construction tree struct abstract-syntax-tree
我正在C中为一个简单的玩具语言实现一个编译器.我有一个工作的扫描器和解析器,以及关于AST的概念功能/构造的合理背景.我的问题与在C中表示AST的具体方式有关.我在网上不同的文本/资源中经常遇到三种风格:
每种类型的节点一个结构.
它有一个基节点"class"(struct),它是所有子结构中的第一个字段.基节点包含一个存储节点类型的枚举(常量,二元运算符,赋值等).使用一组宏访问结构的成员,每个结构一个集.它看起来像这样:
struct ast_node_base {
enum {CONSTANT, ADD, SUB, ASSIGNMENT} class;
};
struct ast_node_constant {
struct ast_node_base *base;
int value;
};
struct ast_node_add {
struct ast_node_base *base;
struct ast_node_base *left;
struct ast_node_base *right;
};
struct ast_node_assign {
struct ast_node_base *base;
struct ast_node_base *left;
struct ast_node_base *right;
};
#define CLASS(node) ((ast_node_base*)node)->class;
#define ADD_LEFT(node) ((ast_node_add*)node)->left;
#define ADD_RIGHT(node) ((ast_node_add*)node)->right;
#define ASSIGN_LEFT(node) ((ast_node_assign*)node)->left;
#define ASSIGN_RIGHT(node) ((ast_node_assign*)node)->right;
Run Code Online (Sandbox Code Playgroud)
每个节点布局一个结构.
这似乎与上面的布局大致相同,除了没有ast_node_add和ast_node_assign它将有一个ast_node_binary来表示两者,因为两个结构的布局是相同的,它们只是由base-> class的内容不同.这样做的好处似乎是一组更加统一的宏(左侧和右侧所有节点的LEFT(节点),而不是每对一对宏),但缺点似乎是C类型检查不会有用(例如,没有办法检测到只有ast_node_add的ast_node_assign).
一个结构总数,带有用于保存不同类型节点数据的联合.
可以在这里找到比我能给出的更好的解释.使用上一个示例中的类型,它看起来像:
struct ast_node {
enum { CONSTANT, ADD, SUB, ASSIGNMENT } class;
union { int value;
struct { struct ast_node* left;
struct ast_node* right; } op;
};
Run Code Online (Sandbox Code Playgroud)
我倾向于最喜欢第三个选项,因为它使得递归遍历变得更容易(因为大量的指针转换被避免支持联合),但它也没有利用C类型检查.第一个选项似乎是最危险的,因为它依赖于指向结构的指针来访问任何节点的成员(甚至同一节点的不同成员需要不同的情况来访问(base vs. left)),但这些类型转换是类型的检查,这可能是没有实际意义的.我的第二个选择似乎是两个世界中最糟糕的选择,尽管我可能会遗漏一些东西.
这三种方案中哪一种最好,为什么?我还没有找到更好的第四种选择吗? 我假设它们都不是"一刀切"的解决方案,所以如果重要的是我正在实现的语言是静态类型的命令式语言,几乎是C的一小部分.
我对第三个(联合)布局的具体问题. 如果我只使用值字段,那么值后面会有空格,以适应op被写入的可能性吗?
Ira*_*ter 19
你可以做任何这些工作.
我更喜欢联合布局,因为所有节点都有"相同"的布局.
[你可能会发现有一个"子子列表"选项是有用的,例如,和任意大的,动态的孩子数组,而不是左倾或右倾列表.
您将发现此问题不是使编译器难以构建的问题.相反,它具有符号表,执行各种分析,选择机器级IR,构建代码生成器以及执行代码优化.然后你会遇到真正的用户,你会发现你真正做错了什么: - }
我会选择一个并运行它,这样你就有机会接近其他问题.
| 归档时间: |
|
| 查看次数: |
9549 次 |
| 最近记录: |