> For the complete documentation index, see [llms.txt](https://koshizuow.gitbook.io/compilerbook/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://koshizuow.gitbook.io/compilerbook/calculator_level_language/step4.md).

# 第4步：改良錯誤訊息

到目前為止，我們做的編譯器在輸入的文法有錯誤時，只能知道有錯誤發生，但不知道在哪裡。這一步我們針對這個問題進行改良。具體來說，要顯示如下較為直覺的錯誤訊息：

```
$ ./9cc "1+3++" > tmp.s
1+3++
    ^ 不是數值

$ ./9cc "1 + foo + 5" > tmp.s
1 + foo + 5
    ^ 標記解析失敗
```

要想能顯示這樣的錯誤訊息，必須在錯誤發生時，知道式發生在輸入的第幾個位元才行。為此，我們來把作為輸入程式的文字列存成變數 `user_input`，定義一個新的函式可以接受只到其中某一個位置的指標，並顯示錯誤訊息。程式碼如下：

```c
// 輸入程式
char *user_input;

// 回報錯誤的位置
void error_at(char *loc, char *fmt, ...) {
  va_list ap;
  va_start(ap, fmt);

  int pos = loc - user_input;
  fprintf(stderr, "%s\n", user_input);
  fprintf(stderr, "%*s", pos, ""); // 輸出pos個空白
  fprintf(stderr, "^ ");
  vfprintf(stderr, fmt, ap);
  fprintf(stderr, "\n");
  exit(1);
}
```

&#x20;`error_at`所接受的指標，會指向指向輸入文字列的某個位置。計算該指標和輸入的開頭的差，就知道錯誤發生的位置，然後就可以在該位置顯示一個醒目的`^`符號。

把 `argv[1]`存到`user_input`變數，然後把程式碼中`error("不是數值")`改為 `error_at(token->str, "不是數值")`，這一步就完成了。

實用的編譯器也應該要針對輸入有錯誤時的行為寫測試，但現階段，輸出錯誤訊息只是為了幫助除錯，可以暫時不用寫測試沒關係。

> 參考實作： [c6ff1d98a1419e69](https://github.com/rui314/chibicc/commit/c6ff1d98a1419e69c31902447e2caa85af4e9844)

{% hint style="info" %}

#### 小知識：程式碼排版器（formatter）

就像讀自然語言的文章（編註：原文為日文，但一般語言也一樣）時，如果遇上句讀等在基本寫作層級發生錯誤的文章會很難讀下去，程式語言如果在縮排、有無空白等前後沒有一致的話，別說程式的內容了，根本稱不上是漂亮的程式碼。程式碼的排版就是在這些和功能無關的地方，機械化地套用某種規則，注意不要寫出讓人分心、難讀的程式碼。

如果很多人一起開發的時候，得好好商量一下究竟要採用哪種格式，但本書是是一個人在開發，可以在主流的格式中隨意選一個自己喜歡的。

在比較新出現的程式語言裡，為了消除「該選什麼樣的格式」這種只是主觀喜好而非必要的討論，有的語言有提供官方的格式。舉例來說像Go語言就有提供`gofmt`這個指令，可以幫忙把程式碼整型變漂亮。`gofmt`並沒有選擇格式風格的選項，也就是說只能整成「Go官方格式」這個唯一選擇。因為不給選擇的空間，Go完全解決了「該怎麼處理格式」的問題。

C或C++有 clang-format 這個排版器可以用，但本書並不想推薦要使用這類工具。與其在寫了之後把奇怪的程式碼用排版器整型，不如試著一開始寫的時候就注意寫長相前後一致的程式。
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://koshizuow.gitbook.io/compilerbook/calculator_level_language/step4.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
