资讯专栏INFORMATION COLUMN

c语言是如何解析表达式语句"2+3*4;"的?

2501207950 / 1124人阅读

摘要:语法分析解析字符如果类型为数字,则解析数字,不为浮点数,所以执行分支。解析函数会调用函数返回代表数字的类型同样为的节点,这点同解析数字的过程,不再赘述。参数为类型为的代表数字的节点,会生成将载入寄存器,返回。函数将弹入寄存器。

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.ucloud.cn/yun/124823.html

相关文章

  • Vue编译器AST抽象语法树源码分析

     直接进入核心现在说说baseCompile核心代码:  //`createCompilerCreator`allowscreatingcompilersthatusealternative   //parser/optimizer/codegen,e.gtheSSRoptimizingcompiler.   //Herewejustexportadefaultcompilerusingthede...

    3403771864 评论0 收藏0
  • 解析ES6变量赋值和基本数据类型

      let和const  let和const两者并不存在变量提升  这里要说明的是变量一定要在声明后使用,否则报错。  vara=[];   for(vari=0;i<10;i++){   a[i]=function(){   console.log(i);   };   }   a[6]();//10  变量i是var声明的,我们要知道这里在全局范围内都有效。我们要知道在每一次循环中,新的...

    3403771864 评论0 收藏0
  • vue parseHTML函数源码解析start钩子函数

      关于parseHTML 函数源码解析 AST 相关知识已做过介绍,下面可以看看Vue start钩子函数源码。  start:functionstart(tag,attrs,unary){   //checknamespace.   //inheritparentnsifthereisone   varns=(currentParent&&currentParent.ns)||...

    3403771864 评论0 收藏0
  • python自动化具体代码解析

      小编写这篇文章的主要目的,主要是给大家解读关于python接口自动化的一些详细代码实例,具体是什么呢?下面就给各位读者详细的解答下。  前言  我们做接口自动化的过程中,解决端口依赖的相关数据时,一般会采用正则匹配去进行获取有关的信息。  正则匹配,又被称为正规表示式、正规表示法、正规表达式、规则表达式、常规表示法(RegularExpression,在编码中常缩写为regex、regexp或...

    89542767 评论0 收藏0

发表评论

0条评论

2501207950

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<