> For the complete documentation index, see [llms.txt](https://overleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://overleaf-pro.ayaka.space/latex/vi/bai-viet-chuyen-sau/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md).

# \expandafter hoạt động như thế nào: Từ các nguyên lý cơ bản đến khám phá mã nguồn của TeX

[Phần 1](/latex/vi/bai-viet-chuyen-sau/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md) [Phần 2](/latex/vi/bai-viet-chuyen-sau/22-how-does-expandafter-work-the-meaning-of-expansion.md) [Phần 3](/latex/vi/bai-viet-chuyen-sau/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md) [Phần 4](/latex/vi/bai-viet-chuyen-sau/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md) [Phần 5](/latex/vi/bai-viet-chuyen-sau/17-how-does-expandafter-work-a-detailed-macro-case-study.md) [Phần 6](/latex/vi/bai-viet-chuyen-sau/18-how-does-expandafter-work-a-detailed-study-of-consecutive-expandafter-commands.md)

## Giới thiệu

Bây giờ chúng ta đã đề cập đến các chủ đề nền tảng cần thiết cho việc tìm hiểu đầy đủ về `\expandafter`:

* những điều cơ bản về các token của TeX và cách chúng được tính toán;
* các nguyên lý đằng sau quá trình mở rộng của TeX;
* việc TeX sử dụng/tạo ra các danh sách token tạm thời trong quá trình xử lý tài liệu;
* cách TeX sử dụng và “xoay xở” với nhiều nguồn đầu vào (bao gồm cả các danh sách token tạm thời).

Trong bài viết này, chúng ta sẽ kết hợp các chủ đề/khái niệm này để giải thích các cơ chế nền tảng của TeX’s `\expandafter` lệnh: nói ngắn gọn, cách nó hoạt động.

## Và vì thế, để \expandafter

Ý tưởng đằng sau `\expandafter` là buộc mở rộng một lệnh (token) trước khi TeX thường làm như vậy. Với hai token, $$\mathrm{T\_1}$$ và $$\mathrm{T\_2}$$ hành động của `\expandafter` $$\mathrm{T\_1T\_2}$$ dẫn đến việc TeX xử lý $$\mathrm{T\_1}\text{<}$$mở rộng của $$\mathrm{T\_2}\text{>}$$, trong đó $$\text{<}\dots\text{>}$$ biểu thị một danh sách token. TeX mở rộng $$\mathrm{T\_2}$$ trước thời điểm đó để token $$\mathrm{T\_1}$$ (ví dụ, một primitive hoặc một macro) có thể nhìn thấy, hoặc có thể tác động lên, các token sinh ra từ việc mở rộng của $$\mathrm{T\_2}$$. Nếu token $$\mathrm{T\_2}$$ đại diện cho một mục không thể mở rộng như một ký tự không hoạt động hoặc (hầu hết) các primitive, thì hành động của `\expandafter` không làm thay đổi gì: TeX sẽ tiếp tục xử lý các token $$\mathrm{T\_1T\_2}$$ theo cách thông thường.

### Giới thiệu cách dùng \expandafter

Nếu bạn chưa dùng `\expandafter`, đây là một ví dụ sử dụng nó với primitive `\uppercase{...}`. Giả sử chúng ta muốn dàn trang tên của tệp `.tex` đầu vào chính nhưng bằng chữ in hoa. Chúng ta có thể biết các lệnh primitive TeX sau:

* `\uppercase`: đúng như tên gọi, chuyển các token ký tự thành dạng chữ hoa tương ứng (nếu có);
* `\jobname`: như chúng ta đã thấy, mở rộng để trả về tên của `.tex` .

Giả sử tệp TeX của chúng ta có tên `mycode.tex` ta có thể hợp lý mong đợi `\uppercase{\jobname}` sẽ dàn trang `MYCODE`. Nhưng không, nó dàn trang `mycode` ở dạng chữ thường. Điều gì đã “sai”?

Nếu chúng ta viết cách dùng tổng quát của `\uppercase` là

```
\uppercase{<token list>}
```

chúng ta có thể nói rằng `\uppercase` xem qua `<token list>` và sẽ chỉ tác động (đổi kiểu chữ) lên *các token ký tự* mà nó phát hiện trong `<token list>`: tất cả các token không phải ký tự đều bị *bỏ qua* vì `\uppercase` sẽ không “nhìn vào” (mở rộng) các token không phải ký tự để xem chúng chứa hay biểu thị điều gì. Vì một token đơn giản chỉ là một giá trị số nguyên, tất cả những gì `\uppercase` phải làm là xem qua danh sách token để kiểm tra xem giá trị số của mỗi token trong `<token list>` có nằm trong phạm vi các giá trị biểu thị token ký tự hay không. Nhân tiện, `\uppercase` cũng sẽ *đổi kiểu chữ của các ký tự hoạt động* để tạo một ký tự hoạt động viết hoa, và vì nó vẫn còn hoạt động nên nó cũng phải được định nghĩa, nếu không TeX sẽ báo lỗi: `Chuỗi điều khiển không được định nghĩa`, nhưng chúng ta đang lạc đề...

Ví dụ, ngay cả khi chúng ta định nghĩa một macro chỉ là văn bản

```
\def\foo{some lower-case text}
```

thì `\uppercase{\foo}` vẫn dàn trang `một số văn bản chữ thường` chứ không phải `SOME LOWER-CASE TEXT` như chúng ta mong đợi, đơn giản chỉ vì hành động của `\uppercase` không cố xác định `\foo` đại diện cho điều gì: nó thấy `\foo` như một token lệnh và bỏ qua nó, giống như nó đã làm với `\jobname`.

### Làm sao sửa điều này? \expandafter sẽ cứu nguy

Để dàn trang phiên bản chữ hoa của tên tệp, chúng ta cần sửa đổi `\uppercase{\jobname}` bằng cách buộc TeX thay thế `\jobname` bằng phần mở rộng của nó (một chuỗi các token ký tự) *trước khi* `\uppercase` bắt đầu làm việc. Một lần nữa, việc mở rộng được dùng để loại bỏ `\jobname` token (lệnh) và thay thế nó bằng kết quả của phép mở rộng của nó (một danh sách token chứa các token ký tự). Vì vậy, nếu chúng ta viết

```
\uppercase\expandafter{\jobname}
```

thì nó sẽ hoạt động: `MYCODE` sẽ được dàn trang. Điều xảy ra là TeX bắt đầu xử lý `\uppercase` và ngay lập tức kiểm tra ký tự ngoặc nhọn mở bắt buộc (`{`); tuy nhiên, TeX phát hiện một `\expandafter` lệnh khiến nó tạm thời “chuyển sự chú ý” sang xử lý `\expandafter{\jobname}`.

Nếu chúng ta so sánh

`\expandafter` $$\mathrm{T\_1T\_2}$$

với ví dụ của chúng ta

`\expandafter{\jobname}`

chúng ta có thể thấy

* $$\mathrm{T\_1} =\space$$`{`<sub>token</sub>
* $$\mathrm{T\_2} =\space$$`\jobname`<sub>token</sub>

Trong đó `{`<sub>token</sub> và `\jobname`<sub>token</sub> chỉ các giá trị token được TeX tính ra—ký hiệu chỉ số dưới <sub>token</sub> được dùng để nhắc chúng ta rằng TeX làm việc trong thế giới của các token số nguyên.

Viết `\uppercase\expandafter{\jobname}` hoạt động vì, nói khái quát (sẽ có chi tiết sau), `\expandafter` khiến TeX thực hiện các tác vụ sau:

1. đọc và lưu dấu mở `{`<sub>token</sub>;
2. đọc token tiếp theo: `\jobname`<sub>token</sub>. TeX nhận ra rằng `\jobname`<sub>token</sub> đại diện cho một lệnh có thể mở rộng và mở rộng nó. `\jobname`<sub>token</sub> được thay thế bằng phần mở rộng của nó—a series of character tokens;
3. sau khi mở rộng `\jobname` lệnh, TeX đặt `{`$$\_\mathrm{token}$$ “trở lại đầu vào” và sử dụng danh sách token sinh ra từ việc mở rộng của `\jobname` để TeX sẽ đọc `\uppercase{`<sub>token</sub>`<expansion of \jobname>`<sub>danh sách token (các ký tự)</sub>`}`, và điều này tạo ra kết quả như mong muốn.

Sơ đồ sau cho thấy TeX xử lý `\uppercase\expandafter{\jobname}`—hãy đọc hình từ dưới lên để theo dõi luồng xử lý.

![Cách \expandafter hoạt động](/files/f5f6d8663839ee3180a65d7556359dc2c36d1c2a)

Các ghi chú sau giải thích các giai đoạn xử lý khác nhau.

1. TeX bắt đầu xử lý `\uppercase` và kiểm tra ký tự ngoặc nhọn mở bắt buộc (`{`) nhưng phát hiện một `\expandafter` lệnh này.
2. Nếu chúng ta so sánh `\expandafter` $$\mathrm{T\_1T\_2}$$ trong đầu vào của chúng ta là `\expandafter{\jobname}` chúng ta có thể thấy $$\mathrm{T\_1} =$$`{`<sub>token</sub> và $$\mathrm{T\_2} =$$`\jobname`<sub>token</sub>. Lưu ý rằng ở đây chúng ta sẽ dùng ký hiệu chỉ số dưới <sub>token</sub> để cho biết TeX đang xử lý các giá trị token số nguyên.
3. `\expandafter` đọc, rồi tạm thời lưu, `{`<sub>token</sub> bằng cách lưu giá trị token số nguyên đó vào một biến nội bộ. Sau đó, TeX sẽ chèn lại token đó vào đầu vào sau khi xử lý `\jobname` lệnh này.
4. `\expandafter` đọc token tiếp theo, `\jobname`<sub>token</sub>, và mở rộng `\jobname` lệnh này.
5. Phần mở rộng của `\jobname` tạo ra một danh sách token tạm thời chứa một chuỗi các token ký tự đại diện cho `.tex` tên tệp. Lưu ý rằng tất cả các token ký tự do `\jobname` được tính bằng mã danh mục 12.
6. Khi `\jobname` đã được mở rộng, TeX chèn lại token đã lưu ở bước 3 (`{`<sub>token</sub>) và đặt nó trở lại đầu vào. TeX làm vậy bằng cách tạo thêm một danh sách token chứa *một* `{`<sub>token</sub>
7. TeX bây giờ đã xử lý xong `\expandafter`, dẫn đến hai danh sách token sẵn sàng được dùng làm nguồn đầu vào của TeX. TeX giờ quay lại xử lý `\uppercase` nhưng đã cấu hình đầu vào của nó sao cho hai danh sách token được tạo bởi `\expandafter` trở thành nguồn token cho `\uppercase`—bây giờ nhìn thấy `\uppercase{`<sub>token</sub>`<expansion of \jobname>`<sub>danh sách token (các ký tự)</sub>`}`. `\uppercase` hiện thấy một chuỗi các token ký tự và có thể tạo ra kết quả mong muốn.
8. Sau khi đọc hết tất cả các token ký tự do `\jobname`, TeX quay lại lấy token từ nguồn đầu vào trước đó của nó (tệp `.tex` ) từ đó nó sẽ đọc token tiếp theo: dấu đóng `}` được yêu cầu để kết thúc danh sách token được xử lý bởi `\uppercase`.

### \expandafter và các danh sách token nội bộ

Các danh sách token tạm thời là một *yếu tố* thiết yếu trong `\expandafter`trong hành vi xử lý của nó: hiểu về việc sử dụng và sự tồn tại của các danh sách token đó có thể giúp làm rõ cách `\expandafter` đạt được kết quả của nó, đặc biệt khi cố gắng viết hoặc hiểu các macro sử dụng nhiều `\expandafter` lệnh liên tiếp để đạt được các dạng xử lý token phức tạp hơn: `\expandafter\expandafter\expandafter...`

Một yếu tố quan trọng khác trong `\expandafter`hành vi của nó, đặc biệt với nhiều `\expandafter` lệnh liên tiếp, là việc sử dụng *đệ quy* (ngay trong phần mềm TeX)—một chủ đề chúng ta sẽ xem xét sau trong bài viết này.

Để giúp hiểu rõ hơn về các danh sách token tạm thời, chúng ta sẽ xem thêm một ví dụ nữa về `\expandafter`, lần này với `\the` lệnh này.

#### \expandafter và các danh sách token nội bộ: ví dụ 2

Trong ví dụ này, chúng ta sẽ thấy cách `\expandafter` có thể được dùng để tác động đến các token được lưu trong một thanh ghi token thông qua lệnh `\toks` . Sau đây là các primitive TeX chúng ta sẽ dùng:

* `**\count** *thanh ghi*=*số*`: một primitive TeX dùng để lưu giá trị `*số*` vào vị trí TeX `*thanh ghi*`;
* `**\toks** *thanh ghi*={*danh sách token*}`: một primitive TeX dùng để lưu `*danh sách token*` vào vị trí thanh ghi token `*thanh ghi*`—lưu một chuỗi token để dùng sau;
* `**\the** *token*`: một lệnh primitive TeX có thể mở rộng, xử lý `*token*`, mặc dù kết quả chính xác phụ thuộc vào bản chất của `*token*` đang được xử lý. `\the` có nhiều công dụng: trong số đó là dàn trang giá trị được lưu trong một tham số hoặc biến TeX (ví dụ, một thanh ghi). Các cách dùng khác của `\the` bao gồm chèn một bản sao của các token được lưu trong một thanh ghi token. Ở đây, chúng ta sẽ dùng `\the` để dàn trang giá trị được lưu trong một `\count` thanh ghi.

Chúng ta sẽ bắt đầu với đoạn mã TeX sau để lưu giá trị `12345` vào `\count` thanh ghi `99`:

```
\count99=12345
```

Nếu chúng ta muốn dàn trang giá trị được lưu trong `\count99` chúng ta có thể dùng `\the\count99` (hoặc `\number\count99`).

Tiếp theo chúng ta sẽ dùng `\toks` lệnh để lưu một số token vào thanh ghi token `99`:

```
\toks99={\the\count99 }
```

Danh sách token được lưu trong thanh ghi token `99` sẽ chứa các mục sau:

|                       |                                                                                                    |
| --------------------- | -------------------------------------------------------------------------------------------------- |
| **Giá trị token TeX** | **Mục biểu diễn**                                                                                  |
| 5382                  | `\the`                                                                                             |
| 7885                  | `\count`                                                                                           |
| 3129                  | `9` (mã ký tự 57 với mã danh mục 12) dẫn đến giá trị token là $$256 \times 12 + 57 = 3129$$        |
| 3129                  | `9` (mã ký tự 57 với mã danh mục 12), dẫn đến giá trị token là $$256 \times 12 + 57 = 3129$$       |
| 2592                  | `<space>` (mã ký tự 32 với mã danh mục 10), dẫn đến giá trị token là $$256 \times 10 + 32 = 2592$$ |

Lưu ý rằng danh sách token được tạo bởi `\toks99` thì *không* chứa giá trị dữ liệu thực sự được lưu trong `\count99` vì `\toks` lệnh không thực hiện mở rộng: nó chỉ đơn giản tạo ra các token và lưu chúng lại. Trong ví dụ của chúng ta, `\the` không được mở rộng nên nó không xử lý `\count99`; ở đây `\the` chỉ đơn giản được biến thành một token (giá trị 5382) và lưu vào danh sách token.

Nếu chúng ta muốn `\toks99` danh sách token chứa các token đại diện cho dữ liệu được lưu trong `\count99` chúng ta sẽ cần một cách nào đó để tạo ra các token đó (làm cho chúng khả dụng) để `\toks` lệnh có thể truy cập chúng. Và dĩ nhiên `\expandafter` có thể làm điều này cho chúng ta. Nếu chúng ta viết:

```
        \toks99=\expandafter{\the\count99 }
```

hành động/xử lý của `\toks` lệnh sẽ được “tạm giữ” trong khi `\expandafter` gây ra (buộc) việc mở rộng của `\the` mà, đến lượt nó, tác động lên `\count` để tạo ra một danh sách token tạm thời chứa các token ký tự đại diện cho dữ liệu được lưu trong `\count99`. Một điểm nhỏ nhưng quan trọng là `<space>` ký tự sau các chữ số `99`: ký tự đó `<space>` có tác dụng kết thúc quá trình quét của TeX khi nó đang tìm một giá trị số.

Ở đây, hành động của `\expandafter` rất giống với ví dụ `\jobname` này.

1. Đọc và lưu dấu mở `{`<sub>token</sub>.
2. Đọc token tiếp theo, `\the`<sub>token</sub>, đại diện cho một lệnh có thể mở rộng, nên TeX mở rộng nó. `\expandafter` buộc mở rộng `\the` mà sau đó tác động lên `\count99` để chuyển dữ liệu được lưu trong `\count` thanh ghi `99` (số 12345) thành một danh sách token tạm thời. Danh sách đó sẽ chứa các token ký tự biểu diễn các chữ số `1`, `2`, `3`, `4` và `5`—các token ký tự với mã danh mục 12.
3. Sau khi mở rộng và xử lý `\the`, TeX đặt `{`<sub>token</sub> “trở lại đầu vào” và sử dụng danh sách token sinh ra từ `\the\count99` để TeX sẽ đọc `\toks99={`<sub>token</sub>`<expansion of \the\count99>`<sub>danh sách token (các ký tự)</sub>`}` và điều này tạo ra kết quả như mong muốn.

Chuỗi sự kiện này được tóm tắt trong sơ đồ sau—hãy đọc hình từ dưới lên để theo dõi luồng xử lý.

![Cách \expandafter hoạt động](/files/008523ed51e5c1aaafb6890316498c9b01e8659c)

1. TeX bắt đầu xử lý `\toks`; nó thấy dấu `=` tùy chọn, rồi kiểm tra ký tự ngoặc nhọn mở bắt buộc (`{`, hoặc bất kỳ ký tự nào có mã danh mục 1) dùng để chỉ ra sự bắt đầu của một danh sách token. Tuy nhiên, TeX phát hiện một `\expandafter` lệnh và tiến hành thực thi lệnh đó thay thế.
2. Nếu chúng ta so sánh `\expandafter` $$\mathrm{T\_1T\_2}$$ trong đầu vào của chúng ta là `\expandafter{\the\count99 }` chúng ta có thể thấy $$\mathrm{T\_1} =$$ `{`<sub>token</sub> và $$\mathrm{T\_2} =$$ `\the<sub>token</sub>`.
3. `\expandafter` đọc, rồi tạm thời lưu, `{`<sub>token</sub> (TeX tạm thời lưu giá trị token số nguyên đó vào một biến nội bộ). Về sau, TeX sẽ chèn lại token đó vào đầu vào, sau khi xử lý `\the`
4. `\expandafter` đọc token tiếp theo, `\the`<sub>token</sub> và mở rộng nó.
5. Phần mở rộng của `\the` tạo ra một danh sách token tạm thời từ việc xử lý `\count99`—danh sách token đó chứa một chuỗi các token ký tự biểu diễn giá trị dữ liệu được lưu trong `\count` thanh ghi `99`.
6. Khi `\the` đã được mở rộng, TeX chèn lại token đã lưu ở bước 3 (`{`<sub>token</sub>) và đặt token đó trở lại đầu vào. TeX làm điều đó bằng cách tạo thêm một danh sách token chứa *một* token `{`<sub>token</sub>.
7. TeX bây giờ đã xử lý xong `\expandafter` và tạo ra hai danh sách token sẵn sàng được dùng làm nguồn đầu vào tiếp theo. TeX quay trở lại xử lý `\toks99=` nhưng giờ TeX đã cấu hình đầu vào của nó sao cho hai danh sách token được tạo bởi `\expandafter` trở thành nguồn token cho `\toks`—bây giờ nhìn thấy `{`<sub>token</sub>`<expansion of \the\count99>`<sub>danh sách token (các ký tự)</sub>`}`. `\toks` có thể truy cập, và lưu, chuỗi 5 token ký tự biểu thị giá trị dữ liệu (`12345`) được lưu trong `\count99`: kết quả mong muốn của chúng ta.
8. Sau khi đọc hết tất cả các token ký tự do `\the\count99`, TeX quay lại lấy token từ nguồn đầu vào trước đó của nó (tệp `.tex` ) từ đó nó sẽ đọc token tiếp theo: dấu đóng `}` được yêu cầu để kết thúc danh sách token sẽ được lưu bởi `\toks99={...}`.

## Cách \expandafter thực sự hoạt động

Trong phần này, chúng ta sẽ nhìn “ở mức thấp” vào bên trong chính TeX: khám phá mã nguồn/các hàm trong TeX thực hiện hành vi của `\expandafter`. Chi tiết được trình bày bằng mã giả kiểu C nhưng sẽ dễ hiểu với bất kỳ ai quen thuộc với các ngôn ngữ lập trình khác.

Sơ đồ chú giải sau giải thích cách TeX triển khai `\expandafter` như một phần của một hàm lớn hơn gọi là `expand()`—hàm lõi điều khiển quá trình mở rộng của TeX. Trong phần chịu trách nhiệm triển khai `\expandafter` chúng ta có thể thấy [*đệ quy* hành vi](https://en.wikipedia.org/wiki/Recursion) trong đó một lời gọi khác đến `expand()` hàm được dùng để xử lý token thứ hai được đọc vào, $$\mathrm{T\_2}$$, đối với những trường hợp mà $$\mathrm{T\_2}$$ có thể mở rộng.

Mặc dù đoạn mã này xuất hiện trong bộ máy TeX của Knuth, các nguyên lý cơ bản được trình bày trong hình này đều áp dụng cho mọi bộ máy TeX.

![Cách \expandafter hoạt động bên trong TeX](/files/5f454099a340a3274c77c223599cbfaf75c8d6b4)

Nhiệm vụ đầu tiên của `expand()` là xác định xem lệnh cần mở rộng là macro hay primitive vì macro có một quá trình mở rộng chuyên biệt được xử lý bởi hàm có tên `macrocall()`.

Nếu lệnh cần mở rộng là một primitive, thì `expand()` hàm sử dụng giá trị mã lệnh hiện tại (được lưu trong biến toàn cục `curcmd`) để xác định primitive cụ thể nào cần được xử lý. Chúng ta có thể thấy các chi tiết này trong một danh sách đầy đủ hơn của `expand()`:

```
    void expand(void)
    {
    //curcmd là một biến toàn cục
    if(curcmd != macro) // curcmd < 111
    {
      switch(curcmd)
      {
        case \expandafter: // Xử lý lệnh \expandafter T1T2
        {
            gettoken(); // Đọc token T1
            t = curtok; // Lưu token T1 vào biến cục bộ t
            gettoken(); // Đọc token T2
            if(curcmd > 100) // Token T2 có thể mở rộng không?
                expand();    // Đúng! T2 có thể mở rộng:
                             // thực hiện mở rộng T2 bằng cách
                             // gọi đệ quy hàm expand()
            else
                backinput(); // T2 không thể mở rộng: đặt token đó
                             // trở lại đầu vào để được đọc lại (sau này)

            curtok = t ;  // Khôi phục biến toàn cục curtok về giá trị đã lưu của T1
            backinput() ; // Đưa token T1 trở lại đầu vào
                          // trước các token sinh ra từ việc mở rộng T2
        }
        break;

        // Mã để xử lý các lệnh có thể mở rộng khác
        case “lệnh chuyển sang văn bản”: // Bất kỳ một trong \number, \string, \romannumeral,
                                        // \meaning, \fontname, \jobname
                                        // Chúng có cùng giá trị curcmd
        break;

        case \noexpand: // Ngăn việc mở rộng token tiếp theo
        ...
        break;

        case \csname:  // Tạo tên một chuỗi điều khiển.
        ...
        break;

        case \the: // Chèn một số token
        ....
        break;

        case “lệnh kiểm tra \if...”: // Xử lý một trong các câu lệnh điều kiện của TeX:
                                      // \if, \ifcat, \ifnum, \ifdim,\ifodd, \ifvmode,
                                      // \ifhmode, \ifmmode, \ifinner, \ifvoid,
                                      // \ifhbox, \ifvbox, \ifx, \ifeof, \iftrue, \iffalse,
                                      // \ifcase, \ifdefined, \ifcsname, \iffontchar
        ...
        break;

        case “\fi hoặc \else”: // Kết thúc điều kiện hiện tại
        ...
        break;

        // v.v. cho bất kỳ lệnh nguyên thủy có thể mở rộng nào khác được hỗ trợ bởi
        // bộ máy TeX

        }

    }else // Không phải là một lệnh nguyên thủy có thể mở rộng: đó là một macro
        {
             macrocall()
        }
        //... đã lược bỏ thêm mã
    }
```

### Tình yêu của TeX đối với các biến toàn cục

Có lẽ phản ánh tuổi đời và thời đại mà nó được thiết kế, mã nguồn của TeX sử dụng rất nhiều cái gọi là [các biến toàn cục](https://en.wikipedia.org/wiki/Global_variable)—thực tế có hàng trăm biến như vậy. Bản chất của các biến toàn cục là chúng có thể được thay đổi/chỉnh sửa từ bất kỳ đâu trong mã nguồn TeX — mà đối với TeX của Knuth, đó là một tệp đơn khối duy nhất chứa hơn 25.000 dòng mã và hàng trăm hàm. Hiểu TeX hoạt động như thế nào không phải lúc nào cũng là việc dễ dàng...

Để xử lý `\expandafter`, TeX đọc các token từ đầu vào hiện tại của nó bằng một hàm có tên là `gettoken()` mà tác dụng của nó là tạo ra một token và đặt giá trị của một số biến toàn cục quan trọng được dùng xuyên suốt mã nguồn TeX. Hai biến như vậy, được cập nhật bởi hành động của `gettoken()`, được dùng trong phần triển khai của `\expandafter`:

* `curtok`: (token hiện tại) giá trị số nguyên của token vừa được đọc;
* `curcmd`: (mã lệnh hiện tại) mã lệnh của lệnh (hoặc ký tự) được token biểu diễn `curtok`.

Khi xử lý `\expandafter`$$\mathrm{T\_1T\_2}$$ TeX đọc token $$\mathrm{T\_1}$$ và tạm thời lưu giá trị của nó (một số nguyên) vào một biến cục bộ có tên là `t`. Sau đó TeX đọc $$\mathrm{T\_2}$$ và kiểm tra xem token đó có đại diện cho một lệnh có thể mở rộng hay không — bằng cách kiểm tra xem mã lệnh của nó (`curcmd`) có > 100 không. Nếu vậy, TeX cần mở rộng lệnh được biểu diễn bởi $$\mathrm{T\_2}$$ và gọi thêm một lần nữa hàm `expand()`: đây là một ví dụ về *đệ quy* vì `expand()` hàm tự gọi chính nó. Nhận thức về tính đệ quy của việc mở rộng, đặc biệt khi dùng `\expandafter`, có thể giúp hiểu cách nhiều `\expandafter` lệnh liên tiếp — tức là, `\expandafter\expandafter\expandafter...` tạo ra hiệu ứng của chúng.

Nếu token $$\mathrm{T\_2}$$ có thể mở rộng, việc mở rộng diễn ra và khi lời gọi đệ quy tới `expand()` trả về, mã bên trong phần triển khai của `\expandafter` chèn lại token $$\mathrm{T\_1}$$ vào đầu vào. Biến toàn cục `curtok` được gán lại bằng giá trị của token đã lưu — được lưu trong biến cục bộ `t`, vốn là giá trị của token $$\mathrm{T\_1}$$— và một lời gọi được thực hiện tới hàm `backinput().`

#### Hàm backinput()

Đúng như tên gọi, hàm này đưa một token “trở lại đầu vào”. Để làm điều đó, TeX sử dụng giá trị hiện tại của biến toàn cục `curtok` để tạo một danh sách token chứa một token duy nhất (mà giá trị số nguyên của nó được cung cấp bởi `curtok`). TeX cũng sắp xếp cơ chế xử lý đầu vào để bảo đảm danh sách một-token đó, vào thời điểm thích hợp, sẽ được TeX đọc lại như một phần của quá trình xử lý đầu vào tiếp theo. Hãy lưu ý kỹ rằng token $$\mathrm{T\_1}$$ được chèn lại *sau khi việc mở rộng hoàn tất*, điều này đảm bảo TeX sẽ đọc token được chèn lại đó *trước khi* khi nó đọc các token sinh ra từ việc mở rộng của $$\mathrm{T\_2}$$.

### Xử lý macro: hàm macrocall()

Như đã thảo luận trước đó, tất cả các macro, cùng với một số lệnh nguyên thủy, đều có thể mở rộng và mọi quá trình mở rộng đều đi qua `expand()` hàm. Tuy nhiên, `expand()` cẩn thận sử dụng `curcmd` giá trị (lệnh hiện tại) để phân biệt giữa các lệnh nguyên thủy có thể mở rộng và macro vì quá trình mở rộng macro được xử lý bởi một hàm chuyên dụng có tên là `macrocall()`. Macro cần một quy trình mở rộng chuyên biệt vì các đối số macro và các token phân cách phải được quét tìm theo một cách rất cụ thể và chặt chẽ; do đó, quy trình đó được giao cho một hàm được thiết kế để làm việc đó: `macrocall()`.

#### Mở rộng macro so với thực thi macro

Macro *mở rộng* không phải là cùng một quá trình với *thực thi*: việc mở rộng một macro là quá trình tiền-thực-thi mà TeX thực hiện để đưa macro *sẵn sàng để thực thi*. “Thực thi” của macro xảy ra khi TeX chủ động đọc và xử lý các token có trong định nghĩa của macro (văn bản thay thế) và các đối số (tham số) của nó.

#### Mở rộng macro

Để mở rộng một macro, TeX trước tiên kiểm tra xem macro có nhận đối số hay không; nếu có, `macrocall()` nó sẽ quét đầu vào rất cẩn thận để tìm các token sẽ trở thành đối số của macro. Quá trình đó bao gồm việc kiểm tra đầu vào của người dùng xem có token phân cách nào được dùng trong định nghĩa gốc của macro hay không — mẫu token trong một lời gọi macro phải khớp chính xác với mẫu token có trong định nghĩa đã lưu. Tuy nhiên, các token được dùng làm dấu phân cách sẽ bị TeX loại bỏ đơn giản: về thực chất, chúng chỉ là một dạng “dấu câu” mà TeX dùng để xác định các token thực sự sẽ trở thành đối số của macro — tức là các token mà người dùng muốn macro xử lý. Để biết thêm thông tin về token phân cách, xem [Macro TeX thực sự hoạt động như thế nào](/latex/vi/chu-de-khac/22-how-tex-macros-actually-work-part-4.md).

Đối với mỗi tham số (`#1, #2...#9`) có trong định nghĩa gốc của macro, TeX quét lời gọi macro thực tế để xác định những token do người dùng cung cấp sẽ dành cho từng tham số nào (tức là tạo thành các đối số của macro). Quá trình đó tạo ra một hoặc nhiều danh sách token nhỏ: một danh sách cho mỗi đối số macro.

Sau khi bất kỳ đối số nào của macro đã được phát hiện và các danh sách token của chúng đã được chuẩn bị, TeX lấy định nghĩa của macro (văn bản thay thế) được lưu trong bộ nhớ của nó và sắp xếp quá trình xử lý đầu vào sao cho bất cứ khi nào TeX sẵn sàng đọc/xử lý thêm token, nó sẽ đọc chúng từ văn bản thay thế của macro, qua đó thực thi macro. Tại điểm thích hợp, trong quá trình thực thi macro, các danh sách token đại diện cho đối số của macro sẽ được đưa vào đúng vị trí trong văn bản thay thế của macro.

Một lần nữa, việc mở rộng một lệnh macro có nghĩa là *loại bỏ* lệnh macro đó (token) khỏi đầu vào và *thay thế* nó bằng danh sách token được lưu làm văn bản thay thế của macro.

Để tìm hiểu sâu về xử lý macro của TeX, hãy xem chuỗi bài viết gồm sáu phần [Macro TeX thực sự hoạt động như thế nào?](/latex/vi/chu-de-khac/01-a-six-part-series-how-do-tex-macros-actually-work.md)

[Phần 1](/latex/vi/bai-viet-chuyen-sau/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md) [Phần 2](/latex/vi/bai-viet-chuyen-sau/22-how-does-expandafter-work-the-meaning-of-expansion.md) [Phần 3](/latex/vi/bai-viet-chuyen-sau/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md) [Phần 4](/latex/vi/bai-viet-chuyen-sau/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md) [Phần 5](/latex/vi/bai-viet-chuyen-sau/17-how-does-expandafter-work-a-detailed-macro-case-study.md) [Phần 6](/latex/vi/bai-viet-chuyen-sau/18-how-does-expandafter-work-a-detailed-study-of-consecutive-expandafter-commands.md)


---

# 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://overleaf-pro.ayaka.space/latex/vi/bai-viet-chuyen-sau/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.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.
