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