> 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/ru/podrobnye-stati/53-what-is-a-tex-token.md).

# Что такое «токен TeX»?

## Мотивация для серии о токенах TeX и связанных понятиях

В этой статье обсуждаются мотивация и методология, использованные для создания серии статей о токенах TeX и связанных понятиях [Новая серия статей: токены TeX и связанные концепции — но зачем (и как)?](https://www.overleaf.com/blog/521-a-new-series-of-articles-tex-tokens-and-related-concepts-but-why-and-how) Как отмечалось в той статье, на протяжении всей этой серии мы основываем наши обсуждения и объяснения на выводах, полученных благодаря собственной сборке исходной программы TeX Кнута, используя её для создания серии статей, цель которой — дать простые описания и понятные объяснения ключевых концепций TeX.

## Введение: какова наша цель?

В этой статье мы точно узнаем, что такое токен TeX, проследив путь обработки от символов во входном файле до фактического создания токенов TeX. На практике это довольно сложно, поэтому мы свели процесс к его основным элементам, стараясь сделать его простым для восприятия и понимания, сохраняя при этом техническую точность.

Сначала мы познакомимся с несколькими важными внутренними понятиями TeX: *примитивов*, *коды команд* и *модификаторы команд*. Затем мы воспользуемся очень простым примером макроса, чтобы увидеть, как именно TeX обрабатывает команду `\def` и получающийся токен, который TeX создаёт для представления этой команды.

В завершение мы кратко рассмотрим, как TeX создаёт токены для представления символов и как категория символа `\catcode` действительно навсегда привязывается к токену символа — это часто упоминается в книгах о TeX, но здесь мы увидим, как именно это достигается.

Следующая схема показывает путь, который мы кратко изложим — от входного текста к токенам TeX:

![Путь от входных данных TeX к токену TeX.](/files/956ea3260fb19ae08e235f8d93cfb5a4fc3f1acd)

## Но сначала: примитивы и коды команд

Каждый движок TeX (TeX Кнута, pdfTeX, XeTeX, LuaTeX) понимает ряд встроенных команд: так называемые *примитивов*—фундаментальные строительные команды, лежащие в основе программируемости TeX. Они называются «примитивами» потому, что, в отличие от пользовательских макросов, не строятся из других команд и не могут быть далее сведены к более простым инструкциям. В TeX Кнута имеется примерно 320 примитивов, хотя следует отметить, что другие движки TeX, такие как pdfTeX, XeTeX и LuaTeX, добавили в исходную программу Кнута новые команды и содержат примитивы, которых нет в программном обеспечении TeX Кнута.

Внутри TeX присваивает числовой *кодом команды* код всем командам — будь то пользовательские макросы или встроенные примитивы. Эти коды команд недоступны пользователю TeX; они просто являются частью внутренних механизмов обработки TeX, но полезно знать о них для дальнейшего обсуждения токенов TeX.

Группы команд с родственной функциональностью имеют один и тот же код команды. Например, `\def`, `\gdef`, `\edef` и `\xdef` все эти примитивы используются для определения макросов и имеют код команды 97 (в TeX Кнута). Очевидно, эти 4 команды определения макросов создают макросы немного разными способами; следовательно, при обработке TeX нужен способ различать их.

Сам по себе код команды (например, 97) не позволяет понять, о какой именно команде создания макроса идёт речь; поэтому, как и следовало ожидать, каждой команде TeX назначается дополнительная информация, называемая её *модификатор команды* (см. примеры ниже).

### Модификаторы команд: два типа

Модификаторы команд делятся на две категории, которые мы будем называть «тип 1» и «тип 2» — TeX не использует эту терминологию, просто здесь так удобнее:

* **Тип 1**: Простые целые значения, которые TeX при необходимости может использовать, чтобы различать команды с одним и тем же кодом команды.
* **Тип 2**: Целое значение, представляющее собой числовую позицию в памяти TeX и указывающее TeX, куда нужно обратиться, чтобы найти информацию для этой команды. Например, это относится к пользовательским командам (макросам), где модификатор команды сообщает TeX, где в памяти хранится определение макроса.

#### Модификаторы команд типа 1 (пример)

Как отмечалось, в TeX Кнута четыре примитивные команды для определения макросов: `\def`, `\gdef`, `\edef`, `\xdef` все имеют код команды 97: они различаются своими модификаторами команд, которые перечислены в следующей таблице:

| Команда | <p>Код<br>команды</p> | <p>Модификатор<br>команды</p> |
| ------- | --------------------- | ----------------------------- |
| `\def`  | 97                    | 0                             |
| `\gdef` | 97                    | 1                             |
| `\edef` | 97                    | 2                             |
| `\xdef` | 97                    | 3                             |

В качестве второго примера Кнут решил реализовать команды `\openout`, `\write`, `\closeout`, `\special`, `\immediate` и `\setlanguage` как «расширения» TeX, лишь чтобы показать, как можно добавлять новые примитивы в TeX. В этом случае эти команды на самом деле не имеют «похожей функциональности», за исключением того, что Кнут решил объединить их для объяснения расширения TeX. Эти 6 команд классифицируются как «расширения» и объединяются под значением кода команды 59, но каждая имеет соответствующий модификатор команды, отличающий её от остальных:

| Команда        | <p>Код<br>команды</p> | <p>Модификатор<br>команды</p> |
| -------------- | --------------------- | ----------------------------- |
| `\openout`     | 59                    | 0                             |
| `\write`       | 59                    | 1                             |
| `\closeout`    | 59                    | 2                             |
| `\special`     | 59                    | 3                             |
| `\immediate`   | 59                    | 4                             |
| `\setlanguage` | 59                    | 5                             |

#### Модификаторы команд типа 2 (краткое объяснение)

Хотя все модификаторы команд являются целыми числами, модификаторы типа 2 требуют немного более подробного объяснения. В TeX эти модификаторы команд называются «указателями», потому что они указывают на позицию в памяти, где TeX может найти дополнительную информацию для этой команды. Это может звучать немного расплывчато, но способы, которыми TeX использует эти указатели для поиска информации, весьма разнообразны, и более подробное объяснение отвлекло бы от основной цели этой статьи. Один пример может помочь: макросы. Когда определяется команда макроса, TeX нужно где-то в памяти сохранить текст замены. Как мы увидим ниже, у пользовательских макросов коды команд находятся между 111 и 114, а модификатор команды — это указатель в памяти, сообщающий TeX, где хранится текст замены (определение макроса).

### Коды команд: расширяемые и нерасширяемые

В исходном коде TeX Кнута коды команд варьируются от 0 до 120 — отметим, что некоторые коды в этом диапазоне предназначены исключительно для специального внутреннего использования и не назначаются командам, доступным пользователю. Стоит отметить, что другие движки TeX, такие как pdfTeX, XeTeX и LuaTeX, добавили к исходному набору Кнута новые команды и содержат больше примитивов и соответствующих кодов команд; однако изложенные здесь принципы являются основополагающими для всех основанных на TeX движков, происходящих от исходного кода Кнута.

Набор кодов команд делится на два основных множества:

* *нерасширяемые команды*: имеют коды команд меньше или равные 100;
* *расширяемые команды*: имеют коды команд больше 100, максимум до 120. Диапазон от 101 до 120 включает пользовательские макросы, а также команды вроде `\csname`, `\expandafter` и `\the`.

Нерасширяемые команды обычно выполняют присваивание значения внутреннему параметру или непосредственно создают материал, который можно набрать. Расширяемые команды обычно «вставляют» поток токенов в текущую обработку TeX или изменяют порядок обработки токенов.

Как отмечалось выше, всем макросам (пользовательским командам) присваиваются коды команд от 111 до 114: разные значения отражают, был ли макрос определён как `\long`, `\outer`, обеими или ни одной из них. Вот пример:

| Тип макроса       | Пример                         | Комментарий               |
| ----------------- | ------------------------------ | ------------------------- |
| Не long, не outer | `\def\ohyeah{....}`            | `\ohyeah` код команды=111 |
| Длинный, не outer | `\long\def\ohyeah{....}`       | `\ohyeah` код команды=112 |
| Не long, outer    | `\outer\def\ohyeah{....}`      | `\ohyeah` код команды=113 |
| Длинный outer     | `\long\outer\def\ohyeah{....}` | `\ohyeah` код команды=114 |

Напомним о модификаторах команд: когда макрос определён, TeX сохраняет его определение в некотором месте памяти: это место (указатель) станет модификатором команды макроса, который будет храниться с кодом команды от 111 до 114 в зависимости от того, как он был определён. Само имя, присвоенное пользовательскому макросу, на самом деле не имеет значения: после обработки входных данных всем им будет присвоен код команды в диапазоне от 111 до 114, и в конечном итоге все команды, которые TeX читает из вашего ввода, будь то примитивы или пользовательские макросы, преобразуются в числовое представление, называемое *токен*.

## Путь от входного текста к токенам TeX

В этом разделе мы воспользуемся очень простым примером макроса, чтобы увидеть, как именно TeX обрабатывает команду `\def` для создания токена, который представляет `\def` команду. Детальная обработка TeX может быть чрезвычайно сложной, поэтому мы не используем параметры макросов или разделители, так как это добавило бы сложности и отвлекло бы нас от нашего пути.

Предположим, что ваш входной файл TeX содержит следующую строку:

```latex
\def\ohyeah{Overleaf is cool!}
```

Когда TeX начинает обрабатывать эту входную строку, он проверяет `\catcode` категорию каждого символа и видит, что первым символом является `\` (первый символ `\def`). Он определяет (ищет это во внутренней таблице), что `\` имеет `\catcode` 0, что означает, что он обозначает начало *управляющей последовательности*. Разумеется, вы можете переопределить любой символ так, чтобы он имел `\catcode` 0, но мы будем считать, что используются стандартные определения plain TeX или LaTeX.

Строго говоря, термин *управляющей последовательности* имеет две подкатегории: *управляющее слово* и *управляющий символ*:

* *управляющее слово*: последовательность символов с `\catcode` буквенной категорией (11);
* *управляющий символ*: одиночный символ, у которого `\catcode` равно *не* буквенная категория (11).

На этом этапе `\` символ выполнил свою задачу и теперь больше не нужен. Обнаружив escape-символ, TeX начинает читать все последующие символы во входных данных, чтобы обнаружить управляющее слово или управляющий символ.

После начального `\`, TeX сразу же обнаруживает `d`: символ, у которого `\catcode` равна 11, что сообщает TeX, что он нашёл первую букву *управляющее слово*. Он продолжает сканировать последующие символы, пока наконец не обнаружит символ, который *не* обязательно `\catcode` буквенную категорию (11). Все последующие символы (после начального `\`) с `\catcode` 11 (буква) считаются образующими имя управляющего слова: то есть имя команды — возможно, макроса или примитива, но TeX пока не знает, к какому типу команды она относится. На этом этапе это просто строка символов.

Итак, в нашем примере TeX спокойно просматривает текст, проверяя каждый символ, пока не достигает начального `\` для `\ohyeah` который также имеет `\catcode` 0. TeX понимает, что зашёл слишком далеко, и вежливо возвращает этот `\` обратно в текстовый поток, чтобы он стал следующим символом, который будет увиден при дальнейшем сканировании текста. К этому моменту TeX определил строку (`def`) которую он знает как текст управляющего слова, состоящего из трёх символов, каждый из которых имеет `\catcode` 11 (`d`, `e` и `f`). Теперь TeX нужно выяснить, что `def` означает: что ему делать? Как вы, возможно, догадались, TeX нужно найти код команды и идентификатор команды для `def` чтобы он мог понять, что делать с этой командой.

## Создаём хеш

Обнаружив управляющее слово (`def`), первое, что делает TeX, — «преобразует» строку символов (`def` в нашем примере) в целое число с помощью так называемой хеш-функции. Нам не нужно вдаваться в детали; достаточно общего описания. По сути, TeX смотрит на каждый символ в только что обнаруженном управляющем слове и использует значение ASCII-кода (или значение Unicode для XeTeX/LuaTeX) каждого символа для вычисления числа, называемого хеш-значением: это просто целое число.

В рамках процесса вычисления хеша TeX также проверит, известно ли ему уже строковое представление только что обнаруженного управляющего слова. Читаемый человеком текст всех команд, будь то примитивы или пользовательские макросы, хранится во внутренней области хранения, называемой *пулом строк*. TeX должен делать это, потому что ему может понадобиться вывести читаемое человеком имя команды — например, когда TeX нужно сообщить об ошибке и указать имя проблемной команды. Например, наш макрос `\def\ohyeah{Overleaf is cool!}` определяет новую команду под названием `\ohyeah` и TeX (на более позднем этапе) должен будет не только вычислить хеш-значение для `ohyeah` (*без* начального `\` символа), но и сохранить строковую (читаемую человеком) форму на случай, если она понадобится для сообщения об ошибках (или других задач).

Если вам нужны более подробные сведения о процессах обработки строк в TeX, я писал об этом в своём [личном блоге](http://www.readytext.co.uk/?p=3590).

В итоге строка символов, представляющая команду `def` преобразуется в числовое значение 1218 (это фактическое значение, вычисленное TeX). На этом этапе отдельные символы `d`, `e` и `f` больше не являются частью основной истории — они были прочитаны из ввода и выполнили свою задачу: теперь речь идёт только о целых числах и *токены*—скоро мы увидим, что такое токен на самом деле! Внутри TeX называет эти хеш-значения *текущей управляющей последовательностью* но в исходном коде этот термин сокращён до переменной под названием `curcs`. Исходный код TeX полон очень коротких, часто довольно загадочных, имён переменных.

Но что же TeX *на самом деле делает* с этим только что полученным целым значением 1218? Как TeX узнаёт, что исходная строка `def`, теперь представленная целым числом 1218, на самом деле относится к инструкции определения макроса? Ответ в том, что у TeX есть своего рода внутренний «шкаф с папками», где он хранит текущее значение и смысл каждой известной ему команды — будь то пользовательский макрос или встроенный примитив. Причина, по которой TeX потрудился преобразовать `def` в хеш-значение 1218 (теперь сохранённое в переменной под названием `curcs`) заключается в том, чтобы использовать его для поиска *значение* для `def`. TeX, конечно, повторит это вычисление хеша для всех управляющих слов, которые он обнаружит во входных данных, — хотя, разумеется, разные управляющие слова дают разные целые значения на выходе хеш-функции: в этом и состоит вся идея.

Внутренний «шкаф с папками» TeX называется *таблицей эквивалентов* и является темой следующего раздела.

### Обращение к таблице эквивалентов

Чтобы подытожить, давайте посмотрим, что мы уже узнали:

* `\` обозначает начало управляющей последовательности (либо *управляющий символ* или *управляющее слово*).
* Если первый символ после `\` имеет `\catcode` 11 (буква), то это начало *управляющее слово*.
* Для *управляющих слов* TeX сканирует входные данные, проверяя все последующие символы, у которых есть `\catcode` 11, и прекращает сканирование, как только находит первый символ, который не *не* имеет `\catcode` категорию 11.
* Строка входных символов (следующая за `\`), которые имеют `\catcode` 11, считается *управляющее слово* введённой пользователем: командой, которая просит TeX «что-то сделать».
* Чтобы начать процесс «что-то делать», TeX преобразует строку символов в управляющем слове в целое число. Для этого он использует так называемую хеш-функцию, которая выдаёт целое число.
* Это целое число (вычисленное хеш-значение) называется *текущей управляющей последовательностью*, но TeX даёт ему более короткое название `curcs`.
* В нашем примере управляющее слово `def` преобразуется в значение 1218, которое сохраняется в переменной под названием `curcs`: то есть `curcs=1218`.

Теперь TeX нужно выяснить, что только что обнаруженное *текущей управляющей последовательностью* на самом деле означает — что TeX с ним делает?

#### Примечание о группировке: необходимость сохранять и восстанавливать информацию

Здесь мы немного отвлечёмся, чтобы напомнить себе, что TeX способен сохранять и восстанавливать информацию: то есть у него есть некая встроенная «память».

Любой, кто писал хотя бы самый простой макрос, должен быть знаком с механизмом группировки TeX — например, с использованием `\def` для создания макросов внутри группы. Если вы не используете `\global` префикс `\def`—созданным макросам, определённым внутри группы, значение или смысл этого макроса сохраняется только внутри этой группы (и вложенных в неё): его определение теряется, когда группа заканчивается. Например, если вы определите простой макрос внутри группы, вот так:

```latex
{\def\foo{Hello}}
```

и попробуете использовать `\foo` вне группы

```latex
{\def\foo{Hello}}% \foo определён внутри группы (заметьте: без использования \global)
\foo %<--- больше не определён, теперь неопределён
```

то мы получим хорошо знакомую ошибку: `Неопределённая управляющая последовательность`. `\foo` имеет значение только внутри той группы (и её подгрупп), в которой он был определён. Кроме того, когда вы переопределяете макрос внутри группы, новое значение может быть утрачено по завершении группы, и восстанавливается прежний смысл (существовавший вне группы).

```latex
\def\foo{Goodbye}
\foo\par% Выводит Goodbye
{\def\foo{Hello}% Переопределён внутри группы:
{Внутри группы 2-го уровня: \foo\par}}% Используется внутри группы 2-го уровня: \foo выводит Hello
Вне группы старое значение восстановлено: \foo\par% Выводит Goodbye
```

Цель этих простых примеров — показать, что у TeX есть некий «механизм хранения» или «память», который сохраняет и восстанавливает «значение» команд — и, конечно, он действительно есть. Мы намекнули на это в предыдущем разделе: этот «механизм хранения» или «шкаф с папками» представляет собой большую внутреннюю таблицу под названием *таблицей эквивалентов*. Именно там TeX хранит текущий смысл или значения всех известных ему команд — встроенных примитивов и пользовательских макросов.

### Таблица эквивалентов: по аналогии

Чтобы объяснить таблицу эквивалентов, мы воспользуемся аналогией. Мы продолжим использовать представление о шкафе с тысячами маленьких ящиков, каждый из которых помечен уникальным целым числом. На этом этапе обработки TeX, по сути, говорит:

«Хорошо, у меня есть это целое значение 1218, которое я только что вычислил и сохранил в переменной под названием `curcs`. Теперь мне нужно выяснить, что оно означает: для этого я пойду и посмотрю в ящик номер 1218 в своём шкафе с папками, чтобы увидеть, что там написано».

TeX использует 1218, чтобы найти нужный ящик, и там находит небольшую записку, содержащую три элемента информации, чьи названия совпадают с теми, что используются в исходном коде TeX:

* **`eq_level:`** уровень группировки, на котором была определена эта запись (уровень 1 = определена глобально). Выше мы видели действие группировки: именно здесь, в таблице эквивалентов, хранится информация об уровне группировки;
* **`eq_type:`** код команды для этой записи;
* **`equiv:`** текущее «значение» этой записи — это может быть простое целое число, такое как упомянутый выше модификатор команды, или указатель на область памяти; например, адрес в памяти, где хранится набор токенов, представляющих определение макроса.

Итак, наше хеш-значение 1218 (сохранённое в переменной `curcs`curcs *ключ* для доступа к ящику, содержащему текущий смысл и значение команды, которую мы изначально ввели в виде строки букв `\def`.

В исходном коде программы TeX `eq_type` для любой команды хранится в переменной под названием `curcmd` а значение `equiv` хранится в переменной под названием `curchr`.

### Что говорит таблица эквивалентов для def?

Как отмечалось, хеш-значение, вычисленное для любой команды, сохраняется в переменной под названием `curcs`; следовательно, для `def` у нас есть `curcs=1218`. Заглянув в позицию 1218 в таблице эквивалентов, TeX найдёт следующую информацию:

* `curcmd`=97. Это код команды для `\def`;
* `curchr`=0. Это модификатор команды для `\def`.

`\def` является примитивной (встроенной) командой TeX, и если она где-то не была переопределена, то третья и последняя часть информации должна быть `eq_level=1` что указывает на то, что значение `\def` определено глобально и не ограничено каким-либо более низким уровнем группировки. Внутри TeX значение `eq_level` прикреплённое к команде, играет чрезвычайно важную роль в механизме группировки TeX, но мы не будем рассматривать это далее.

Следующая схема подводит итог рассмотренному нами объяснению:

![Путь от входных данных TeX к токену TeX.](/files/956ea3260fb19ae08e235f8d93cfb5a4fc3f1acd)

## Токены TeX для команд

После того как мы проделали путь через приведённые выше объяснения, фактическое вычисление токенов TeX для управляющих последовательностей оказывается на самом деле очень простым. TeX использует значение `curcs` (1218) из хеш-функции, чтобы создать простое целое число, которое он называет *токен*. Вычисление для получения токена из значения `curcs` таково:

```c
curtok = 4095 + curcs
```

TeX хранит значение текущего токена (самого недавно вычисленного) в переменной под названием `curtok`.

Итак, в заключение, токен TeX, представляющий `\def` команды таков `4095 + 1218 = 5313`. И на этом всё для токенов TeX, которые представляют последовательности команд: это просто целое число, вычисляемое как значение из хеш-таблицы плюс 4095.

## Токены TeX для символов

Когда TeX нужно создать токен, представляющий символ, он использует следующее, столь же простое вычисление:

```c
curtok = 256*catcode + (ASCII value of character)
```

Обратите внимание, что для движков с поддержкой Unicode, таких как LuaTeX, используются немного иные вычисления.

Например, токен TeX, представляющий символ пробела с `\catcode` категорией 10 и ASCII-значением 32, равен:

```c
256*10 + 32 = 2592
```

### Списки токенов, содержащие символы

Когда вы создаёте простой список токенов, например,

```latex
\toks100={Hello}
```

TeX создаст следующий список токенов и сохранит его в памяти для последующего использования:

* H→ 256 × 11 + 72 = 2888
* e→ 256 × 11 + 101 = 2917
* l→ 256 × 11 +108 = 2924
* l→ 256 × 11 +108 = 2924
* o→256 × 11 + 111 = 2927

Глубоко в памяти TeX регистр токенов 100 обеспечит доступ к области хранения «Hello», сохранённой как 5 значений токенов: 2888, 2917, 2924, 2924, 2927. Обратите внимание, что эти токены объединяют ASCII-код каждого символа и значение его `\catcode`в момент, когда они преобразуются в токены (токенизируются). Как только символы преобразованы в символьные токены, `\catcode` связанное с ними значение становится постоянным и хранится внутри токенов для последующего использования, когда пользователь скажет, например, `\the\toks100`.

Как отмечалось, символьный токен вычисляется по формуле `256*catcode + (ASCII value)` тогда как токен управляющей последовательности вычисляется по формуле `4095 + curcs` где `curcs` —это хеш-значение управляющего слова (текстовой строки введённой пользователем команды), обнаруженного TeX во входных данных. Стоит отметить, что символьные токены всегда меньше 4095. Поэтому TeX легко может определить, представляет ли конкретный токен управляющую последовательность (команду) или символ, а затем выяснить, какая именно управляющая последовательность или символ, `\catcode` пара закодирована в этом токене.


---

# 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/ru/podrobnye-stati/53-what-is-a-tex-token.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.
