c语言是如何解析表达式语句"2+3*4;"的?
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返回。
转 https://www.cnblogs.com/iszhang/p/15596715.html
标签:
C++
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)