> 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/54-what-is-a-tex-token-list.md).

# Что такое список токенов TeX

## Итак, что же такое «список токенов TeX»?

В одной [предыдущей статье](/latex/ru/podrobnye-stati/53-what-is-a-tex-token.md)— также часть этой [серии о низкоуровневых TeX-нюансах](/latex/ru/podrobnye-stati/01-a-new-series-of-articles-tex-tokens-and-related-concepts-but-why-and-how.md)— мы исследовали процессы, с помощью которых TeX сканирует ваш `.tex` файл, чтобы генерировать новые токены: мы рассмотрели фундаментальную природу токена TeX и то, как TeX их создаёт (см. [Что такое «токен TeX»?](/latex/ru/podrobnye-stati/53-what-is-a-tex-token.md)).

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

Списки токенов играют ключевую роль во внутренней работе TeX, нередко довольно неожиданным образом, например во внутреннем действии команд вроде `\uppercase` и `\lowercase`. Одно из особенно важных применений списков токенов — хранение и выполнение макросов; эту тему мы подробно рассмотрим в одной из следующих статей этой серии.

### TeX получает свой ввод из файлов и списков токенов

У движков TeX есть три источника ввода — два из них вам, возможно, знакомы:

* физические текстовые файлы, хранящиеся на диске;
* текст, который пользователь вводит в терминал (командную строку);

но есть и третий способ чтения/получения ввода: списки токенов!

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

В качестве быстрого примера низкоуровневая (примитив TeX) `\toks` команда позволяет создать список токенов, который TeX сохраняет в памяти для последующего повторного использования:

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

Чтобы извлечь эти токены (то есть указать TeX рассматривать их как следующий источник ввода), нужно выполнить команду вроде

```latex
\the\toks100
```

Это заставит TeX переключиться с создания новых токенов из вашего входного файла на получение следующего ввода оттуда, где эти токены (созданные с помощью `\toks`) хранятся — в так называемом *регистр токенов* , который просто является внутренней областью памяти, известной TeX (здесь это регистр 100).

Кроме того, списки токенов могут внутренне генерироваться «на лету» рядом команд TeX. Один пример — команда `\jobname` которая генерирует последовательность символьных токенов — по одному токену на каждый символ в имени основного файла, который TeX обрабатывает. Ещё один пример — команда `\string` ; например

```latex
\string\mymacro
```

