c语言是如何解析表达式语句"2+3*4;"的?
2024-08-30 16:17:42
1. 要编译的测试代码:
int main(void)
{
2+3*4;
}
2. 词法分析
词法分析将字符变成token,其中很重要的是token的类型,如字符2的token类型为TK_NUM,这在后面的语法分析阶段有用。
3. 语法分析
3.1 解析字符"2"
if (tok->kind == TK_NUM) {
Node *node;
if (is_flonum(tok->ty)) {
node = new_node(ND_NUM, tok);
node->fval = tok->fval;
} else {
node = new_num(tok->val, tok);
} node->ty = tok->ty;
*rest = tok->next;
return node;
}
如果token类型为数字,则解析数字,2不为浮点数,所以执行else分支。
static Node *new_num(int64_t val, Token *tok) {
Node *node = new_node(ND_NUM, tok);
node->val = val;
return node;
}
创建一个类型为ND_NUM的node节点,这个节点就代表了数字2,数字2存储在node节点的val变量中。
3.2 解析"+"
static Node *add(Token **rest, Token *tok) {
Node *node = mul(&tok, tok); for (;;) {
Token *start = tok; if (equal(tok, "+")) {
node = new_add(node, mul(&tok, tok->next), start);
continue;
} if (equal(tok, "-")) {
node = new_sub(node, mul(&tok, tok->next), start);
continue;
} *rest = tok;
return node;
}
}
数字2的node节点由mul函数返回,此时tok为"+",所以会调用new_add函数,在这个函数中会创建类型为ND_ADD的node节点,
这个节点的左表达式为代表数字2的node节点,右表达式为代表乘法运算的node节点。
static Node *new_add(Node *lhs, Node *rhs, Token *tok) {
if (is_numeric(lhs->ty) && is_numeric(rhs->ty))
return new_binary(ND_ADD, lhs, rhs, tok);
...
}
static Node *new_binary(NodeKind kind, Node *lhs, Node *rhs, Token *tok) {
Node *node = new_node(kind, tok);
node->lhs = lhs;
node->rhs = rhs;
return node;
}
3.3 解析"*"
static Node *mul(Token **rest, Token *tok) {
Node *node = cast(&tok, tok); for (;;) {
Token *start = tok; if (equal(tok, "*")) {
node = new_binary(ND_MUL, node, cast(&tok, tok->next), start);
continue;
} if (equal(tok, "/")) {
node = new_binary(ND_DIV, node, cast(&tok, tok->next), start);
continue;
} if (equal(tok, "%")) {
node = new_binary(ND_MOD, node, cast(&tok, tok->next), start);
continue;
} *rest = tok;
return node;:
}
}
mul函数会调用cast函数返回代表数字3的类型同样为ND_NUM的node节点,这点同解析数字2的过程,不再赘述。
由于tok此时为"*",所以会创建类型为ND_MUL的乘法node节点,这个节点的左表达式为代表数字3的类型为
ND_NUM的node节点,右表达式为cast函数返回的代表数字4的类型为ND_NUM的node节点。
4. 解析上一步生成的语法树生成汇编代码
static void gen_expr(Node *node) {
switch (node->kind) {
case ND_NUM: {
println(" mov $%ld, %%rax", node->val);
return;
...
} gen_expr(node->rhs);
push();
gen_expr(node->lhs);
pop("%rdi"); switch (node->kind) {
case ND_ADD:
println(" add %s, %s", di, ax);
return; case ND_MUL:
println(" imul %s, %s", di, ax);
return;
...
}
...
}
4.1 gen_expr的参数为类型为ND_ADD的node节点,首先递归调用gen_expr,传入的参数为类型为ND_MUL的node节点,又会递归调用
gen_expr,传入的参数为类型为ND_NUM的代表数字4的node节点,此时会生成汇编语句"mov rax, 4",将4载入rax寄存器,gen_expr返回。
4.2 push函数生成"push rax",将4压入栈。
4.3 gen_expr的参数为类型为ND_NUM的代表数字3的node节点,会生成"mov rax, 3",将3载入rax寄存器,gen_expr返回。
4.4 pop("%rdi")函数将4弹入rdi寄存器。
4.5 由于node节点类型为ND_MUL,所以生成"imul eax, edi",计算3*4,结果保存在eax寄存器中,并从gen_expr返回。
4.6 回到参数为ND_ADD的gen_expr函数中。
4.7 push函数生成"push rax",将3*4压入栈。
4.8 gen_expr参数为类型为ND_NUM的代表数字2的node节点,会生成"mov rax, 2",将2载入rax寄存器,gen_expr返回。
4.9 pop("%rdi");函数将3*4弹入rdi寄存器。
4.10 由于node节点类型为ND_ADD,所以生成"add eax, edi",计算2+3*4,结果保存在eax寄存器中,并从gen_expr返回。
最新文章
- java数据结构_附12_图、顶点和边的定义(双链存储)
- Java程序员的日常 —— 多进程开发
- 在PYTHON3中,使用Asyncio来管理Event loop
- mvc-3模型和数据(2)
- nltk安装及wordnet使用详解
- Oracle中的 UPDATE FROM 解决方法
- ArcGIS.Server.9.3和ArcGIS API for JavaScript地图实现Toorbar功能(四)
- cdn是什么和作用有些
- C#如何获取真实IP地址
- 子句判断、启动强度和去模糊化--AForge.NET框架的使用(三)
- AngularJs学习笔记6——四大特性之依赖注入
- Scheme N皇后
- 中小学Python编程语言教学
- .Net Core 管道中的ConfigureServices 和Configure
- python列表生成式
- gym 101755
- SpringBoot日记——Spring的安全配置-登录认证与授权
- const 内联 枚举 宏
- Nodejs搭建wss服务器
- debug 在windows下的使用
热门文章
- WinForm 控件 DataGridView 常用操作
- HCNP Routing&;Switching之BGP报文结构、类型和状态
- 新一代数据科学ide平台DataSpell提前发行版体验
- 轻量级 Java 基础开发框架,Solon &; Solon Cloud 1.5.48 发布
- Visual Studio Debug only user code with Just My Code
- 如何查找一个目录中所有c文件的总行数
- pycharm安装第三方库
- Less-23 preg_replace1
- 短短 29 天,应对高峰 100W+ 访问,看浙大如何交出满分答卷
- Sequence Model-week1编程题2-Character level language model【RNN生成恐龙名 LSTM生成莎士比亚风格文字】