当前位置:网站首页>自己动手写RISC-V的C编译器-02语法描述方法和递归下降解析
自己动手写RISC-V的C编译器-02语法描述方法和递归下降解析
2022-08-11 05:29:00 【YOUNIKOJIAO】
自己动手写RISC-V的C编译器-02语法描述方法和递归下降解析
本节增加对
*
、/
、+
、-
、()
运算的支持
使用生成规则表示运算符优先级
expr = mul("+" mul | "-" mul)*
mul = num("*" num | "/" num)*
上面的表达式可以很容易的推导出对于对于运算1*2+3
的语法树
由expr
开始推导乘除法一定会在加减法的更下一层,所以很自然的得出乘法优先级大于加减法优先级。接下来我们对表达式进行拓展,使编译器能解析()、*、/
expr = mul("+" mul | "-"mul)*
mul = primary("*" primary | "/" primary)*
primary = num | "(" expr ")"
运算1*(2+3)
的语法树如下:
数据结构设计
通过上面的解释,我们发现将一串暂时无意义的字符串变成一个有规则的树形结构可以更好的表示算式的优先级,上面的树形结构就是所谓AST(抽象语法树) 所以解析算式我们采用AST
节点数据结构设计
和上一篇类似,我们依然采用enum
类型来自动生成每个节点的类型编号。
typedef enum {
nd_num,
nd_add,
nd_sub,
nd_mul,
nd_div,
} node_kind;
AST的节点就是一个二叉树的子节点,所以他应该包括三部分节点类型、左子树、右子树、如果是数字节点那还应该包括数字的值。
typedef struct node node;
struct node {
node_kind kind;
node *lch;
node *rch;
int val;
};
使用三个函数来构建二叉树的基本结构
node *new_node(node_kind kind) {
// 用于构建一个子节点并返回该节点的指针
node *nd = calloc(1, sizeof(node));
nd->kind = kind;
return nd;
}
node *new_binary(node_kind kind, node *lch, node *rch) {
// 用域构建一个二叉树
node *nd = new_node(kind);
nd->lch = lch;
nd->rch = rch;
return nd;
}
node *new_num(int val) {
// 构建一个数字子节点
node *nd = new_node(nd_num);
nd->val = val;
return nd;
}
递归下降解析
构建AST
有了上面的运算规则就可以着手编写程序对算式进行解析了,首先先构建语法树。构建语法树需要用到三个函数expr
、mul
、primary
分别用于解析算式、乘法算式、数字或括号表达式
node *expr(token **rest, token *tok);
node *mul(token **rest, token *tok);
node *primary(token **rest, token *tok);
下面通过代码具体看看如何构建出一颗AST
// expr = mul("+" mul | "-"mul)*
// mul = primary("*" primary | "/" primary)*
// primary = num | "(" expr ")"
node *expr(token **rest, token *tok){
node *nd = mul(&tok, tok);
while (true) {
if (equal(tok, "+")) {
nd = new_binary(nd_add, nd, mul(&tok, tok->next));
continue;
}
if (equal(tok, "-")) {
nd = new_binary(nd_sub, nd, mul(&tok, tok->next));
continue;
}
*rest = tok;
return nd;
}
}
node *mul(token **rest, token *tok) {
node *nd = primary(&tok, tok);
while (true) {
if (equal(tok, "*")) {
nd = new_binary(nd_mul, nd, primary(&tok, tok->next));
continue;
}
if (equal(tok, "/")) {
nd = new_binary(nd_div, nd, primary(&tok, tok->next));
continue;
}
*rest = tok;
return nd;
}
}
node *primary(token **rest, token *tok) {
if (tok->kind == num) {
node *nd = new_num(tok->val);
*rest = tok->next;
return nd;
}
if (equal(tok, "(")) {
node *nd = expr(&tok, tok->next);
*rest = skip(tok, ")");
return nd;
}
error_at(tok->loc, "expexted an expression");
return NULL;
}
通过遍历AST生成汇编代码
以以下AST为例
先遍历到最右子节点,将3
压入栈。之后遍历到节点2
,并放入a0
中,将3
从栈中弹出并放入a1
中。根据2
和3
的父节点类型选择相应的汇编代码,最后完成汇编代码的生成。
int depth;
void push() {
printf(" addi sp, sp, -8\n");
printf(" sd a0, 0(sp)\n");
depth ++;
}
void pop(char *reg) {
printf(" ld %s, 0(sp)\n", reg);
printf(" addi sp, sp, 8\n");
depth --;
}
void gen_expr(node *nd) {
if (nd->kind == nd_num) {
printf(" li a0, %d\n", nd->val);
return;
}
gen_expr(nd->rch);
push();
gen_expr(nd->lch);
pop("a1");
switch (nd->kind)
{
case nd_add:
printf(" add a0, a0, a1\n");
return;
case nd_sub:
printf(" sub a0, a0, a1\n");
return;
case nd_mul:
printf(" mul a0, a0, a1\n");
return;
case nd_div:
printf(" div a0, a0, a1\n");
return;
default:
break;
}
}
边栏推荐
- mongoose连接mongodb不错,显示encoding没有定义
- JS case exercise (classic case of teacher pink)
- Event Preview | On April 23, a number of wonderful sharing sessions of OpenMLDB will come, which will live up to the good time of the weekend
- 8-byte standard request parsing during USB enumeration
- 经纬度求距离
- Day 83
- Day 76
- 场景驱动的特征计算方式OpenMLDB,高效实现“现算先用”
- Real-time Feature Computing Platform Architecture Methodology and Practice Based on OpenMLDB
- 论文解读:跨模态/多光谱/多模态检测 Cross-Modality Fusion Transformer for Multispectral Object Detection
猜你喜欢
Day 79
umi约定式路由规则修改
构建面向特征工程的数据生态 ——拥抱开源生态,OpenMLDB全面打通MLOps生态工具链
OpenMLDB v0.5.0 released | Performance, cost, flexibility reach new heights
Invalid revision: 3.18.1-g262b901-dirty
父子节点数据格式不一致的树状列表实现
OpenMLDB Pulsar Connector:高效打通实时数据到特征工程
The official website of OpenMLDB is upgraded, and the mysterious contributor map will take you to advance quickly
Node 踩坑之80端口被占用
127.0.0.1 已拒绝连接
随机推荐
哥德巴赫猜想与整数环
Pinyougou project combat notes
JS进阶网页特效(pink老师笔记)
Vscode remote connection server terminal zsh+Oh-my-zsh + Powerlevel10 + Autosuggestions + Autojump + Syntax-highlighting
jdbc接口文档参考,jdbc接口方法逻辑探究
论文解读:GAN与检测网络多任务/SOD-MTGAN: Small Object Detection via Multi-Task Generative Adversarial Network
OpenMLDB Meetup No.2 会议纪要
OpenMLDB官网升级,神秘贡献者地图带你快速进阶
2021-09-11 C语言 变量与内存分配
无效的修订:3.18.1-g262b901-dirty
【无标题】
PyQt5中调用.ui转换的.py文件代码解释
vscode插件开发——代码提示、代码补全、代码分析
关于if(x)和while(x)的解释
JVM调优整理
The role of the port
字节(byte)和位(bit)
Use the adb command to manage applications
Byte (byte) and bit (bit)
栈stack