генерирует последовательность символьных токенов для каждой буквы в имени `\mymacro`— включая начальный `\` символ. В конце этой статьи мы подробнее рассмотрим некоторые «команды, генерирующие токены».

## Список токенов: объяснение по аналогии

Если у вас нет опыта программирования и/или знаний в области информатики, «списки токенов» могут показаться несколько туманным и, возможно, немного запутанным понятием. Однако если вы хотите научиться хорошо писать макросы TeX/LaTeX, то хорошее понимание таких тем, как токены TeX, списки токенов и коды категорий (`\catcode`) окажется чрезвычайно полезным.

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

### Списки токенов: аналогия (мысленный эксперимент)

Мы проведём «мысленный эксперимент», чтобы создать основу для понимания списков токенов TeX. Представьте, что у вас есть большой набор контейнеров, например сотни жестяных коробок — мы не можем использовать термин «box» для обозначения контейнеров в нашем мысленном эксперименте, потому что, конечно, «box» в TeX имеет вполне конкретное значение, совершенно не связанное с нашей темой. Поэтому мы назовём наши контейнеры «жестяными коробками» (Tins), где каждая Tin:

* имеет уникальный идентификационный номер, напечатанный снаружи;
* разделена внутри на два отсека.

Эти два отсека устроены следующим образом:

* левый отсек хранит предмет, который вы хотите поместить в Tin;
* правый отсек предназначен для листка бумаги, на котором можно написать одно число: номер, идентифицирующий другую Tin.

![тест](/files/cf44432249af9e65fa287c48a8b0798606ab7aac)

Предположим, у вас есть коллекция, скажем, из 5 предметов, и вы хотите хранить эту коллекцию в этих Tin; но, увы, каждая Tin может вместить только 1 предмет того типа, который вы хотите хранить.

Для простоты предположим, что мы хотим хранить 5 цветных кружков:

![{{{alt}}}](/files/9961d58e7905cd2bf0b2bda3b43389bc795c9903)

Более того, когда вы возвращаетесь, чтобы извлечь эти предметы из вашей системы хранения (Tin), эти предметы *должны* должны быть извлечены/найдены в определённом порядке — в том порядке, в котором они были сохранены: эта последовательность должна сохраняться. Как этого добиться?

Мы можем воспользоваться тем, что каждая Tin:

* имеет уникальный идентификационный номер, прикреплённый снаружи;
* имеет 2 отсека — только один из них мы будем использовать для хранения нашего предмета, а в другом лежит листок бумаги с написанным на нём номером другой Tin.

Предположим, что каждая Tin пуста, — но ничто не мешает вам открыть любую Tin и проверить, пуста ли она; если нет, пробуйте следующую, пока не найдёте пустую Tin.

Можно поступить следующим образом. Поместим первый предмет (тёмно-зелёный кружок) в одну из наших Tin (например, Tin 124) и запишем номер этой первой Tin — неважно, какой именно номер у этой первой Tin, важно лишь, чтобы мы где-то его записали и сохранили для последующего использования.

![{{{alt}}}](/files/70bdc3ed36a5e39a7d801903eccbf225773666fb)

Найдите вторую Tin — любую Tin с любым номером (например, Tin 432) — и запишите её номер. Напишите номер этой второй Tin (432) на листке бумаги и поместите эту записку *в первую Tin* (Tin 124). Мы помещаем второй предмет (светло-зелёный кружок) во вторую Tin. Итак, сейчас у нас такая ситуация:

* записка, не хранящаяся в Tin, в которой указано, что первая Tin имеет номер 124 (в ней лежит наш первый предмет);
* в Tin 124 мы добавили ещё одну записку, в которой сказано, что следующий предмет находится в Tin 432.

По сути, мы *связали* наши первые две Tin: мы знаем, с чего начинать (Tin 124), и что записка в Tin 124 подсказывает, в какой Tin находится следующий предмет (Tin 432).

![{{{alt}}}](/files/66d6e41b7a7f3692c4408ce32d990cab078d6269)

Затем мы находим третью Tin, записываем её номер (например, Tin 543) на листке бумаги и помещаем это в *вторую* Tin (номер 432). Затем мы помещаем третий предмет (красный кружок) в третью Tin.

Теперь у нас связаны три Tin в последовательность: отправная точка, Tin 124 (тёмно-зелёный кружок) → Tin 432 (светло-зелёный кружок) → Tin 543 (красный кружок) →…

![{{{alt}}}](/files/9432b3625cdf420a02fc02f2078d018b8217ef39)

Повторите этот процесс для последних двух предметов (светло-синего и тёмно-синего кружков), используя Tin 213 (светло-синий кружок) и Tin 102 (тёмно-синий кружок).

![{{{alt}}}](/files/ef05f47c6578edc7eeab7b63c7c43e5ea76ea314)

Теперь у нас все 5 Tin связаны между собой (с использованием числового идентификатора каждой Tin), и мы можем извлечь все сохранённые предметы — в правильном порядке — просто посещая каждую Tin по очереди, доставая наш предмет и читая записку, которая сообщает, в какой Tin находится следующий предмет.

### А что насчёт последнего предмета в нашем списке (Tin 102)?

Почему именно об этом предмете мы должны беспокоиться? До сих пор мы хранили каждый предмет в Tin вместе с запиской о том, в какой Tin находится следующий предмет: что же должна говорить эта записка для последнего предмета в нашем списке — ведь следующей Tin не существует.

Когда мы доходим до последнего предмета (Tin), должно быть очевидно, что эта Tin (содержащая последний предмет) — последний элемент нашего списка; нам не нужно искать ещё одну Tin, потому что её нет. Один из способов сделать это — поместить в нашу последнюю Tin (102) «особый» номер Tin. Мы можем использовать любой номер, если выберем уникальное число, которое не является номером реальной Tin, например «Tin -1», «Tin 0»: неважно, лишь бы мы знали, что «Tin -1» или «Tin 0» и т. д. сразу подсказывает остановиться: искать больше Tin не нужно, потому что это последняя, и, следовательно, больше нет предметов для извлечения.

### От «предметов» и «Tin» к токенам и TeX

Теперь нам нужно перейти от нашей аналогии к описанию, более близкому к реальности TeX. Во-первых, вместо хранения кружков разного цвета в наших воображаемых Tin можно рассматривать эти Tin как хранилище токенов TeX: простых целых чисел. Это более простая часть переноса нашей аналогии в мир программного обеспечения (TeX). Но каков может быть программный эквивалент наших физических пронумерованных Tin с «отсеками»?

Мы не хотим слишком далеко уходить в программные концепции, но можно думать о наших «Tin» как о нескольких байтах компьютерной памяти, «упакованных» в удобную единицу хранения. Использование числового идентификатора для каждой Tin в нашей аналогии можно считать адресом внутри компьютерной памяти, где располагается каждый такой небольшой пакет памяти. В самом TeX эти маленькие пакеты хранения называются «словами памяти» — термин, отражающий время/эпоху, когда был создан TeX (1970-е). Эти «слова памяти» — фундаментальный строительный блок, используемый в TeX, но здесь нам не нужно рассматривать их подробнее — тот, кто хочет большего, может обратиться к статье на [личном блоге автора](http://www.readytext.co.uk/?p=3537).

В терминах программирования то, о чём мы говорили, называется [*связным списком*](https://en.wikipedia.org/wiki/Linked_list): список токенов TeX — это связный список, построенный из контейнеров хранения TeX, называемых *словами памяти* где каждое слово памяти может использоваться для хранения:

* один *значения*: значения токена (целого числа);
* один *ссылки*: адрес следующего слова памяти, содержащего следующий токен в нашем списке.

## Где TeX использует списки токенов?

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

### Пример макроса

Макрос можно считать состоящим из трёх частей:

```
\def\<имя макроса><текст параметров>{<текст замены>}
```

Обратите внимание, что вместо `\def` вы могли бы использовать `\edef`, `\gdef` или `\xdef`.

**Примечание для пользователей LaTeX**: здесь мы определяем макросы с помощью «сырых», низкоуровневых команд TeX (называемых *примитивов*). Пользователям LaTeX будет более знакомо создание макросов через `\newcommand` (который сам является макросом).

Когда вы просите TeX создать (определить) макрос, он создаст токен, который представляет `<имя макроса>` и *список токенов* который представляет объединённые `<текст параметров>` и `<текст замены>`. TeX аккуратно сохранит всё, чтобы токен, представляющий `<имя макроса>` был связан со списком токенов, представляющим его определение (`<текст параметров>` и `<текст замены>`).

Например, если мы определим `\mymacro` так:

```latex
\def\mymacro abc #1 defz{Я напечатал "#1"!}
```

Мы можем видеть, что его составные части:

* `<имя макроса>` = `mymacro`
* `<текст параметров>` = `abc #1 defz`
* `<текст замены>` = `Я напечатал "#1"!`

