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返回。
  
 
 
 
 
 
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM