> 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/zh-tw/geng-duo-zhu-ti/18-how-overleaf-created-the-tex-primitive-reference-data.md).

# Overleaf 如何建立 TeX 基本命令參考資料

本文說明用來產生 TeX 原始命令兩張交叉參照表的方法與技術：

* [按 TeX 引擎列出的 TeX 原始命令](/latex/zh-tw/geng-duo-zhu-ti/46-tex-primitives-listed-by-tex-engine.md) 以及；
* [按 CJK TeX 引擎列出的 TeX 原始命令](/latex/zh-tw/geng-duo-zhu-ti/45-tex-primitives-listed-by-cjk-tex-engine.md).

本資訊提供給對細節較感興趣的讀者，但並非使用交叉參照表本身的前提。為了滿足不同讀者的需求，我們提供一個非常簡短的摘要版本，並附上較長的說明，供希望更深入探討這些議題的人參考。

## 簡短摘要／概覽版本

為了建立交叉參照表，Overleaf 處理了 9 個 TeX 引擎的原始碼，以擷取每個引擎所支援的原始命令清單：該過程產生了 9 個文字檔（每個 TeX 引擎 1 個檔案）。這 9 組原始命令合併後形成一份「主清單」，其實也就是各組原始命令的聯集：總共約有 1000 個獨特的原始命令分布於各個引擎中。對於每個引擎，都會將其自己的原始命令清單與主檔（所有命令的集合）交叉參照，以判定它支援那約 1000 個命令中的哪些：這些比較結果列於下列兩張表：

* [TeX 原始命令交叉參照資料](/latex/zh-tw/geng-duo-zhu-ti/46-tex-primitives-listed-by-tex-engine.md)
* [TeX 原始命令交叉參照資料（適用於 CJK 引擎）](/latex/zh-tw/geng-duo-zhu-ti/45-tex-primitives-listed-by-cjk-tex-engine.md)

## 「建置軟體」101：那是什麼意思？

在本文其餘部分，我們會提到「建置 TeX 引擎」這個概念；如果你不是程式設計師，或沒有用 C 或 C++ 之類的編譯式語言寫過程式，這可能是個陌生的概念。就我們的用途而言，建置軟體——也就是 TeX 引擎——是指從其組成部分，也就是以用來開發該程式的程式語言所寫成的原始碼檔，產生可執行的 TeX 程式的過程。

## 完整版本：想知道細節嗎？請繼續閱讀……

每一個以 TeX 為基礎的排版引擎都支援 TeX 語言的一種「方言」：一組特定的原始命令，這些命令控制各引擎的排版功能，並提供建立／定義巨集的基礎積木：也就是使用者自訂的命令序列。無論是為 LaTeX、plain TeX 或任何其他巨集套件撰寫，每一個巨集歸根究柢都是由原始命令所構成——雖然你可能需要透過多層額外的巨集深入追查很長一段路，才能到達 TeX 原始命令的「基岩層」。為了產生這些原始命令的參照資料而分析的 9 個 TeX 引擎，當然有許多共通命令，但每個 TeX 引擎也都有其開發者新增的專屬原始命令，用來支援該 TeX「版本」特有的功能。

TeX 引擎的原始命令內建於可執行的 TeX 軟體中：原始命令不是使用者組合出來的巨集，而是用來控制各引擎排版行為的基本、不可再分／原子性的指令。因此，要為任何 TeX 引擎建立一份權威的原始命令清單，最可靠的方法就是檢視實際產生可執行 TeX 程式（經編譯）的原始碼，並從原始碼中擷取所定義的原始命令清單。聽起來應該很簡單，對吧？然而，由於 TeX 40 年的發展歷史，探索／檢視 TeX 引擎的原始碼檔（LuaTeX 除外）並不是特別直接。造成這些複雜性的原因，在於 Knuth 用來撰寫最初 TeX 原始碼的工具、程式語言（Pascal）與方法學（literate programming，文學式程式設計）——而所有其他引擎最終都源自這份原始碼。