Например, вы могли бы вызвать `\mymacro` так:

```latex
\mymacro abc THIS TEXT defz
```

что приводит к `Я напечатал "THIS TEXT"!` набору текста — а `abc` и `defz` вошли *не* набираются. `abc` и `defz` являются последовательностями символьных токенов, используемых для *ограничивания* параметра макроса `#1` и поглощаются и отбрасываются, когда вызов вашего макроса успешно обрабатывается TeX.

Когда вы определили `\mymacro`, шаблон токенов, содержащийся в сохранённом, действует как «шаблон», который TeX может использовать, чтобы определить:

* какие токены во входных данных являются токенами-разделителями;
* какие токены во входных данных фактически образуют параметр(ы) вашего макроса (здесь то, что вы используете для `#1` в вызове `\mymacro`).

Вы должны вызвать `\mymacro` с `<текст параметров>` с разделителями, идентичными тем, что использовались при его определении, — это включает использование символьных разделителей с одинаковыми кодами категорий. Если разделители в `<текст параметров>` используемые для вызова `\mymacro` отличаются от тех, что использовались для его определения («шаблона», сохранённого в памяти), TeX может довольно сильно запутаться — когда он попытается обработать `\mymacro` он не сможет сопоставить «шаблон», сохранённый в его памяти.

