> For the complete documentation index, see [llms.txt](https://ayakaleaf-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://ayakaleaf-pro.ayaka.space/latex/ru/drugie-temy/21-how-tex-macros-actually-work-part-3.md).

# Как на самом деле работают макросы TeX: часть 3

[Часть 1](/latex/ru/drugie-temy/19-how-tex-macros-actually-work-part-1.md) [Часть 2](/latex/ru/drugie-temy/20-how-tex-macros-actually-work-part-2.md) [Часть 3](/latex/ru/drugie-temy/21-how-tex-macros-actually-work-part-3.md) [Часть 4](/latex/ru/drugie-temy/22-how-tex-macros-actually-work-part-4.md) [Часть 5](/latex/ru/drugie-temy/23-how-tex-macros-actually-work-part-5.md) [Часть 6](/latex/ru/drugie-temy/24-how-tex-macros-actually-work-part-6.md)

## Время сделать паузу!

Прежде чем перейти к следующей части этой истории, нам нужно подвести итог: напомнить себе, куда мы идём, и собраться с мыслями, чтобы убедиться, что все ключевые идеи на месте. Напоминаю, наш разобранный пример основан на предположении, что TeX прочитал строку текста, содержащую `Hello World \jobname` и что TeX верстает это, чтобы построить абзац.

### Конечная цель

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

* TeX читает (сканирует) каждый символ во входных данных, и для каждого символа у TeX есть два вида информации:
* **код символа**: целое число, используемое для идентификации этого символа, например, при хранении во входном файле .tex;
* **код категории**: ещё одно целое число, внутреннее для TeX, которое он использует для назначения значения каждому символу, прочитанному из ввода.
* Как только TeX считывает символ, код категории каждого символа становится *безвозвратно* связан с этим отдельным символом посредством создания символьного токена:
* TeX использует простую формулу, чтобы «упаковать» код символа и соответствующий ему код категории в целое число, называемое символьным токеном.
* Вы можете изменить значение любого символа, который TeX *ещё не прочитал* назначив другой код категории любому символу, поведение которого вы хотите изменить — то есть изменив способ, которым TeX обращается с этим символом.
* Переопределение (переназначение) кодов категорий выполняется с помощью примитива TeX `\catcode` команды.
* Когда TeX видит код категории 0, он переключается в специальный режим сканирования и начинает искать команду: либо (потенциально) многобуквенное *управляющее слово* или однобуквенный *управляющий символ*.

Пока что мы рассматривали процесс сканирования входа TeX, когда он распознаёт отдельные символы и использует код категории каждого символа, чтобы определить, что делать дальше. Некоторые символы — это просто обычные элементы текста для верстки (например, код категории 11), но мы также видели пробельные символы (код категории 10) и escape-символы (код категории 0). Есть и другие коды категорий, которые, ради краткости, мы не рассматривали — например, код категории 1 («начало группы», например, `{`), код категории 2 («конец группы», например, `}`) и другие. Каждый код категории играет свою собственную роль во входном сканировании TeX и последующей обработке программными процессами/алгоритмами внутри TeX.

### Токены: краткий обзор

Понятие «токены» является центральным для того, как работает TeX: вы будете видеть упоминания «токенов» или ссылки на них во всех книгах, статьях и интернет-сообществах, посвящённых TeX, поэтому стоит кратко повторить эту тему — более подробную информацию можно найти в ранее опубликованной статье [Что такое «токен TeX»?](/latex/ru/podrobnye-stati/53-what-is-a-tex-token.md)

Мы уже видели, что TeX преобразует входные символы в токены, объединяя код символа и код категории в одно составное целое число. TeX делает нечто похожее и для команд: используя имя команды, он вычисляет целое число, называемое *токеном команды* (мы рассмотрим это подробнее). В качестве ориентира можно думать о токенах как о способе TeX «упаковывать» элементы, прочитанные из ввода, подготавливая их к передаче на следующую стадию обработки TeX. Когда все элементы (символы или команды) аккуратно завернуты в единое числовое представление, их легче обрабатывать дальше по цепочке. Например, когда TeX хочет сохранить часть вашего ввода для последующего использования, например определение макроса, TeX нужно лишь сохранить ваше определение макроса, каким бы сложным оно ни было, как последовательность целых чисел, где каждое целое число — это токен, представляющий символ или команду, входящие в (содержащиеся в) определение вашего макроса.

## Итак, что дальше?

В заключительном разделе Части 2 мы увидели, как escape-символ (код категории 0) переводит TeX в специальный режим обработки, где он ищет имя команды. В нашем примере TeX обнаружил строку символов `jobname` и мы завершили Часть 2 на том месте, где TeX собирался «что-то сделать» с этой строкой символов (именем команды). В этой части мы подробно рассмотрим, что TeX делает дальше.

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

### От строки символов к выполнению команды: как?

Сначала давайте вернёмся к Графике 5b из Части 2, где TeX определил, что определённая строка символов во входных данных составляет имя команды: `jobname`. Графика 5b показывает, что TeX должен «проверить внутренние таблицы...». Что же это *на самом деле означает*?

![TeX ищет имя команды](/files/e5b43b51cbbffcf8a15286cc4094d63bdcf6ab2c)

Более подробное описание того, как TeX «проверяет внутренние таблицы», чтобы перейти от строки символов (например, `jobname`) к точному определению того, что это за команда и что она означает, можно найти в предыдущей статье [Что такое «токен TeX»?](/latex/ru/podrobnye-stati/53-what-is-a-tex-token.md) Здесь мы кратко изложим ключевые идеи, стараясь избежать излишних повторов.

Начнём с аналогии. Предположим, вы читаете книгу и натыкаетесь на незнакомое слово: что вы делаете? Сегодня почти наверняка вы «тянетесь к Google», но давайте предположим, что вы предпочитаете более старый метод: вы тянетесь к словарю, который перечисляет слова и даёт их значение(я). У TeX есть аналогичный механизм: внутренний «словарь», который перечисляет все команды, известные TeX на данный момент, и «значение» этих команд. Под «значением» мы имеем в виду, к какому типу относится команда: что она делает, а также любую другую информацию, которая может понадобиться TeX для выполнения этой команды. Заметим также, что термин «команда» включает любые макросы TeX/LaTeX, написанные пользователями/программистами TeX, и [сотни встроенных примитивных команд](/latex/ru/drugie-temy/46-tex-primitives-listed-by-tex-engine.md).

Продолжая нашу аналогию со словарём. Когда нам, как читателям, нужно выяснить значение слова, мы ищем его в словаре по алфавитному списку слов, который предоставляет словарь — но, конечно, TeX работает не совсем так. Возвращаясь к нашему исходному `jobname` примеру, как TeX находит в своём «словаре» «значение» `jobname`— и что именно это «значение» даёт TeX?

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

Каждый раз, когда TeX обнаруживает команду, использованную во входных данных, и ему нужно что-то узнать об этой команде, он преобразует последовательность символов в имени команды в «эквивалентное» целое число и использует это число, чтобы найти команду в своём «большом словаре». Программистам среди вас будет интересно узнать, что TeX использует форму [хэш-функции](https://en.wikipedia.org/wiki/Hash_function) для этого преобразования.

![Диаграмма хэш-функции](/files/cada78ad49ffa5924c9ceb633018bf9c87897705)

### Графика 6: От символов к значению команды

Следующая графика показывает путь, который проходит команда, пока TeX преобразует строку символов в эквивалентное целое число, которое он называет **curcs**, и использует это целое число для поиска значения команды в «большом словаре» TeX. Результатом такого поиска являются два вида информации: два целых числа, называемые **curcmd** и **curchr**, которые TeX может использовать, чтобы точно определить, что именно делает команда и как затем её выполнить.

![TeX преобразует строку символов в эквивалентное целое число для поиска значения команды](/files/196e50268aa14015dfbbc1ea8b5804997579ec44)

Внутри TeX существует переменная под названием **curcs** (**cur**rent **c**ontrol **последовательности**equence) которая используется для хранения целочисленного значения команды, над которой TeX в данный момент работает, то есть, **curcs** хранит целое число, вычисленное из имени команды. Но это ещё не вся история, потому что есть ещё одна деталь: если TeX только что прочитал/обработал символ, а не команду, он установит **curcs** значение 0, чтобы помнить, что последним прочитанным элементом был символ, а не команда.

### Что команды означают для TeX

Если мы посмотрим на набор [встроенных команд, предоставляемых движками TeX](/latex/ru/drugie-temy/46-tex-primitives-listed-by-tex-engine.md) мы увидим, что некоторые из этих команд тесно связаны: они выполняют похожие задачи; например, существует 4 примитивные команды, которые все движки TeX используют для определения (создания) макросов: `\def`, `\gdef`, `\edef`, `\xdef`. Эти 4 команды все определяют макросы, но, конечно, каждая делает это немного по-своему. Если посмотреть на это с точки зрения программирования: у нас есть 4 команды определения макросов, которые в целом делают одно и то же, но нам нужно выбирать между ними, чтобы учитывать их индивидуальное поведение.

Чтобы справиться с этим, TeX присваивает каждой команде два значения, и именно эти два значения TeX понимает как «значение» команды (её роль/что она делает) — эти два значения внутренние для TeX, глубоко внутри программы, и являются частью «внутреннего механизма», недоступного пользователям. Каждой команде TeX, будь то встроенный примитив или определённая пользователем команда, присваиваются два значения, которые для TeX определяют/классифицируют её поведение — то, что она означает для TeX. Когда TeX использует свой «большой словарь» для поиска команды, он находит эти две важные части информации:

* **кодом команды**: своего рода «общая классификация», указывающая, к какому «типу» относится команда — например, команда «определения макроса» (одна из `\def`, `\gdef`, `\edef`, `\xdef`); команда «создания коробки» (одна из `\hbox`, `\vbox` или `\vcenter`) и так далее для сотен команд, поддерживаемых движками TeX. Макросам (пользовательским командам) также присваивается код команды.
* **модификатор команды**: это вспомогательная информация, которая предоставляет TeX конкретные сведения о команде. Макросам (пользовательским командам) также присваивается модификатор команды — хотя у макросов модификатор команды играет немного иную роль, чем у примитивов (для макросов модификатор команды указывает, где в памяти хранится определение макроса).

В совокупности код команды и модификатор команды однозначно идентифицируют каждую команду. Вот коды команд и модификаторы команд для команд определения макросов, как они используются в оригинальном программном обеспечении TeX Кнута — обратите внимание, что другие движки TeX могут использовать иные значения, но принцип остаётся абсолютно тем же:

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

### Итог: как разобраться во всех этих переменных/значениях

Сейчас мы буквально утопаем в большом количестве информации о значениях, переменных, значениях команд и прочих деталях — это может быстро запутать, так что давайте упорядочим то, что мы знаем. Когда TeX читает что-то из вашего ввода, это либо символ, либо команда. Каждый раз, когда TeX читает что-либо из ввода, ему нужно сохранить информацию о том, что он только что прочитал (просканировал):

* Для **символы**: ему нужно записать код символа и код категории. Ему также нужно создать и сохранить значение токена, которое TeX вычисляет с помощью этих значений.
* Для **команды**: TeX нужно знать числовой эквивалент, **curcs**, который он вычислил из имени команды. Ему также может понадобиться сохранить «значение», которое он получил, найдя команду в «словаре» TeX: код команды и модификатор команды. Кроме того, TeX также должен вычислить значение токена, представляющее эту команду.

Да, это запутанно: вокруг плавает много переменных и идей токенов, так что давайте попробуем во всём этом разобраться.

Внутри TeX использует четыре глобальные переменные для хранения информации о последнем элементе, который TeX прочитал (или над которым в данный момент «работает») — мы не будем подробно обсуждать эти переменные, но знание об их существовании помогает немного лучше понять, что на самом деле происходит:

* **curcmd**: (current command) целочисленная переменная. Используется для хранения *текущей команды* значения команды, которая обрабатывается *или* она хранит текущий *код категории* код обрабатываемого символа;
* **curchr**: (current character) целочисленная переменная, но то, что она хранит, зависит от того, что TeX только что прочитал из ввода:
* **символ**: Если последним прочитанным элементом является символ, **curchr** хранит текущий *код символа*.
* **команда**: Если последним прочитанным элементом является команда, **curchr** хранит *модификатор команды*: дополнительная информация, которую TeX использует, чтобы поддержать/уточнить **curcmd**—потому что, как мы видели выше, некоторые команды имеют одно и то же значение **curcmd**
* **curcs**: (текущая управляющая последовательность) целочисленная переменная, которая хранит значение, вычисленное из строки символов в имени команды. **curcs** = 0, если последним прочитанным элементом был отдельный символ, а не имя управляющей последовательности (имя команды);
* **curtok**: (текущий токен) целочисленная переменная, которая хранит значение текущего токена — либо токена команды, либо символьного токена.

Вот эта информация в виде таблицы:

|                                                     |                                                                                                                                                                                                                          |                                                                                                                                                                                                                                                                       |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Глобальная переменная, используемая внутри TeX:** | **Когда TeX сканирует символ:**                                                                                                                                                                                          | **Когда TeX сканирует команду:**                                                                                                                                                                                                                                      |
| **curcmd**                                          | Хранит код категории текущего символа                                                                                                                                                                                    | Хранит *кодом команды*—который определяет «тип» текущей команды                                                                                                                                                                                                       |
| **curchr**                                          | Хранит код символа текущего символа                                                                                                                                                                                      | Хранит вспомогательные данные (называемые *модификатор команды*), которые предоставляют дополнительную информацию о текущей команде                                                                                                                                   |
| **curcs**                                           | 0                                                                                                                                                                                                                        | Положительное ненулевое целое число, вычисляемое (с помощью хэш-функции) на основе строки символов, содержащейся в имени команды. Оно используется для доступа к «словарю» TeX, чтобы найти текущее значение команды — получить её код команды и модификатор команды. |
| **curtok**                                          | Для 8-битных движков TeX *символьным токеном* вычисляется по формуле: $$\text{curtok}=256\times \text{curcmd} + \text{curchr}$$ где $$\text{curcmd}$$ — это код категории символа, а $$\text{curchr}$$ — это код символа | Для 8-битных движков TeX *токеном команды* вычисляется по формуле: $$\text{curtok}=4095 + \text{curcs}$$                                                                                                                                                              |

### Дополнительные замечания о текущем токене

Для символов максимальное возможное значение токена получается при использовании наибольшего кода категории (15) и наибольшего кода символа, который для 8-битных движков TeX равен 255. В теории (для 8-битных движков TeX) максимальное значение символьного токена, $$\text{curtok}\_{\text{max}}$$, равно:

$$\text{curtok}\_{\text{max}}= 256\times 15 + 255 = 4095$$

Мы отмечаем «в теории», потому что код категории 15 используется для обозначения «недопустимого символа», который вызывает ошибку TeX: недопустимый символ никогда не пройдёт через процесс входного сканирования TeX и, следовательно, никогда не станет символьным токеном.

Для команд текущий токен ($$\text{curtok}$$) вычисляется из $$\text{curtok}=4095 + \text{curcs}$$ но для команд $$\text{curcs}$$ равно *всегда* ненулевое, поэтому TeX может легко определить, что представляет собой токен:

* Если $$\text{curtok} > 4095$$ то это токен команды;
* Если $$\text{curtok} < 4095$$ это символьный токен.

По сути, TeX использует токены — простое целочисленное значение — чтобы «упаковать» всю информацию, которую ему нужно знать об элементе, прочитанном из ввода.

## Часть 4

В Части 4 мы рассмотрим ряд примеров макросов, чтобы показать роль и назначение `<текст параметров>` раздела макроса, который служит «шаблоном токенов» и может быть построен с помощью токенов-разделителей.

[Часть 1](/latex/ru/drugie-temy/19-how-tex-macros-actually-work-part-1.md) [Часть 2](/latex/ru/drugie-temy/20-how-tex-macros-actually-work-part-2.md) [Часть 3](/latex/ru/drugie-temy/21-how-tex-macros-actually-work-part-3.md) [Часть 4](/latex/ru/drugie-temy/22-how-tex-macros-actually-work-part-4.md) [Часть 5](/latex/ru/drugie-temy/23-how-tex-macros-actually-work-part-5.md) [Часть 6](/latex/ru/drugie-temy/24-how-tex-macros-actually-work-part-6.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://ayakaleaf-pro.ayaka.space/latex/ru/drugie-temy/21-how-tex-macros-actually-work-part-3.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.