我們要特別指出 LuaTeX 是個例外，因為其核心引擎程式碼已改寫為 C，以移除 Pascal 及下文所詳述的其他舊式複雜性（Web2C）；因此，雖然 LuaTeX 的原始碼相當龐大，但其「封裝」與散布方式相較於其他 TeX 引擎要容易理解得多。基於這一點，並根據從原始碼建置它們所使用的工作流程／流程，將 TeX 引擎分成兩類會更方便：

1. LuaTeX：自訂（較現代）建置流程
2. 其他所有引擎：舊式（Web2C）建置流程

## 舊式程式碼的脈絡：為何建置（大多數）TeX 引擎很複雜

如我們下文將探討的，Knuth 將其最初的 TeX 原始碼以一個名為 `tex.web` 的單一巨型檔案釋出，而 Knuth 每隔 7 年仍會更新一次，以修正任何尚未解決的錯誤——從不加入新功能，純粹只是修錯。

TeX 原始碼的副檔名（`.web`）可能不太熟悉，你或許會想 Knuth 用什麼語言寫 TeX？答案是 Pascal，但 `.web` 這個副檔名還需要多做一些說明。Knuth 開發了一種程式設計方法，稱為 [literate programming](https://en.wikipedia.org/wiki/Literate_programming) ，在這種方法中，程式的原始碼與文件說明會合併在一起，並以一個副檔名為 `.web`的單一複合檔案（程式碼加文件）形式發布：這種檔案類型稱為 WEB 檔。我們在下文會更詳細地解釋 WEB 檔。

### 建立新的 TeX 引擎：Knuth 的限制條件

雖然 Knuth 早已將其 TeX 原始碼（`tex.web`）無償提供給所有人，但他也在其絕對權利範圍內，提出一項關鍵限制：他的（`tex.web`）原始碼不得直接編輯／修改後，再以「TeX」這個程式名稱重新散布。在原始碼中他寫道：

```
% 這個程式的著作權 (C) 1982 屬於 D. E. Knuth；所有權利保留。
% 僅在以下情況下才授權複製此檔案：(1) 你是 D. E. Knuth，或是
% (2) 你對你的副本完全不做任何修改。（WEB 系統提供
% 透過輔助檔進行變更；主檔應保持完整。）
```

以及：

```
如果此程式被更改，所得系統不應稱為
`\TeX'；官方名稱 `\TeX' 本身保留給
彼此完全相容的軟體系統。
有一套稱為 ``\\.{TRIP} 測試'' 的特殊測試套件可供
協助判定某個特定實作是否值得被
稱為 `\TeX' [參見史丹佛大學計算機科學報告 CS1027，
1984 年 11 月]。
```

總之：不要透過編輯並散布修改過的主 TeX 原始碼版本來進行變更，並繼續把它稱作 `tex.web`。如果你確實想做變更，例如新增原始命令等等，那麼你必須透過「以輔助檔進行變更」來套用這些變更，並為你的「TeX 的衍生版本」取一個能將它與「TeX」區分開來的名稱，而在排版形式（$$\mathrm\TeX$$）中，這是美國數學學會的商標。

### 舊式傳承

雖然曾有人嘗試以現代程式語言與方法學徹底重寫 TeX——例如兩個以 Java 為基礎的倡議 [新排版系統](https://en.wikipedia.org/wiki/New_Typesetting_System) 和 [εχTEX](http://www.extex.org/) 以及其他例如 [一個以 Clojure 為基礎的](https://www.infoq.com/news/2015/01/implementing-tex-in-clojure)，但沒有一個完全成功。關於為進一步發展 TeX 而設計的專案與倡議，其歷史是個有趣的主題，讀者或許想要 [造訪英國 TeX FAQ](https://texfaq.org/FAQ-enginedev) 以取得更多資訊。

那些非 LuaTeX 的倡議中，成功的也包括 e-TeX、pdfTeX、XeTeX 及其他引擎，它們都是 *直接建立在* Knuth 原始程式碼之上：取用他的原始碼並「套用變更」，以衍生出具備額外能力的新引擎——例如新增原始命令、產生 PDF 輸出、支援 UTF-8 文字輸入等等。雖然這條路帶來了顯著成功，但也意味著這些衍生引擎承襲了 Knuth 40 年前建立的舊式程式碼與開發技術。

這裡的關鍵重點是：除了 LuaTeX 之外，大多數源自 Knuth 原始碼的 TeX 引擎，都是先取一個單一巨型檔案（通常 `tex.web`）並套用變更，進而產生另一個同樣單一巨型檔案，作為該新引擎的核心原始碼。進階讀者或許想直接跳到關於 [pdfTeX 與 XeTeX 的說明註記](#aside-xetex-and-pdftex).

### 一些額外的 TeX 歷史／背景

TeX 誕生的時刻是 [Knuth 的日記記錄為 1977 年 3 月 30 日](/latex/zh-tw/shen-ru-wen-zhang/55-what-s-in-a-name-a-guide-to-the-many-flavours-of-tex.md#the-genesis-of-tex-a-brief-history)，距今已超過 40 年。TeX 內部是一個極為複雜的程式，Knuth 在其原始碼上費盡心力，將其 [以極其詳盡的方式記錄下來](https://www.amazon.co.uk/Computers-Typesetting-TeX-Program-TEX/dp/0201134373)。為了做到這點，Knuth 發展出一種程式設計風格，稱為 [literate programming](https://en.wikipedia.org/wiki/Literate_programming) 在這種方法中，程式的原始碼與文件說明會合併在一起，並以副檔名為 `.web` 的複合檔案形式發布（稱為 WEB 檔）。Knuth 選擇 Pascal 作為撰寫 TeX 軟體的程式語言，而且也不令人意外地，使用 TeX 排版語言來撰寫最終文件。因此，Knuth 的 TeX 主原始碼以單一巨型檔案的形式發布，名為 `tex.web`：Pascal 原始碼與用於文件的 TeX 排版程式碼混合而成。

若程式是以 Knuth 的 literate programming 風格／方法學撰寫（TeX、MetaFont、BibTeX 及其他程式皆是如此），你就需要先預處理 WEB 檔，以擷取文件或原始碼。若要存取程式的文件，你可以用一個名為 `tex.web`的工具來處理 WEB 檔（例如， [WEAVE](http://tug.org/texinfohtml/web2c.html#weave-invocation) ，它會將文件產生為一個 `.tex` 可排版的 [TANGLE](http://tug.org/texinfohtml/web2c.html#tangle-invocation) 檔案，輸出一個副檔名為 `.p` 的檔案，其中包含 Pascal 原始碼。

撰寫本文時（2019 年初），Knuth 最新版的 TeX 是 3.14159265，日期為 2014 年 1 月。再次提醒，Knuth 的 TeX 原始碼只包含在一個檔案中，裡面約有 25,000 行 TeX/Pascal 程式碼！

### 從 Pascal 到 C

自 TeX 誕生以來的 40 多年間，Pascal 已逐漸式微，如今幾乎沒有人還會考慮從原始的 Pascal 原始碼來建置 TeX。為了避開 Knuth 使用 Pascal 的做法，有一個稱為 [Web2C](http://tug.org/texinfohtml/web2c.html) 的工作流程被設計出來（約在 1987 年），其做法是將 TeX 的 Pascal 原始碼機械化地（也就是透過軟體）轉換成等效的 C 程式碼，接著再用這些 C 程式碼來編譯 TeX 並建置可執行程式。它運作得很好，但唯一的缺點是，機械產生的 C 原始碼並非供人隨意閱讀之用：它 *極其* 冗長且幾乎難以理解，因為它是寫給編譯器看的，不是給人看的——這裡有一張截圖，顯示由 TeX 的 Pascal 原始碼產生的 C 程式碼片段：

![](/files/bdcd28f4ebdf4a6601c025b11d781d56c52592bf)

### 另一個 Knuth 用語：WEB 變更檔

如上所述，要在 Knuth 的原始碼上進行建構，你必須「套用變更」，或者用 Knuth 的話說，進行「透過輔助檔的變更」：但這實際上是什麼意思？接下來介紹 *變更檔機制*.

### 變更檔：建立新 TeX 引擎的機制

想以某種方式擴充 Knuth 的 TeX，也就是在 Knuth 的原始工作上建構的開發者，通常希望建立一個全新的 TeX「版本」，或者提供一個 *擴充* ，可加入任何 TeX 引擎。擴充的例子包括 [SyncTeX](https://github.com/jlaurens/synctex) 和 [EncTeX](https://ctan.org/pkg/enctex?lang=en)——例如，SyncTeX 是一個非常實用的擴充，如今已包含在所有 TeX 引擎中。EncTeX 的需求在很大程度上已被支援 Unicode 的 TeX 引擎演進所取代——但要注意，EncTeX 已內建於 pdfTeX。

無論目的是產生 TeX 的新「版本」（也就是 Knuth 原始 TeX 的衍生版），或是建立一個擴充，其開發者都會從 Knuth 的原始碼開始，套用必要的修改來建立新的 TeX 引擎（或附加擴充）。然而，如上所述，任何希望修改 TeX 行為的人都必須透過「以輔助檔進行變更」來做，因為這些變更／修改不得透過 *直接* 編輯 Knuth 的原始碼來套用：開發者必須使用所謂的 WEB *變更檔機制*。用來修改 Knuth 的 TeX 的程式碼是以 WEB「語言」撰寫，並儲存到一個或多個程式碼檔（稱為 *變更檔*），之後再與 Knuth 原本未動過的主原始碼 *合併* 。這個合併過程會建立一個新的複合 WEB 檔，現在其中包含了新／修改後的 TeX 軟體的 *核心* 原始碼。 *變更檔* 通常副檔名為 `.ch` ，但實際上可以是開發者想要的任何副檔名。

#### 如何使用／套用變更檔？

如今，要套用變更檔並修改一個「主」WEB 檔，最簡單的方法是使用一個名為 [TIE](https://ctan.org/pkg/tie)的工具程式。例如，假設你想透過新增幾個新的原始命令來修改 Knuth 的 TeX，或者想改變既有（標準）TeX 原始命令的行為。你會用 WEB 系統的文學式程式設計（literate programming）以 Pascal 撰寫你的程式碼，並將其儲存為例如 `myprim.ch`的檔案。下一步是將你的程式碼（在 `myprim.ch`中）與 Knuth 的主原始檔 `tex.web` 合併，並產生一個新的複合 WEB 檔，代表我們姑且稱之為 `mytex.web`。要做到這點，你只需像這樣執行 TIE 程式：

```
tie -m mytex.web tex.web myprim.ch
```

假設合併成功，這會產生一個新的 WEB 檔， `mytex.web`mytex.web， `tex.web` 同時讓 Knuth 的主原始檔完全不受影響，這是符合要求的。

現在假設其他人喜歡你所做的變更，並想在你的工作之上再加入他們的變更。與其散布你修改過的 TeX 版本（`mytex.web`） `myprim.ch`，你決定只發布／分享變更檔 `moreprim.ch` 。任何希望在你的工作基礎上繼續擴充的人，現在都可以建立並分享他們自己的變更檔，例如 *兩個* 變更檔與 Knuth 原始版本合併，以產生另一個 TeX 程式，例如 `newmytex.web`:

```
tie -m newmytex.web tex.web myprim.ch moreprim.ch
```

### 實際 TeX 系統：多個變更檔

上面對 TIE 的描述，其實非常接近許多 TeX 引擎在實務上的建置方式：它們從 Knuth 的 `tex.web` 開始，然後加入一連串變更檔，生成該引擎的 WEB 原始檔。每個 TeX 引擎都需要自己特定的一組變更檔，而且這些檔案必須以嚴格的順序套用／處理（合併）：順序錯了就會導致合併失敗，因為序列中的每個變更檔都依賴於鏈中較早出現的變更檔所引入的改動。

以下是一段 TIE 範例執行，將多個變更檔套用到 Knuth 的 `tex.web` 以產生 `ktex.web`——這個複合 WEB 檔對 Knuth 的 TeX 做了修改，使它準備好（適合）透過 Web2C 流程轉換為 C。另請注意以下幾點：

* `tex.ch` 是一個非常大的變更檔，其中除了其他事情外，還修改 TeX 以使用 Kpathsea；
* SyncTeX 擴充是透過多個變更檔加入的。

```
tie -m ktex.web tex.web tex.ch enctex.ch synctex-def.ch0 synctex-mem.ch0 synctex-mem.ch2 synctex-rec.ch0 synctex-rec.ch1 synctex-rec.ch2 tex-binpool.ch
這是 TIE，CWEB 版本 2.4。
版權所有 (c) 1989,1992 THD/ITI。保留所有權利。
(tex.web)
(tex.ch)
(enctex.ch)
(synctex-def.ch0)
(synctex-mem.ch0)
(synctex-mem.ch2)
(synctex-rec.ch0)
(synctex-rec.ch1)
(synctex-rec.ch2)
(tex-binpool.ch)
....500....1000....1500....2000....2500....3000....3500....4000....4500
....5000....5500....6000....6500....7000....7500....8000....8500....9000
....9500....10000....10500....11000....11500....12000....12500....13000
....13500....14000....14500....15000....15500....16000....16500....17000
....17500....18000....18500....19000....19500....20000....20500....21000
....21500....22000....22500....23000....23500....24000....24500....
(未發現任何錯誤。)
```

#### 補充：XeTeX 與 pdfTeX

為求完整，我們應該指出，pdfTeX 與 XeTeX 的建置流程其實並不是從 Knuth 的 `tex.web`開始；它們改為從名為 `pdftex.web` 和 `xetex.web` 的檔案開始：大概是因為改動相當廣泛，與其散布／發布已經包含對 Knuth 原始碼做出非常重大修改的 WEB 檔，這樣做更合理。

### 範例：e-upTeX

日本的 TeX 社群已開發出多種 TeX 引擎，旨在處理日文排版的複雜性：

* **pTeX**：將 Knuth 的 TeX 引擎擴充以支援日文排版；
* **e-pTeX**：e-TeX 與 pTeX 的組合（再加上 pdfTeX 引入的一些原始命令）；
* **upTeX**：具備 Unicode 支援的 pTeX 版本，並加入更佳處理 CJK（中文、日文與韓文）的擴充；
* **e-upTeX**：e-TeX 與 upTeX 的組合（合併）。

#### 產生 e-upTeX 的複合原始檔

要建立 e-upTeX（含 SyncTeX）的複合 WEB 原始檔，你要從 Knuth 的 `tex.web` 開始，但需要依以下順序套用 **26** 個別變更檔，才能得到一個可從中擷取原始命令清單的單一複合檔：

```
etex.ch, tex.ch0, tex.ch, tex.ech, etex.ch0,
ptex-base.ch, uptex-m.ch, euptex.ch0, eptex.ech,
etex.ch1, euptex.ch1, synctex-def.ch0, synctex-ep-mem.ch0,
synctex-mem.ch0, synctex-e-mem.ch0, synctex-ep-mem.ch1,
synctex-p-rec.ch0, synctex-rec.ch0, synctex-rec.ch1,
synctex-e-rec.ch0, synctex-p-rec.ch1, fam256.ch,
pdfstrcmp-eup-pre.ch, pdfutils.ch, pdfstrcmp-eup-post.ch,
tex-binpool.ch
```

#### 套用變更檔：哪些檔案，以及依什麼順序？

如前所述，嚴格依正確順序套用／處理變更檔至關重要——但你要如何找出需要哪些檔案，以及應該依何順序處理？幸運的是，這些關鍵資訊記錄在 TeX Live 發行版所包含的檔案中，而對 TeX Live 原始碼的探究揭示了支配各 TeX 引擎建置需求的規則。遵循那些規則，Overleaf 得以重建每個 TeX 引擎的複合 WEB 原始碼檔，並擷取原始命令清單以進一步處理資料。

## 最後：如何擷取原始命令清單？

一旦複合 WEB 檔建構完成，使用正規表示式擷取原始命令清單的工作就很直接，因為所有原始命令都是透過一個名為 `primitive(...)`的單一 Pascal 函式定義（「註冊」）的。以下是取自 Knuth `tex.web` 原始碼的一些真實範例：

```
primitive("lineskip",assign_glue,glue_base+line_skip_code)
primitive("baselineskip",assign_glue,glue_base+baseline_skip_code)
primitive("parskip",assign_glue,glue_base+par_skip_code)
primitive("abovedisplayskip",assign_glue,glue_base+above_display_skip_code)
primitive("belowdisplayskip",assign_glue,glue_base+below_display_skip_code)
primitive("abovedisplayshortskip",assign_glue,glue_base+above_display_short_skip_code)
...
...
```

如你所見， `primitive(...)` 函式非常適合用正規表示式做文字處理：被註冊的原始命令名稱會以引號括起來（`"..."`）同時還帶有用來分類每個原始命令行為的額外資料（這裡我們不會詳細討論）。在擷取每個引擎的原始命令清單後，這些資料再由一些 Lua 腳本處理，以產生包含表格化結果的 HTML。

### 回到 LuaTeX

我們已指出 LuaTeX 不使用與其他 8 個 TeX 引擎完全相同的建置流程。在簡要探討過 Web2C 流程、Pascal 轉 C 的轉換以及變更檔機制之後，我們現在可以說明 LuaTeX 的差異之處：LuaTeX 的開發者決定捨棄將 Pascal 轉成 C 的繁瑣流程——如《 [LuaTeX 參考手冊](http://www.pragma-ade.com/general/manuals/luatex):

> 》所述，

…編譯框架是 web2c，我們仍然使用它，但不再進行 Pascal 轉 C 這一步。

嚴格來說，我們也應該指出，LuaTeX 的某些原始碼檔使用了一種 Knuth 文學式程式設計方法的變體，稱為 [CWEB](https://en.wikipedia.org/wiki/CWEB)，它是以 C 而不是 Pascal 為基礎。

### 不只是 WEB 檔：還需要其他原始碼

在為任何 TeX 引擎（LuaTeX 除外）生成複合 WEB 原始檔後，還必須擷取 Pascal 原始碼並轉換為 C 程式碼，但這並不是完整的解決方案。除了由 WEB 原始檔（Pascal⮕C）生成的 C 程式碼之外，大多數 TeX 引擎還仰賴（需要）若干額外的周邊原始碼檔（函式庫），通常以 C 撰寫——例如 [Kpathsea](https://www.tug.org/kpathsea/)。周邊原始檔（函式庫）實作的是那些不需要、或無法以 WEB（Pascal）撰寫的功能。凡是在 TeX 的 WEB「語言」中撰寫的內容，都必須使用 Pascal 語言，之後再擷取並轉換為機器產生的 C：如果你根本不需要這麼做，那一開始何不直接用 C 或 C++ 來寫。


---

# 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/zh-tw/geng-duo-zhu-ti/18-how-overleaf-created-the-tex-primitive-reference-data.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.