Когда TeX видит, что вы вызываете макрос, он сканирует ваш входной текст, создаёт новые токены и пытается токен за токеном сопоставить их со списком токенов `<текст параметров>` шаблона, сохранённым как часть определения вашего макроса. Если разделители, используемые во входном тексте, приводят к последовательности токенов, не совпадающей с теми, что хранятся в «шаблоне», TeX обычно выдаст ошибку.

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

Предположим, мы изменили код категории `z` на, скажем, 12 — обычно это 11 — и попробуем вызвать наш макрос так:

```latex
\catcode`z=12
\mymacro abc THIS TEXT defz more text here...
```

На этот раз это не сработает, потому что код категории `z` был изменён. Вы увидите такую ошибку:

```latex
Сбежавший аргумент?
THIS TEXT defz
! Абзац закончился до того, как \mymacro был завершён.
<будет прочитано снова>
\par
l.22
```

Когда TeX читает и сканирует `z` в `defz` он не может распознать его как конец `\mymacro`его `<текст параметров>` , используемого в вашем входном файле. До появления этого ошибочного `z` TeX правильно сопоставлял первые 3 символа `def` но это `z` (с кодом категории 12) сбивает сканирование TeX. Предположим, `z` имел код категории 11, когда мы *определили* `\mymacro`: это привело бы к тому, что значение токена 256×11 + 122 = 2938 было бы сохранено как часть `\mymacro`его определения (то есть сохранено как часть «шаблона»). Однако при коде категории 12, `z` теперь будет создано значение токена 256×12 + 122 = 3194. Поскольку значение токена (для `z`) прочитанное из вашего ввода (значение 3194) не совпадает с `z`-токеном, содержащимся в сохранённом `<текст параметров>` шаблоне списка токенов (значение 2938), TeX продолжит сканировать ваш ввод. TeX будет продолжать сканировать текст, следующий после вашего макроса (*еще текст здесь* ...) в поисках дополнительных токенов — пытаясь сопоставить сохранённый шаблон с токенами, которые он находит во входных данных. Скорее всего, он не найдёт правильную последовательность токенов, и возникнет ошибка, поскольку TeX «перескочит» ваш ввод и ошибочно прочитает лишний текст, чтобы создать дополнительные токены — эти лишние токены не должны были быть прочитаны на этом этапе и почти наверняка вызовут ошибку.

Мы подробнее рассмотрим это в одной из следующих статей.

## Другие способы использования списков токенов

К другим командам, используемым для создания/хранения списков токенов, относятся:

```latex
\toks<n>={...}
\everypar={...}
\everymath={...}
\everydisplay={...}
\everyhbox={...}
\everyvbox={...}
\output={...}
\everyjob={...}
\everycr={...}
\errhelp={...}
```

Каждая из этих команд создаёт список токенов из символов и команд внутри фигурных скобок «{...}», и этот список токенов предназначен для повторного использования в определённых обстоятельствах. Например, `\everypar={...}` создаёт и сохраняет набор токенов (список токенов), который TeX вставляет во входные данные непосредственно перед началом нового абзаца.

## Скрытое использование списков токенов: примеры

В этом заключительном разделе мы рассмотрим несколько практических примеров использования списков токенов в неожиданных сценариях.

### Пример 1: \uppercase{...} и \lowercase{....} — временные списки токенов

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

Хороший пример — команды `\uppercase{...}` или `\lowercase{...}` потому что их работа при первом знакомстве может показаться довольно запутанной. Но как только вы поймёте, что они делают глубоко внутри TeX и невидимо для пользователя, их работа станет гораздо понятнее.

Предположим, у вас есть простая последовательность букв, которую вы хотите перевести в верхний регистр — например, abcde в ABCDE. Что ж, с помощью TeX это довольно просто: `\uppercase` команду:

```latex
\uppercase{abcde}
```

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

```latex
\toks100={abcde}
\def\mychars{abcde}
```

Затем, в какой-то момент, вы можете решить, что хотите повторно использовать эту последовательность букв, но на этот раз в верхнем регистре; и вы пытаетесь

```latex
\uppercase{\the\toks100}
```

и

```latex
\uppercase{\mychars}
```

Но, увы, ни один из этих вариантов не работает. Почему?

### Секретные списки токенов!

Чтобы понять, как команды `\uppercase{...}` `\lowercase{...}` на самом деле работают, мне пришлось заглянуть внутрь механизма TeX, поэтому следующее объяснение основано на этом.

Когда TeX обнаруживает `\uppercase{<material>}` или `\lowercase{<material>}` во входных данных, первое, что делает TeX, — создаёт (временный) внутренний список токенов из `<material>` , заключённого между «{» и «}», которые следуют за `\uppercase{...}` или `\lowercase{...}` командами; этот временный список токенов является внутренним для TeX.

Ключевой момент, центральный для понимания того, как `\uppercase{<material>}` и `\lowercase{<material>}` на самом деле работают, заключается в том, что любые команды или макросы, содержащиеся в `<material>` не *развёрнут*: всё, что делает TeX, — это генерирует токены из символов и команд, помещённых между `{...}`. Во время работы `\uppercase{<material>}` или `\lowercase{<material>}` ничего между фигурными скобками не выполняется: оно просто превращается в токены.

После `<material>` внутри `{...}` после преобразования в (временный) список токенов TeX затем снова просматривает каждый токен в этом списке и проверяет, является ли он *символ* токеном или *команда* токеном (используя числовое значение токена). Если TeX обнаруживает символьный токен, он изменяет этот токен, чтобы скорректировать регистр символа (в зависимости от того, `\uppercase` или `\lowercase` что обрабатывается). TeX просто игнорирует любые токены команд и не «заглядывает внутрь» токенов команд, чтобы увидеть, что они представляют или содержат (например, макрос, содержащий символы), — они просто пропускаются: операциями изменения регистра действительно обрабатываются/затрагиваются только символьные токены.

Так, например, если мы выполним команду TeX вроде `\uppercase{abcde}` TeX создаст список токенов из `abcde` содержащий только символьные токены: все они преобразуются, чтобы создать последовательность изменённых токенов, представляющих A, B, C, D и E. Эти изменённые токены возвращаются в процессор ввода TeX, что приводит к `ABCDE` набору текста. Однако если мы сохранили наши символы *внутри макроса*— например `\def\mychars{abcde}`— и пытаемся перевести их в верхний регистр вот так:

```latex
\uppercase{\mychars}
```

тогда это не сработает, и будет набрано abcde — а не ABCDE, как можно было бы ожидать. Если затем мы попытаемся сохранить наши символы в списке токенов вроде `\toks0={abcde}` и выполним `\uppercase{\the\toks0}` то, опять же, `\uppercase` не сработает, потому что список токенов будет полностью состоять из токенов, на которые не влияет `\uppercase`.

Рассмотрим пример с нашим макросом, `\mychars`, после того как TeX обнаруживает `\uppercase` во входных данных, TeX ищет значение `\uppercase` и выполняет его, создавая временный список токенов из `{\mychars}`. Очевидно, что этот временный список токенов содержит лишь один токен, который не является символьным токеном, а представляет нашу команду макроса `\mychars`: поэтому при выполнении `\uppercase`, этот токен игнорируется —`\mychars` не представляет символьный токен. Однако, как отмечалось выше, после того как `\uppercase` завершит свою работу, временный список токенов (созданный действием `\uppercase`) возвращается в полный механизм обработки входных данных (сканирования) TeX. Когда TeX снова читает этот список токенов, он обнаруживает токен, который представляет наш `\mychars` макрос, который TeX выполняет (разворачивает) и генерирует последовательность символов для набора abcde — всё ещё в нижнем регистре, потому что они были «упакованы» внутри макроса и, следовательно, невидимы для действий `\uppercase`.

После того как TeX повторно просмотрит временный список токенов, созданный для `\uppercase{...}` или `\lowercase{...}`, и обработает все символьные токены, он переключится на использование этого временного списка токенов как источника ввода: будет набирать символы (обработанные символьные токены) и выполнять команды и макросы.

### Как это исправить?

Поскольку `\uppercase{...}` или `\lowercase{...}` будет действовать только на символьные токены, нам нужен способ «принудительно распаковать» символы, содержащиеся в нашем макросе `\mychars` (или содержащиеся в `\toks` регистре) до того, как `\uppercase{...}` или `\lowercase{...}` начнёт действовать на него. Под «распаковкой» мы на самом деле имеем в виду процесс TeX *расширения*:

* замены команды TeX/LaTeX на *последовательность* токенов *, из которых эта команда* (*например, макрос*) *состоит,* или
* производя последовательность токенов, которую команда предназначена *генерировать*. Одним из примеров команды, которая генерирует токены, является `\jobname`, которая создаёт последовательность символьных токенов, представляющих имя основного TeX-файла, который обрабатывается.

#### Низкоуровневая магия: scantoks(..., ...)

Здесь мы действительно заглядываем в более тёмные уголки внутреннего устройства TeX, так что вы можете пропустить этот раздел, если только вам не нравятся такие детали…

После того как TeX обнаруживает `\uppercase` или `\lowercase` во входном потоке, он выполняет внутреннюю функцию под названием `scantoks(..., ...)` задача которой — сгенерировать список токенов, используя элементы между открывающей «{» и закрывающей «}» скобками: как уже обсуждалось, этот список токенов затем просматривается, чтобы обнаружить (а затем скорректировать) любые символьные токены для изменения регистра символов по необходимости. Обратите внимание, что здесь мы говорим о `scantoks(..., ...)` как о внутренней функции, встроенной в исходный код движков TeX — здесь это не имя управляющей последовательности.

В рамках своей работы `scantoks(..., ...)` можно указать, нужно ли разворачивать или не разворачивать создаваемый им список токенов, и для `\uppercase` и (`\lowercase`) он не разворачивает токены: он лишь создаёт их и помещает в список токенов.

Одна из первых вещей, которые `scantoks(..., ...)` должен сделать, — проверить наличие открывающей «{» (или любого символа с кодом категории `\catcode` 1), потому что он должен убедиться, что пользователь не допустил синтаксическую ошибку и не забыл открыть «{» (или любой символ с кодом категории 1), — поскольку символ с кодом категории 1 требуется, чтобы обозначить начало списка элементов, подлежащих токенизации.

И вот хитрость: задача поиска открывающей «{» запускает `scantoks(..., ...)` процесс расширения TeX, а это означает, что следующие примеры будут работать:

```latex
\let\ob={
\uppercase\ob abcde}
\def\obb{\ob}
\uppercase\obb xyz}
```

Если взять пример `\obb`, макроса, то он распознаётся как *разворачиваемая команда* и должным образом разворачивается TeX (через `scantoks(..., ...)` функцию) в поисках открывающей фигурной скобки (любого символа с кодом категории 1). Это означает, что мы можем использовать «`\expandafter` трюк» для достижения нашей цели — «распаковать» наши символы из рамок макроса, то есть развернуть его. Обратите внимание, что `\expandafter` также относится к категории *разворачиваемая команда*, поэтому TeX выполняет его здесь и позволяет ему сделать свою работу в рамках поиска открывающей «{» (или любого символа с кодом категории 1).

Итак, если вы определите:

```latex
\toks0={abcde}
\def\mychars{abcde}
```

И сделаете так:

```latex
\uppercase\expandafter{\mychars}
\uppercase\expandafter{\the\toks0}
```

в обоих случаях теперь будет набираться ABCDE, потому что the `\expandafter` вызывает «распаковку» (расширение) `\mychars` и `\the\toks0`—оба приводят к `\uppercase` видят поток символьных токенов, который они могут обработать, чтобы изменить регистр.

### Пример 2: \string—ещё временные списки токенов

Внутри TeX классифицирует `\string` как одну из своих так называемых команд «convert»: выполняющих операцию «преобразования в текст». Команда `\string` предназначена для преобразования токена в удобочитаемую текстовую версию — то есть для набора удобочитаемой строки символов, из которой этот токен был первоначально создан.

Например `\string\hello` создаёт временный список токенов, содержащий символы \\, h, e, l, l, o — да, даже включая начальный «\». После того как этот список токенов создан, TeX затем перечитывает его, и текст команды «`\hello`» набирается — да, включая «\», если вы выберете подходящий шрифт…

Вы можете задаться вопросом, как/почему TeX может набирать символ экранирования, если он обычно используется, чтобы заставить сканер TeX создавать токен команды: почему здесь он так не делает? Ответ связан с категориями кодов: обычно символ «\» имеет catcode 0 (escape-символ), но когда `\string` генерирует свой внутренний список токенов, он делает кое-что немного иное. Когда он создаёт список символьных токенов, он присваивает всем символам, кроме пробела, категориальный код 12, а пробелу — catcode 10; напомним, что символьные токены вычисляются как 256 × catcode + значение ASCII. Поэтому, когда TeX перечитывает (вводит) временный список токенов, который `\string` сгенерирован из `\hello`, TeX *не видит символ экранирования* потому что токен для «\» был вычислен с catcode 12, а не 0: TeX просто воспринимает «\» как обычный символ и набирает его.

Строго говоря, стоит, пожалуй, отметить, что TeX на самом деле не создаёт токен для escape-символов, когда обнаруживает их во входных данных. Как только он распознаёт символ с категорией кода 0, этот символ просто используется, чтобы «запустить» создание токена управляющей последовательности: после того как он заставил TeX сделать это, escape-символ выполнил свою работу и больше не учитывается.

### Техническое примечание

Команда под названием `\showtokens{...}` (введённая движком e-TeX) может показывать списки токенов (в файле журнала). Из руководства e-TeX:

> Команда `\showtokens{<token list>}` отображает список токенов и позволяет выводить величины, которые не могут быть отображены с помощью `\show` или `\showthe`, например:
>
> ```latex
> \showtokens\expandafter{\jobname}
> ```

## В заключение

В разделе 291 исходного кода TeX (см. страницу 122 книги [TeX: The Program](https://www.amazon.co.uk/Computers-Typesetting-TeX-Program-TEX/dp/0201134373)) Кнут описывает список токенов следующим образом:

> «Список токенов — это односвязный список узлов по одному слову в mem, где каждое слово содержит токен и ссылку. Определения макросов, определения процедур вывода, метки, `\write` тексты и ещё несколько других вещей TeX запоминает в виде списков токенов, обычно им предшествует узел со счётчиком ссылок в его поле «token\_ref\_count».»

При первом прочтении это, возможно, было не так легко понять, но, надеюсь, теперь это стало немного понятнее.


---

# 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/54-what-is-a-tex-token-list.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.
