# 第11步：return

本節會加上`return`的支援，讓我們可以編譯底下的程式碼：

```c
a = 3;
b = 5 * 6 - 8;
return a + b / 2;
```

`return`也可以出現在程式的中途。就像普通的C語言，程式執行到最初的`return`就會結束，從函式中返回。舉例來說底下的程式會回傳最初`return`的值，也就是5：

```c
return 5;
return 8;
```

為了實作這個功能，我們先來想想加上`return`的文法會長什麼樣吧。至今為止我們的陳述都只有式子，新的文法則需要接受`return <式子>;`這樣的陳述。所以文法會變成像這樣：

```
program = stmt*
stmt    = expr ";"
        | "return" expr ";"
...
```

實作上，標記解析器、分析器、指令產生器全部都需要逐步修改。

首先讓標記解析器可以識別`return`這個標記，以`TK_RETURN`型的標記來代表。像`return`或`while`、`int`這類文法上有特別意義的標記（非關鍵字）數量非常有限，像這樣以不同型態來代表不同標記的作法會比較簡潔。

接下來，要判斷標記是否為`return`，似乎標記解吸器只要判斷剩餘的輸入字串是否以 return 開頭就可以了，但這樣的話像`returnx`這樣的標記會被標記解吸器誤判為`return`和`x`。所以，除了輸入要以 return 開頭，還需要判斷接下來下一個字不是標記的一部份。

判斷所給定的文字是否為組成標記的字，也就是英文字母、數字或底線的函式如下所示：

```c
int is_alnum(char c) {
  return ('a' <= c && c <= 'z') ||
         ('A' <= c && c <= 'Z') ||
         ('0' <= c && c <= '9') ||
         (c == '_');
}
```

利用這個函式，在tokenize裡加上底下的程式碼，就可以把`return`做標記解析為`TK_RETURN`型了：

```c
if (strncmp(p, "return", 6) == 0 && !is_alnum(p[6])) {
  tokens[i].ty = TK_RETURN;
  tokens[i].str = p;
  i++;
  p += 6;
  continue;
}
```

接著對分析器動手，讓其可以分析包含有`TK_RETURN`型的標記列。為此，我們先加上代表`return`的結點型態`ND_RETURN`。然後，修改讀取陳述的函式，讓其可以分析`return`句的文法。如往例，只要把文法直接對應到函式上，就可以成功分析文法了。新的`stmt`函式如下所示：

```c
Node *stmt() {
  Node *node;

  if (consume(TK_RETURN)) {
    node = calloc(1, sizeof(Node));
    node->kind = ND_RETURN;
    node->lhs = expr();
  } else {
    node = expr();
  }

  if (!consume(';'))
    error_at(tokens[pos].str, "不是';'標記");
  return node;
}
```

`ND_RETURN`型的結點只有在這裡可以產生，所以在此我們不寫新的函式，而是直接在該處進行`malloc`並設定其值。

最後修改指令產生器，讓其可以輸出對應`ND_RETURN`結點的合適指令。新的`gen`函式的一部份如下所示：

```c
void gen(Node *node) {
  if (node->kind == ND_RETURN) {
    gen(node->lhs);
    printf("  pop rax\n");
    printf("  mov rsp, rbp\n");
    printf("  pop rbp\n");
    printf("  ret\n");
    return;
  }
  ...
```

上述程式碼的`gen(node->lhs)`函式呼叫中，會輸出將作為`return`回傳值式子的指令。該段指令應該會在堆疊頂部留下1個值。`gen(node->lhs)`後接著的指令，會把該值從堆疊中彈出並放在 RAX，然後從函式中回傳。

到前一章為止所實作的功能，在函式的最後一定會輸出1個`ret`指令。照本章所說明的方法實作`return`後，就會在`return`句以外還會輸出多的`ret`指令。也可以這些指令統整起來，但此處為了實作簡單，我們允許其輸出複數的`ret`指令。在現在這個時間點，太在意這些瑣碎的細節也沒用，重要的是以實作簡單為優先。雖然能寫難的程式是很有用的能力，但有時讓程式不要變得太難，是更有用的能力。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://koshizuow.gitbook.io/compilerbook/function_local_variable/step11.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
