
Когда процессор ввода встречает два символа с кодом категории 5 подряд (другими словами, пустую строку), он вставляет макрос \par
.
Когда \vbox{Abc.}
заканчивается, TeX завершает текущий абзац, но не вставкой макроса \par
. Похоже, что TeX вставляет \par
примитив вместо этого. Правильно ли я понимаю, что TeX делает в этом случае? А как насчет других мест, куда TeX вставляет \par
? Когда это макрос \par
, а когда примитив?
\catcode`@=11
\let\@@par\par
\def\par{\typeout{Macro!}\@@par}
Abc.
\vbox{Abc.\tracingall}
решение1
В программе TeX есть ровно 7 мест, где TeX выполняет внутренний конструктор абзацев, т.е. превращает горизонтальный список (если он находится в процессе построения) в абзац. Это происходитнетпутем вставки \par
токена во входной поток, но путем выполнения процедурыконец_графареализовано в модуле §1096 в программе TeX.
Эта процедура ничего не делает, если TeX не находится в горизонтальном режиме, и практически ничего, если он находится в горизонтальном режиме, но с пустым списком (пустые абзацы игнорируются TeX), и выполняет процедуруРазрыв строкив противном случае (и это делает всю магию, добавляя штрафы за прогулы и т. д.).
Семь мест:
- в конце внутренних вертикальных структур, таких как закрывающая скобка,
\vbox
но также\noalign
или\vcenter
или ячейка выравнивания - когда примитивныйpar_endощущается (что изначально доступно на макроуровне как значение токена
\par
) - сразу после завершения процедуры вывода (OR) (таким образом, любой горизонтальный список, начатый в OR, не будет продолжаться материалом из гранок, а сформирует отдельный абзац)
Ни в одном из этих случаев не вставляется \par
токен (который может подлежать переопределению); вместо этогоконец_графапроцедура выполнена!
\par
Токены вставляются только в горизонтальном режиме и встречаются примитивы, несовместимые с горизонтальным режимом (например \vskip
, , , ... полный список в §1094 в коде TeX). И, конечно, в процессе токенизации, когда TeX заменяет два символа конца строки на (т. е. делая пустые строки эквивалентными ).\hrule
\par
\par
решение2
В соответствии сTeXbook, правило для окончания строк следующее
Если TeX видит символ конца строки (категория 5), он отбрасывает любую другую информацию, которая может остаться на текущей строке. Затем, если TeX находится в состоянииН(новая строка), символ конца строки преобразуется в токен управляющей последовательности
\par
(конец абзаца); если TeX находится в состоянииМ(середина строки), символ конца строки преобразуется в токен для символа 32 ( ) категории 10 (пробел); и если TeX находится в состоянииС(пропуская пробелы), символ конца строки просто опускается.
Здесь важно отметить, что TeX вставляет токен \par
, а не \par
примитив. Это означает, что это \par
должно быть определено при любых обстоятельствах. Кнут иллюстрирует этот момент, обсуждая команды, которые принудительно включают горизонтальный режим:
Появление <вертикальная команда> в ограниченном горизонтальном режиме запрещено, но в обычном горизонтальном режиме это заставляет TeX вставить токен
\par
во входные данные; после прочтения и расширения этого\par
токена TeX увидит <вертикальная команда\par
> токен снова. ( Будет использоваться текущее значение управляющей последовательности ;\par
может больше не соответствовать примитиву TeX\par
.)
Теперь, то, что, кажется, нигде не упоминается, это то, что происходит в конце внутреннего вертикального режима. Очевидно, что приведенные выше правила не позволяют вставлять токен, \par
поскольку нет маркеров конца строки, с которыми нужно иметь дело. (Вы получаете вставленный, \par
если между последним материалом в \vbox
и концом поля есть пустая строка.)
При чтении выходных данных трассировки нет упоминания о \par
примитиве в конце поля, но shipout показывает
....\penalty 10000
....\glue(\parfillskip) 0.0 plus 1.0fil
и, конечно, там явно построен абзац. Поэтому я пришел к выводу, что конец внутреннего вертикального режима неявно вставляет примитив \par
, и таким образом вставляет обычный материал конца абзаца, а затем запускает конструктор абзаца.
решение3
Есть причина, по которой TeX помещает «первоначальное значение» \par
в конец a \vbox
: рассмотрим следующий глупый ввод
\vbox{\let\par\empty a}
Когда TeX находит }
, он создает его резервную копию, завершает горизонтальный режим, вставляя «оригинал \par
», а затем перечитывает , }
который закрывает внутренний вертикальный режим.
Мы можем смоделировать, что произойдет, если TeX вставит текущее значение \par
перед повторным чтением }
by
\vbox{\let\par\empty a\vskip0pt}
потому что \vskip
вставляет (текущее) \par
и TeX перечитывает \vskip
. Результат — бесконечный цикл!
(Найдено вДискуссия 1993 года по темеcomp.text.tex
.)