> 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/uk/dokladni-statti/09-an-introduction-to-luatex-part-2-understanding-directlua.md).

# Вступ до LuaTeX (Частина 2): розуміння \directlua

## Мета цієї статті

У першій частині цієї статті, [Вступ до LuaTeX (Частина 1): Що це таке — і що робить його таким відмінним?](/latex/uk/dokladni-statti/07-an-introduction-to-luatex-part-1-what-is-it-and-what-makes-it-so-different.md), ми коротко оглянули LuaTeX як надзвичайно універсальний рушій TeX: складну програмовану систему верстки, яка надає широкий набір інструментів для побудови рішень для інженерії та виробництва документів.

У цій завершальній частині ми уважно розглянемо найважливіший компонент набору інструментів LuaTeX:  `\directlua` команду, яка забезпечує «ворота» до програмного керування версткою LuaTeX через мову сценаріїв Lua.

Однак повноцінне використання LuaTeX через  `\directlua` потребує певних базових знань із кількох тем TeX: токенів TeX, списків токенів і механізму розгортання. Мета цієї статті — дослідити й пояснити ці фундаментальні концепції TeX: поєднати процеси, пов’язані з TeX, що стоять за  `\directlua` щоб виробити розуміння того, як це працює, і надати основу, на якій можна будувати власні рішення для верстки за допомогою LuaTeX.

Ця стаття містить численні короткі приклади, щоб продемонструвати й пояснити ключові аспекти  `\directlua`його поведінки, навмисно уникаючи надто складного коду на користь коротких фрагментів коду. За потреби приклади використовують базовий (raw/plain) TeX — хоча більшість людей використовують і надають перевагу LaTeX (макросам), базові команди TeX мають перевагу простоти.

## Вступ до Lua в LuaTeX

[Lua](https://www.lua.org/about.html) — це мова сценаріїв, чий [вихідний код](https://www.lua.org/download.html) є дуже портативним і легко вбудовується в програмні застосунки, дозволяючи розробникам інтегрувати можливості сценаріїв у свої програми. Lua було вбудовано в  [багато застосунків](https://en.wikipedia.org/wiki/List_of_applications_using_Lua) і є популярним вибором у індустрії програмних ігор — ймовірно, найвідоміший приклад — це  [World of Warcraft](https://wowwiki.fandom.com/wiki/Lua_functions).

LuaTeX, як випливає з назви, — це рушій TeX, який вбудовує мову сценаріїв Lua, надаючи користувачам можливість керувати поведінкою верстки LuaTeX, додаючи програми Lua (сценарії) до своїх документів. Окрім безпосереднього керування LuaTeX, користувачі можуть використовувати Lua лише як дуже потужну мову програмування для виконання завдань, яких може бути надзвичайно важко досягти за допомогою мови TeX — яка, за будь-якою об’єктивною міркою, є складною для вивчення й опанування. Завдяки додаванню та інтеграції Lua, LuaTeX стає дуже універсальним і потужним рушієм TeX, який безпосередньо підтримує дві мови програмування.

### Використання Lua і TeX у вашому документі: введіть \directlua

Lua і TeX — це дві *дуже різні* мови програмування: Lua набагато ближча до того, що більшість людей вважає мовою програмування, але TeX із його кодами категорій, токенами, макросами та механізмом розгортання дуже далекий від досвіду/очікувань більшості людей щодо мови, якою пишуть програми. Однак, як показала історія, мова TeX витримала випробування часом, бо вона добре виконує те, для чого була створена: керування версткою, навіть якщо спосіб її роботи дещо таємничий.

Щоб розв’язати проблему змішування мов Lua і TeX в одному документі TeX, розробники LuaTeX запровадили нову команду під назвою  `\directlua` яка є шляхом до використання Lua — і як самостійної мови програмування, і для керування поведінкою верстки LuaTeX.

Функція `\directlua` команда дозволяє користувачам вбудовувати код Lua у свої документи TeX; згодом цей код передається вбудованому інтерпретатору мови Lua у LuaTeX. Однак  `\directlua` також дозволяє вам  *поєднувати* код Lua і (La)TeX разом, в межах тієї самої  `\directlua` команди — хоча це створює додаткові складнощі через фундаментальні відмінності між мовами програмування на основі Lua і TeX. Головна проблема під час використання комбінації коду (La)TeX і Lua полягає в тому, щоб ці дві мови мирно співіснували й не «ставали одна одній на заваді».

`\directlua` найкраще підходить для коротших фрагментів коду Lua в документі, але за бажанням ви можете використовувати її і з більш об’ємними програмами Lua. Загалом, більші програми Lua та бібліотеки коду Lua зберігаються у зовнішніх файлах, які можна завантажувати за допомогою функції Lua  `dofile()`  у  `\directlua` команді. З погляду обробки TeX суттєвою перевагою використання зовнішніх файлів коду Lua є уникнення ускладнень, що виникають через механізм кодів категорій TeX — тема, яку в цій статті розглянуто повністю.

### Більш формальний опис \directlua

Функція [Довідник LuaTeX](http://www.pragma-ade.com/general/manuals/luatex.pdf) описує `\directlua` це так (із незначними змінами):

> Щоб об’єднати код Lua з введенням TeX, потрібні кілька нових примітивів. Примітив  `\directlua` використовується для негайного виконання коду Lua. Базовий синтаксис такий: `\directlua{⟨code⟩}`. `⟨code⟩` повністю розгортається, а потім передається інтерпретатору Lua. Після читання та застосування розгортання до  `⟨code⟩`, отриманий список токенів перетворюється на рядок, ніби його було відображено за допомогою  `\the\toks`.

Звісно, це технічно точно, але, можливо, не так легко зрозуміти без деяких знань про низькорівневі процеси TeX — такі як токени та розгортання.

## Розуміння \directlua: Які теми ми розглянемо?

У цій статті ми детальніше розглянемо деякі ключові базові теми та наведемо низку прикладів, покликаних продемонструвати, як  `\directlua` працює і де (або чому) слід бути обережним, коли поєднуєте TeX і Lua у своєму  `⟨code⟩`.

Ми дослідимо такі теми з достатньою докладністю, щоб закласти основу для розуміння  `\directlua` та його «попередньої обробки» коду, який ви використовуєте всередині нього:

* коди категорій і токени TeX: перетворення тексту на токени та токенів на текст;
* процес розгортання TeX (і запобігання розгортанню);
* механізми/послідовності escape-символів Lua для символів і рядків;
* використання коментарів у стилі Lua;
* короткий вступ до Lua API LuaTeX.

Якщо ви розумієте, як рушії TeX створюють і використовують токени, та усвідомлюєте механізм розгортання TeX, тоді ви матимете основу, необхідну, щоб розкрити неймовірну універсальність  `\directlua` команду.

## Основи: від тексту до токенів і від токенів до тексту

Overleaf опублікував кілька статей, які детально розглядають токени TeX та пов’язані концепції, тож ми не повторюватимемо весь цей матеріал тут; натомість ми окреслимо ті сфери/теми, що мають значення для кращого розуміння  `\directlua`.

Ось список раніше опублікованих статей, які можуть вас зацікавити:

* [Що таке токен TeX?](/latex/uk/dokladni-statti/53-what-is-a-tex-token.md)
* [Що таке список токенів TeX?](/latex/uk/dokladni-statti/54-what-is-a-tex-token-list.md)
* [Як працює \expandafter: Вступ до токенів TeX](/latex/uk/dokladni-statti/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md)
* [Шестичастинна серія: Як насправді працюють макроси TeX?](/latex/uk/inshi-temi/01-a-six-part-series-how-do-tex-macros-actually-work.md)

### Розуміння символьних токенів

Будь-який символ, який рушій TeX може прочитати з текстового файлу, подається двома числовими значеннями:

* його *код символу* (значення ASCII або, сьогодні, його кодова точка Unicode);
* друге, орієнтоване на TeX, значення, яке називається його *код категорії*.

Читачі, які хотіли б дізнатися більше про коди категорій, можуть зацікавитися цим вступом, опублікованим Overleaf: [То з чого почати? З кодів категорій](/latex/uk/inshi-temi/19-how-tex-macros-actually-work-part-1.md#so2c-where-do-we-start3f-with-category-codes).

Наприклад, якщо рушій TeX зчитує символ  `Скопійований` він матиме доступ до двох частин інформації: `Скопійований`код символу (65) і код категорії (зазвичай 11). Після того як TeX ввів цей символ  `Скопійований`, код його категорії не буде змінено, але макроси користувача можуть змінювати коди категорій, що може вплинути на будь-які  *наступні* символа `Скопійований` , які *ще не були прочитані* TeX. Отже, TeX потрібно зафіксувати, що  *цей* символа `Скопійований`, *щойно прочитаний*, має код категорії 11. Для цього TeX використовує пару цілих чисел (65,11), щоб обчислити ще одне ціле значення, яке він називає  *символьним токеном*. Обчислюючи це значення токена, яке передається до внутрішньої обробки TeX, цей конкретний  `Скопійований` і його код категорії *зв’язуються разом*; фактично, цей символьний токен  *містить у собі* дані, які TeX має знати про цей символ для використання в будь-яких подальших діях верстки глибше всередині рушія TeX.

#### Як обчислюються символьні токени?

По-перше, нам потрібно пам’ятати, що рушії TeX використовують код категорії 13 для створення так званих  *активних символів*: будь-який символ із кодом категорії 13 поводиться як міні-макрос; отже, і, як ми побачимо нижче, токени для активних символів обчислюються інакше, ніж для звичайних символів з іншими кодами категорій, такими як 10, 11 або 12.

Для *неактивні* символи:

* старі 8-бітні рушії (TeX Кнута, e-TeX, pdfTeX) обчислюють символьні токени для  *неактивні* символів, використовуючи&#x20;

$$\text{(non-active) character token} = (256 \times \text{category code}) + (\text{ASCII character code})$$

* для LuaTeX, який має справу зі значеннями символів Unicode, обчислення для  *неактивні* символів подібне, але дає значно більші цілі значення:

$$\text{(non-active) character token} = (2^{21} \times \text{category code}) + (\text{Unicode value})$$

Повертаючись до нашого попереднього прикладу для літери A з кодом категорії 11, LuaTeX обчислив би значення символьного токена  $$2^{21} \times 11 + 65 = 23068737$$. Після обчислення це значення символьного токена  *зв’язує* цю конкретну літеру A з кодом категорії 11. Макроси користувача можуть змінити код категорії для будь-якої наступної літери A, але код категорії цієї вже зафіксовано шляхом перетворення її на токен для використання під час проходження через внутрішні механізми LuaTeX. LuaTeX зберіг, або вмістив, задумане значення цього символу, визначене в момент його читання.

Рушії TeX використовують загалом  [16 різних кодів категорій](/latex/uk/inshi-temi/43-table-of-tex-category-codes.md) та *будь-якому* з цих кодів категорій можна призначити, через  `\catcode` команду,  *будь-якому* будь-якому символу, який рушій TeX здатен прочитати. Зміни кодів категорій використовуються, щоб змінювати спосіб, у який рушії TeX обробляють певні символи у вхідних даних, дозволяючи користувачам TeX писати макроси, які дають спеціальні результати верстки або поведінки.

**Активні символи**

Як зазначалося, рушії TeX використовують код категорії 13, щоб надати символу «особливого значення», роблячи його так званим  *активним символом* який поводиться як міні-макрос: жоден початковий  `\` не потрібен, ізольованого символу завдяки його коду категорії достатньо, щоб запустити його макросоподібну поведінку.

Оскільки активний символ діє як міні-макрос, його не перетворюють на  *символьним токеном* , а на другий (цілочисельний) тип токена, який називається  *токеном команди*. Вони обчислюються так:

* для старіших 8-бітних рушіїв (TeX Кнута, e-TeX, pdfTeX) токени для активних символів обчислюються так:

1. обчислити проміжне значення, яке називається $$\text{curcs}$$ (**поточ**ний **c**послідов **ність**) де $$\text{curcs} = \text{character code} + 1$$3. обчислити значення токена, де $$\text{active character token} = \text{curcs} + \text{4095}$$

* для LuaTeX обчислення трохи складніше, оскільки йому потрібно працювати з повним діапазоном символів Unicode, будь-який з яких може бути зроблений активним:

1. обчислити проміжне ціле значення $$\text{curcs}$$ за допомогою так званої *хеш-функції* до значення кодової точки Unicode активного символу, поданого в UTF-8: $$\text{curcs}=\texttt{hashfunction}\text{(UTF-8 text for Unicode value of active character)}$$3. обчислити цілочисельне значення токена: $$\text{active character token} = \text{curcs} + 2^{29} - 1$$

**Приклади**

* 8-бітні рушії: обчислення токена для активного символу  `~` (код символу 126) дає  $$\text{curcs} = 126 + 1 = 127$$, що дає значення токена  $$4095 + 127 = 4222$$.
* LuaTeX: обчислення токена для активного символу  `~` дає  $$\text{curcs}=3186$$ що дає значення токена  $$3186 + 2^{29} - 1 = 536874097$$. Токени LuaTeX використовують значно більші цілі значення!

### Розуміння токенів команд

Окрім обробки  *окремих* символів, рушії TeX, звісно, можуть обробляти  *послідовності* символів, які називаються  *команди* (або, точніше,  *послідовностями керування*). За традицією, символ  `\` використовується, щоб позначити початок команди, але це лише умовність — насправді замість нього можна використати будь-який символ із кодом категорії 0 (символом екранування).

Рушії TeX розпізнають два типи команд, відомі як  *керуючі слова* та *керуючі символи*:

* **керуючі слова**: команди, побудовані з одного або кількох символів, що мають код категорії 11;
* **керуючі символи**: односимвольні команди, де код категорії цього символу *не* 11: такі як `\$`, `\#` або `\\`.

**Примітка**: Примітиви TeX `\chardef`, `\mathchardef`, `\countdef`, `\dimendef`, `\skipdef`, `\muskipdef` та `\toksdef` також використовуються для визначення послідовностей керування, але, на відміну від звичайних макровизначень, отримані послідовності керування (керуючі слова або керуючі символи)  *не є розгортними*— ми розглянемо їх докладніше нижче.

#### Як обчислюються токени команд?

Так само, як і активні символи, рушії TeX використовують другий тип цілочисельного значення токена для представлення команд: *токени команд*— згадайте, що активні символи також генерують токени команд, оскільки вони поводяться як міні-макроси.

Обчислення, які 8-бітні рушії використовують для створення цілих чисел токенів команд, можна знайти в цій  [статті Overleaf](/latex/uk/dokladni-statti/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md#how-tex-calculates-token-values). Тут ми підсумуємо ключові кроки обчислення токенів команд для LuaTeX — які дещо відрізняються, оскільки LuaTeX має обробляти значення кодів символів Unicode, які можуть бути значно більшими за 8-бітні значення; однак обчислення LuaTeX дотримуються тих самих загальних принципів, що й у старіших 8-бітних рушіїв.

Після виявлення вхідної команди рушії TeX, зокрема LuaTeX, ігнорують початковий  `\` символ: він не використовується в обчисленнях значень токена команди, а лише слугує «перемикачем», щоб повідомити рушію TeX, що йому потрібно обробити команду. Значення токена команди обчислюється за послідовністю з (одного або кількох) символів, присутніх у назві команди — LuaTeX обчислює токени команд для керуючих символів і керуючих слів за тим самим алгоритмом:

1. обчислити проміжне ціле значення $$\text{curcs}$$ за допомогою так званої [хеш-функції](https://en.wikipedia.org/wiki/Hash_function) до рядка Unicode UTF-8, що містить символи в назві команди: $$\text{curcs}=\texttt{hashfunction}\text{(Unicode UTF-8 string of characters in command name)}$$3. обчислити значення токена команди, де $$\text{command token} = \text{curcs} + 2^{29} - 1$$

**Приклади**

* для `\\` команди (керуючого символу), LuaTeX обчислює $$\text{curcs}=94$$, у результаті чого отримує значення токена для `\\` значення $$94 + 2^{29} - 1 = 536871005$$.
* для `\vskip` примітивної команди (керуючого слова) LuaTeX обчислює $$\text{curcs}=3560$$, у результаті чого отримує значення токена для `\vskip` значення $$3560 + 2^{29} -1 = 536874471$$.
* для макроса, визначеного користувачем `\mynewmacro` (керуючого слова) LuaTeX обчислює $$\text{curcs} = 2971$$, у результаті чого отримує значення токена для `\mynewmacro` значення $$2971 + 2^{29} -1 = 536873882$$.

Після створення токени можуть зберігатися для подальшого використання у так званих  *списках токенів* або можуть бути негайно передані далі для подальшої обробки всередині рушія TeX. Використання цілих чисел для представлення токенів не лише працює на всіх типах обчислювальних платформ/операційних систем, але й є дуже ефективним способом для TeX зберігати/обробляти дані.

### Як рушій TeX визначає тип токена (команда чи символ)

Маючи певне ціле значення токена,  $$T$$рушій TeX може легко визначити, чи  $$T$$ є командою чи символом, перевіряючи, чи  $$T$$ перевищує певний  $$\text{threshold value}$$ — це  $$\text{threshold value}$$ залежить від рушія TeX. Якщо  $$T \geq \text{threshold value}$$ то $$T$$ це токен команди, інакше  $$T$$ це символьний токен.  $$\text{threshold value}$$ є $$4095$$ для 8-бітних рушіїв і  $$2^{29}-1$$ (536,870,911) для LuaTeX. Кнут розробив методи, які використовуються у формулах обчислення токенів, так, щоб його рушій TeX і всі наступні рушії, засновані на його коді/архітектурі, могли швидко й легко перевіряти значення токенів.

## Токени можна розібрати на частини (і перетворити назад на текст)

Токени (цілі числа) — це механізм, за допомогою якого рушій TeX «вміщує» все, що йому потрібно записати про елемент вхідних даних (символ чи команду). Однак бувають випадки, коли рушію TeX потрібно виконати зворотний процес токенізації — з’ясувати, що було спочатку прочитано для створення цього значення токена — окремий символ або послідовність з одного чи кількох символів, що утворюють назву команди:

* **для символьних токенів**: Будь-який символьний токен можна розділити на дві складові: код символу і відповідний код категорії, призначений цьому символу *у момент, коли його було спочатку прочитано*. Як і всі рушії TeX, LuaTeX не змінюватиме це початкове призначення коду категорії, але використовуватиме його під час подальшої внутрішньої обробки.
* **для токенів команд:** Вони трохи докладніші, але якщо подивитися на обчислення токенів команд у LuaTeX, зокрема токенів для активних символів, видно, що вони мають певний шаблон: $$\text{command token} = \text{curcs} + 2^{29} -1$$

де $$\text{curcs}$$ обчислюється залежно від типу токена команди, що генерується: активний символ, керуючий символ або керуюче слово.  $$\text{curcs}$$ змінна  *надзвичайно* є $$\text{curcs}$$ важливим компонентом внутрішньої роботи рушія TeX: маючи будь-яке ціле значення токена команди, LuaTeX може дуже легко витягнути значення  $$\text{curcs} = \text{token value} - (2^{29} -1)$$.

### Чому  $$\text{curcs}$$ таке важливе?

Внутрішня змінна TeX  $$\text{curcs}$$ (**поточ**ний **c**послідов **ність**послідовність) є надзвичайно важливим компонентом внутрішніх, «підкапотних» операцій рушія TeX. Хоча ви не будете і не можете використовувати або отримувати до неї доступ безпосередньо у своєму коді,  $$\text{curcs}$$ відіграє вирішальну роль, оскільки рушії TeX використовують поточне значення  $$\text{curcs}$$ як індекс у внутрішніх таблицях, які зберігають дані про кожну команду, відому рушію на даний момент. Ці таблиці зберігають інформацію про поточне значення команди: що вона робить або означає, а також записують послідовність символів, спочатку використаних для обчислення цього  $$\text{curcs}$$ значення. Витягуючи значення  $$\text{curcs}$$ з токена команди, рушій TeX може визначити назву, тобто текст, зрозумілий людині, що відповідає будь-якому (командному) токену, дозволяючи виконувати перетворення токена в текст, які є ключовим аспектом  `\directlua`його роботи.

### Перетворення цілих токенів назад на символи або послідовності символів (назви команд)

Ми бачили, що рушії TeX перетворюють вхідні символи або послідовності символів на цілі значення токенів, але бувають випадки, коли рушію TeX потрібно  *повернути* цей процес — щоб вивести текст, зрозумілий людині, який спочатку використовувався для створення цих цілих значень токенів; наприклад:

* записуючи повідомлення про помилки або попередження на екран або `.log` файл;
* виводячи код TeX/LaTeX у текстовий файл через  `\write` команди;
* коли перетворюємо послідовність токенів на текст у  `\directlua` (як ми незабаром побачимо!)

#### Перетворення символьних токенів на текст

Як зазначалося, токени для неактивних символів обчислюються з використанням коду категорії та коду символу (значення Unicode) вхідного символу. LuaTeX використовує формулу:

$$\text{character token} = (2^{21} \times \text{category code}) + (\text{Unicode value})$$

Це нескладне програмне завдання — розділити ціле  $$\text{character token}$$ значення, щоб отримати його складову — код символу ($$\text{Unicode value}$$) і $$\text{category code}$$.

#### Перетворення токенів команд на текст

Усі рушії TeX зберігають назву (послідовність символів) кожної команди, про яку вони «знають»: чи то команда, визначена користувачем як макрос, чи вбудований примітив — зберігання назв примітивних команд відбувається під час запуску рушія TeX, задовго до того, як він починає обробляти ваш код. Для команд, визначених користувачем (макросів), назва цього макроса (без початкового  `\`) зберігається як частина процесів визначення макросів всередині рушіїв TeX.

Коли рушію TeX потрібно отримати доступ до людськозрозумілого тексту, з якого спочатку було обчислено ціле значення токена команди, або вивести його, він спочатку визначить  $$\text{curcs}$$ значення для цього токена; у LuaTeX,  $$\text{curcs} = \text{token} - (2^{29} -1$$). Використовуючи значення  $$\text{curcs}$$ витягнуте з токена команди, рушій TeX може отримати доступ до внутрішньої структури даних, яка називається  *пулом рядків* щоб визначити послідовність символів, зрозумілих людині, які спочатку використовувалися для обчислення цього конкретного значення  $$\text{curcs}$$ і, відповідно, відповідного токена команди.

Як ми побачимо, ці дії з обробки токенів — перетворення послідовностей символів на цілі значення токенів і перетворення цілих значень токенів назад у послідовності символів («де-токенізація») — є  *фундаментальними механізмами* , які використовуються всередині  `\directlua`.

## Списки токенів

Коли рушій TeX читає вхідні дані, генеруючи символьні та командні токени (і обробляючи їх), він може натрапити на певні команди, які наказують рушію (тимчасово) припинити передавання токенів далі для подальшої обробки, а натомість зберегти їх для подальшого використання. Найпоширеніший приклад — визначення макроса за допомогою однієї з команд визначення макросів  `\def`, `\edef`, `\gdef` або `\xdef`— команди LaTeX, такі як  `\newcommand` , є макросами, які надають додаткову функціональність, побудовану на основі низькорівневих примітивів, які зрештою виконують фактичний процес визначення макроса. Макрос можна вважати назвою, наданою певному списку збережених токенів: списку токенів.

Рушії TeX широко *використовують* списки токенів, особливо  [тимчасові внутрішні списки](/latex/uk/dokladni-statti/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md) , які використовуються для внутрішньої обробки. Кожен рушій TeX також надає команди рівня користувача для створення списків токенів, які зберігаються на потім, коли вони знадобляться користувачеві або самому рушію TeX. Кількість команд створення списків токенів (вбудованих примітивів) залежить від рушія TeX, але всі вони мають спільний мінімальний набір, підтримуваний кожним рушієм, наприклад  `\toks` примітив.

На практиці список токенів — це просто збережена послідовність цілих значень:

* вхідні дані зчитуються, щоб генерувати (обчислювати) окремі токени, що представляють символ або команду;
* потім кожен токен зберігається, зберігаючи послідовність, у якій токени були згенеровані з вхідних даних.

Рушії TeX зберігають списки токенів за допомогою структури даних, яка називається  [зв'язаний список](https://en.wikipedia.org/wiki/Linked_list) (однозв’язного типу). Читачів, які хочуть дізнатися більше про списки токенів, запрошуємо прочитати статтю Overleaf  [Що таке список токенів TeX?](/latex/uk/dokladni-statti/54-what-is-a-tex-token-list.md) яка використовує аналогію для побудови понять/ідей, що стоять за списком токенів. Глибоке дослідження списків токенів TeX і того, як вони використовуються в обробці макросів, можна знайти в серії статей Overleaf  [Як насправді працюють макроси TeX?](/latex/uk/inshi-temi/01-a-six-part-series-how-do-tex-macros-actually-work.md)

#### Список токенів у графічній формі

Наступна графіка показує список токенів, згенерований LuaTeX, із відповідними значеннями токенів, отриманими з такого вхідного&#x20;

`Hi, \TeX! \hskip 5bp`

Наприклад, якщо ми визначимо `\mymacro` як `\def\mymacro{Hi, \TeX! \hskip 5bp}` визначення  `\mymacro` було б збережене в пам’яті за допомогою такого списку токенів:

![](/files/537136f6522f8565f8ece6f743c01254ee8b3296)

Список токенів — це послідовність зв’язаних елементів, які називаються  *вузлів*, назва невеликого пакета пам’яті LuaTeX, виділеного для збереження кожного елемента списку (як окремі ланки в ланцюжку). Кожен вузол містить ціле значення токена і адресу пам’яті  *наступного* вузла в ланцюжку, утворюючи структуру даних, яка називається  [зв'язаний список](https://en.wikipedia.org/wiki/Linked_list). Останній вузол позначає кінець списку, використовуючи спеціальне «нульове значення» для наступного вузла — бо його не існує.

**Примітки:**

* Для зручності ми включили адресу кожного окремого вузла, але на практиці ці дані не зберігаються у вузлах списку токенів; потрібна лише адреса  *наступний вузол* необхідна для побудови списків токенів рушія TeX.
* Другий стовпець у графіці під назвою «Що означає кожен токен» показує серію сірих блоків, що містять інформацію про токен, що міститься в кожному вузлі: це суто інформаційні дані і  *не* не є

Ось таблиця значень токенів, що містяться у списку токенів, зображеному вище:

|                      |                    |                                                                             |                     |
| -------------------- | ------------------ | --------------------------------------------------------------------------- | ------------------- |
| **Елемент введення** | **Тип введення**   | <p><strong>Код категорії</strong><br><br><strong>(якщо символ)</strong></p> | **Значення токена** |
| H                    | символа            | 11                                                                          | 23068744            |
| i                    | символа            | 11                                                                          | 23068777            |
| ,                    | символа            | 12                                                                          | 25165868            |
|                      | символа            | 10                                                                          | 20971552            |
| \TeX                 | команда (макрос)   |                                                                             | 536871539           |
| !                    | символа            | 12                                                                          | 25165857            |
|                      | символа            | 10                                                                          | 20971552            |
| \hskip               | команда (примітив) |                                                                             | 536874247           |
| 5                    | символа            | 12                                                                          | 25165877            |
| b                    | символа            | 11                                                                          | 23068770            |
| p                    | символа            | 11                                                                          | 23068784            |

**Примітка:** Наш початковий вхідний текст має a після  `\hskip` команди, але в списку токенів немає токена, що представляв би цей символ. Цей символ був поглинутий процесом сканування (читання) вхідних даних LuaTeX, тому що він був використаний для завершення пошуку LuaTeX символів, що утворюють  `\hskip` команду.

## Як насправді працює \directlua

Тепер, коли ми розглянули токени, списки токенів і перетворення токенів у текст, наступне завдання — зрозуміти поняття токена в рушії TeX *розширення*.

Як зазначалося, `\directlua{⟨code⟩}` можна попросити обробити `⟨code⟩` який містить і код Lua, і код TeX/LaTeX, але вбудований інтерпретатор мови Lua в LuaTeX не розуміє TeX або LaTeX: тож як це може працювати? Як можливо, щоб `⟨code⟩` містив інструкції TeX/LaTeX, не вводячи інтерпретатор Lua в повну плутанину командами, яких він не розуміє? Наприклад, така `\directlua` команда використовує лише макроси TeX, але вона працює:

```
\def\aa{tex}
\def\bb{.}
\def\cc{print}
\def\dd{("Hello")}
\directlua{
   \aa\bb\cc\dd
}
```

Цей `\directlua` команда приводить до набору в LuaTeX `Привіт` але чому і як це працює, якщо мова Lua не розуміє макросів TeX?

Відповідь міститься в попередньому описі, який ми запозичили з [Довідник LuaTeX](http://www.pragma-ade.com/general/manuals/luatex.pdf) але ми можемо вважати, що `\directlua{⟨code⟩}` працює завдяки тому, що LuaTeX спочатку «попередньо обробляє» `⟨code⟩` перед тим, як будь-що передається інтерпретатору Lua. Суть цієї «попередньої обробки» — тобто що вона насправді означає і які її наслідки для вашого `⟨code⟩`— це наступна тема, яку ми розглянемо, щоб допомогти читачам, зацікавленим у використанні потужності та гнучкості `\directlua`.

### Як LuaTeX обробляє \directlua: перший погляд

Щоб розширити наше розуміння `\directlua`«попередньої обробки» ми можемо почати з такої спрощеної діаграми, яка дає огляд того, що відбувається.  `⟨code⟩` наданий для `\directlua{⟨code⟩}` спочатку перетворюється на токени за допомогою процесів і обчислень, обговорених вище; ця послідовність токенів зберігається в списку токенів. Після того як цей список токенів побудовано, кожен токен у ньому знову перетворюється на текстове подання: текст, створений кожним токеном — символьним токеном або токеном-командою — об’єднується (конкатенується), утворюючи один рядок коду, який передається інтерпретатору Lua для виконання.

![](/files/30c9590d0c44517f1e668d8d048849f61df2f771)

Але зачекайте, який сенс переходити від тексту до токенів і одразу ж перетворювати ці токени назад у текст? Вас, мабуть, не здивує, що так, тут є ще один важливий процес, який ми не включили до цієї схеми: *розширення токенів*. Кожен токен, отриманий із тексту у вашому `⟨code⟩` підлягає своєрідній «перевірці», під час якої LuaTeX застосовує тест, щоб визначити, чи є цей токен командою, що належить до підмножини, відомої як *розширювані команди*. Якщо так, LuaTeX відфільтровує цю команду, *видалення* видаляючи її з вашого `⟨code⟩` та *замінивши її* результатом(ами) процесу, який рушії TeX називають *розширення токенів*.

### Як LuaTeX обробляє \directlua: другий погляд (на розширення)

Механізм розширення TeX є основним компонентом усіх рушіїв набору на основі TeX, оскільки, зрештою, кожен із них походить від (або базується на) оригінальному вихідному коді й дизайні TeX Кнута. Однак поняття розширення важко пояснити стислою, але доступною мовою, тому що на практиці expansion — це «парасольковий» термін, який використовується для опису одного процесу, але такого, що породжує цілий спектр результатів. Ці різні наслідки є результатом дещо еклектичного набору команд, до яких можна застосовувати розширення, тож можна вважати, що кожна розширювана команда має певну «поведінку розширення».

Як *першого наближення* для розуміння розширення можна сказати, що розширення токена (команди) означає *видалення* вилучення цієї команди (токена) з поточного вхідного потоку TeX і *заміщення* заміщення її послідовністю токенів, що виникають унаслідок виконання цієї конкретної розширюваної команди, — тобто заміна початкового токена результатами/наслідками її розширення *поведінки*. Однак це початкове «визначення» розширення — як створення нових токенів, які TeX має прочитати, — не є цілком точним для всіх розширюваних команд, але як відправна точка воно цілком достатнє.

Для простого прикладу: примітив TeX `\jobname` є розширюваною командою, а його *розширення* це послідовність символьних токенів, що представляють ім’я основного вхідного файлу TeX. Якщо TeX вирішує розгорнути `\jobname` команду (токен), вона *видаляється* з поточного джерела введення TeX і *заміщується* послідовністю символьних токенів, яку вона генерує, — після чого TeX читає/обробляє її.

У `\directlua`після того як розширюваний токен оброблено (видалено) і замінено новими токенами, LuaTeX продовжить читати саме ці нові токени, які щойно поставив на їхнє місце, — але деякі з цих нових токенів також можуть бути розширюваними. Оскільки `\directlua` виконує так зване *повне розширення*LuaTeX читатиме ці нові токени і, знову ж таки, проходитиме процес розширення, щоб розгорнути (видалити) будь-які нові (розширювані) токени — цей процес розширення триває доти, доки не залишиться жодного розширюваного токена. Однак є два важливі винятки з цього правила «продовжувати розширення», і обидва ми обговоримо нижче:

* використовуючи конструкцію `\the\toks`;
* навмисного запобігання (придушення) розширення для одного чи кількох вибраних токенів.

Як уже зазначалося, наше робоче визначення (перше наближення) для розуміння розширення не охоплює повного спектра поведінок розширення, демонстрованих підмножиною розширюваних команд. Наприклад, деякі розширювані команди не генерують токени так, як `\jobname` це робить, але вони можуть:

* «фільтрувати» токени з вхідного потоку: умовні команди рушія TeX (`\if`, `\ifcat`, `\ifnum`, `\ifdim`, `\ifodd`, `\ifvmode`, …) є розширюваними. Їхня поведінка розширення — це своєрідне «фільтрування токенів» — умовні конструкції можна використовувати в `\directlua`.
* «перемішувати» токени у вхідному потоці:  [`\expandafter` команду](/latex/uk/dokladni-statti/03-a-six-part-article-series-on-expandafter-tex-tokens-and-expansion.md) є розширюваною і змінює послідовність, у якій розгортаються два токени.
* запобігати розширенню: розширювані команди `\noexpand` та `\unexpanded` пригнічують розширення токенів-команд у вхідному потоці.
* перетворюють послідовності символів у вхідному потоці на токени-команди: `\csname … \endcsname.`
* перетворюють внутрішні величини на послідовність символьних токенів: `\number` та `\the` — це розширювані команди, які генерують послідовність символьних токенів, що представляють значення внутрішньої величини.
* перетворюють токени-команди на символьні токени: `\string` та `\detokenize` — це розширювані команди, які перетворюють свої аргументи на послідовність символьних токенів із кодом категорії 12. Зверніть увагу, що `\detokenize` відрізняється від `\string`: `\detokenize` може обробляти кілька токенів і вводить символ пробілу, з кодом категорії 10, після обробки токенів-команд, створених із *керуючі слова*. Фактично, `\detokenize` додає пробільний символ у кінці після імені команди — ми побачимо кілька прикладів пізніше в статті.

#### Уточнення нашого «визначення» розширення

Тепер ми можемо узагальнити наше визначення і сказати, що розширення команди (токена) передбачає *видалення* цієї команди (токена) з поточного джерела введення TeX і *замінивши її* з результатом *операції(й) з токенами* виконаного цією командою. По суті, процес розширення змушує розширювану команду виконувати певний тип «операції» над токенами в поточному вхідному потоці TeX, що впливає на кількість або поведінку токенів, які TeX згодом прочитає — точна природа цієї «операції» залежить від того, яка саме команда розширюється. Усі макроси й активні символи є розширюваними, але лише невелика кількість вбудованих команд (примітивів) рушія TeX класифікується як розширювані — список розширюваних команд залежить від рушія TeX, яким ви користуєтеся.

Кожен новий рушій TeX успадковує примітивні команди, вбудовані в його предків, — рушії TeX попередніх поколінь, від яких він походить, — і деякі з цих успадкованих примітивів будуть розширюваними. Звісно, новий рушій TeX може вирішити не реалізовувати деякі з примітивних команд, наявних у попередніх рушіях, або змінити їхню поведінку відповідно до потреб нового рушія. Крім того, нові рушії TeX зазвичай реалізують додаткові примітиви, щоб забезпечити підтримку власної розширеної функціональності — деякі з них також можуть бути розширюваними. Відповідно, кількість доступних вам розширюваних команд змінюватиметься залежно від рушія TeX, який ви використовуєте — LuaTeX має їх чималу кількість.

Ще одна складність у поясненні/розумінні розширення, а можливо, й справжній виклик, — це точно знати *коли* чи рушій TeX насправді виконає процес розширення. Це велика й складна тема, оскільки розширення глибоко вбудоване в усі внутрішні механізми рушіїв TeX: у нас немає місця розглядати це детально, окрім використання розширення в `\directlua`.

### Як LuaTeX обробляє \directlua: фінальний погляд

Наведена нижче діаграма підсумовує `\directlua` дії попередньої обробки, що відбуваються всередині самого рушія LuaTeX. На цій діаграмі також показано дві низькорівневі (внутрішні) функції LuaTeX, які фактично виконують роботу: `scan_toks()` та `tokenlist_to_cstring()`. Ці функції написані мовою C і знаходяться глибоко всередині виконуваного програмного забезпечення LuaTeX: вони є частиною внутрішнього механізму LuaTeX і не *безпосереднього* доступні вашому коду TeX/LaTeX.

![](/files/0ae3d9486b3a6b1982fd13b7854cccba3ae1f62d)

Наведений нижче опис `\directlua ⟨code⟩`попередньої обробки \directlua ⟨code⟩ підсумовує наведену вище діаграму.

1. Послідовність символів у вашому ⟨code⟩ обробляється `scan_toks()`. Її мета — читати ваш ⟨code⟩ символ за символом, щоб генерувати символьні токени та токени-команди. Оскільки вона створює токени, код категорії, призначений кожному символу в ⟨code⟩ на момент його зчитування, має надзвичайно важливе значення.
2. Під час `scan_toks()`токенної обробки (генерації) будь-яка розширювана команда (токен) розгортається *якщо тільки* запобігається за допомогою таких команд, як `\protected` (визначення макросів), `\noexpand`, `\unexpanded` тощо. Активні символи (код категорії 13) також розгортаються (якщо цьому не запобігти).
3. Потік токенів, створений `scan_toks()` формується в один довгий список токенів — токени, присутні в цьому списку, включають ті, що утворилися внаслідок застосування розширення до розширюваних команд (таких як макроси), наявних у вашому `⟨code⟩`. Також зауважте, що `scan_toks()` *не* не запускають і не спричиняють виконання жодного токена, що представляє нерозширювану команду: такі нерозширювані токени просто пропускаються далі, щоб бути включеними до списку токенів, який будується.
4. Коли список токенів завершено і вся діяльність із розширення завершена, цей список токенів обробляється іншою функцією під назвою `tokenlist_to_cstring()` яка перетворює кожен токен у фінальному списку токенів назад у його текстове представлення. Це створює рядок тексту, який є кодом Lua, що передається інтерпретатору Lua. Для успішного виконання цей рядок має містити синтаксично правильний код Lua.
5. Обробка цього коду в Lua відбувається у два кроки:
6. Вбудований інтерпретатор Lua в LuaTeX аналізує й «компілює» код Lua, згенерований на попередніх кроках. Якщо цей аналіз/компіляція зазнає невдачі, інтерпретатор Lua згенерує помилки (такі як синтаксичні помилки) — ці помилки можуть спричинити збій запуску LuaTeX, якщо тільки ви не вирішили використовувати `--interaction=nonstopmode` у командному рядку.
7. Якщо аналіз/компіляція успішні, інтерпретатор Lua виконує код, скомпільований на кроці (5a).

По суті, `scan_toks()` функція є основою дій попередньої обробки LuaTeX: її головне завдання — розгорнути всі розширювані команди TeX/LaTeX, що містяться в тексті вашого `⟨code⟩` і побудувати список токенів з усього, що вона обробила. Ще раз наголосимо, що `scan_toks()` *не виконує нерозширювані команди* (токени): вона просто *зберігає* ці токени у створюваному нею списку токенів. Після завершення цей список токенів згодом перетворюється *назад у текстове представлення* за допомогою `tokenlist_to_cstring()`— список токенів є суто TeX-концепцією, яка є цілком чужою для інтерпретатора Lua, тому й виникає потреба перетворити його на текст, що стає кодом Lua для передачі інтерпретатору Lua.

## Розширення як «інтерфейс» між мовами програмування

Можна вважати, `\directlua`що процес розширення використовується як механізм або інтерфейс для передавання даних/інформації з «світу TeX» у «світ Lua»: тобто як спосіб для мови TeX передавати дані мові Lua. Наприклад, код TeX такий як `\number\count75` може бути використаний для передавання значення зі «світу TeX», збереженого в лічильному регістрі 75, до цілочисельної змінної x у «світі Lua»:

```
\count75=1564 % Data existing in the "TeX World"
\directlua{
   local x=\number\count75 \space % Transfer TeX data to the "Lua World"
   tex.print("x= "..x)
   local y = (2*x-65)/5
   tex.print(" and y = "..y)
}
```

Це генерує код Lua

```
 local x=1564 tex.print("x= "..x) local y = (2*x-65)/5 tex.print(" and y = "..y)
```

**Примітка**: Ми додали `<space>\space` після `\number\count75` щоб забезпечити збереження символу пробілу між `1564` та `tex.print`— хоча тут це не є строго необхідним, бо Lua все одно правильно розбере код, навіть якщо ми його опустимо. Символ пробілу, що йде відразу після `\count75` поглинається під час процесу, який рушії TeX використовують для пошуку числових значень — тут це значення, що передається `\count`. Символ пробілу після `75` використовується для завершення пошуку LuaTeX послідовності цифр `75` і поглинається з вхідного потоку.  `\space` макрос розгортається, щоб надати символ пробілу, потрібний для розділення тексту `1564` та `tex.print`.

Використовуючи наведений вище код, LuaTeX набере

`x= 1564 and y = 612.6`

Тут механізм «передавання даних» досягається за допомогою `\number`: розширюваної команди, яка в цьому випадку наказує TeX отримати значення, збережене в `\count` регістрі `75` і з цього значення (`1546`) згенерувати серію символьних токенів, по одному символьному токену на кожну цифру, що дає послідовність токенів для цифр `1`, `5`, `6` та `4`. Ці 4 символьні токени включаються до основного списку токенів, який будується `\directlua` і згодом знову перетворюються на своє текстове представлення, коли список токенів перетворюється на текст. Безумовно, це дуже непрямий шлях від `\count75` значення регістра, збереженого всередині LuaTeX, до цифр, призначених для коду Lua, але, зрештою, це працює.

**ПОРАДА:** Якщо ви хочете перевірити результати дій розширення LuaTeX, можна написати такий код:

```
\directlua{
   local foo=[[local x=\number\count75
   tex.print("x= "..x)
   local y = (2*x-65)/5
   tex.print(" and y = "..y)]],
   print(foo)
}
```

У цьому прикладі ми використовуємо метод довгих дужок, щоб створити рядкову змінну `foo` призначену для зберігання рядка коду Lua, згенерованого шляхом розширення всього, що між `[[` та `]]`. Цей рядок виводиться в консоль через виклик функції Lua `print(foo)`.

В Overleaf ви можете переглянути подібні результати, записавши вміст `foo` до `.log` файлу за допомогою функції Lua LuaTeX `texio.write()`:

```
\directlua{
   local foo=[[local x=\number\count75
   tex.print("x= "..x)
   local y = (2*x-65)/5
   tex.print(" and y = "..y)]],
   texio.write(foo)
}
```

## Токени в списку токенів \directlua: нерозширювані токени та нерозгорнуті токени

Ми вже зазначали, що `\directlua{⟨code⟩}` виконує *повне розширення* вашої `⟨code⟩`: вона видаляє й розгортає всі розширювані команди, доки не залишаться лише нерозширювані токени. Послідовність токенів, створена `\directlua`обробкою (у `scan_toks()` функції), об’єднується, утворюючи список токенів, окремі токени якого буде перетворено назад у текст для передачі в Lua.

Однак ми ще не розглянули завершальну частину цієї історії, оскільки нам потрібно взяти до уваги два «класи» токенів-команд, які можуть потрапити до списку токенів, що будується всередині `\directlua`: ми називатимемо їх *токенами скорочених команд* та *нерозгорнуті* токени:

* **токенами скорочених команд**: Цей тип токена-команди виникає з контрольних послідовностей, визначених за допомогою одного з примітивів TeX `\chardef`, `\mathchardef`, `\countdef`, `\dimendef`, `\skipdef`, `\muskipdef` та `\toksdef`. Ці примітивні команди використовуються для визначення контрольних послідовностей, що представляють числове значення — отримані контрольні послідовності є *не* розширюваними.
* **нерозгорнуті токени**: Цей тип токена виникає з команд, які зазвичай було б розгорнуто, але `\directlua` було або:
* явно наказано *не* їх розгортати; наприклад, придушення розширення командами `\noexpand` або `\unexpanded`— ми незабаром пояснимо, як це робиться;
* вставлено токени шляхом обробки послідовності `\the\toks` (про це нижче).

### Дві «групи» токенів у списку токенів \directlua

На основі нашого обговорення можна сказати, що токени, які містяться у списку токенів, що будується під час першого етапу `\directlua`попередньої обробки (у `scan_toks()` функції), належать до двох груп:

1. *за своєю природою нерозширювані* токени

* будь-який токен, що представляє неактивний *символа*;
* будь-який токен, що представляє нерозширюваний *примітив* *команду*;
* будь-який токен, що представляє *скорочену команду* (вони не є розширюваними, див. нижче).

3. *нерозгорнуті* токени:

* будь-який токен, що представляє розширювану команду, розширення якої було *придушене* (або якого уникнули) під час `\directlua`попередньої обробки.

#### Токени скорочених команд: створення нерозширюваних команд

Як зазначалося, рушії TeX надають набір примітивів (вбудованих команд), які можна використовувати для створення *нерозширюваних* контрольних послідовностей (позначених тут як `⟨command⟩`). Ці примітиви мають вигляд:

* `\chardef ⟨command⟩ = ⟨numeric value⟩`
* `\mathchardef ⟨command⟩ = ⟨numeric value⟩`
* `\countdef ⟨command⟩ = ⟨numeric value⟩`
* `\dimendef ⟨command⟩ = ⟨numeric value⟩`
* `\skipdef ⟨command⟩ = ⟨numeric value⟩`
* `\muskipdef ⟨command⟩ = ⟨numeric value⟩`
* `\toksdef ⟨command⟩ = ⟨numeric value⟩`

де `⟨numeric value⟩` — це деяке ціле значення, відповідне для кожної команди.

Тут ми коротко розглянемо використання `\chardef` щоб продемонструвати ключову особливість цих примітивів — створення `⟨command⟩` яка є нерозширюваною. Ви можете використати ``\chardef\mydollar=`\$`` щоб створити контрольну послідовність `\mydollar` і використовувати її для набору `$`:

`Я заплатив \mydollar30.`

Це набере `Я заплатив $30.` Контрольна послідовність `\mydollar` створена за допомогою `\chardef` не є розширюваною, як видно з наведеного нижче прикладу:

```
\chardef\mydollar=`\$
\directlua{
   local x =[[I paid \mydollar30.]]
   texio.write(x)
}
```

Що дає такий текст у `.log` файлу

`Я заплатив \mydollar 30.`

Це показує, `\mydollar` було *не* розгорнуто під час `\directlua`попередньої обробки. Пробіл, що з’являється після `\mydollar` додається тоді, коли токен-команда перетворюється на своє текстове представлення.

Коли ви використовуєте `\chardef` для створення контрольної послідовності, внутрішня класифікація TeX цієї контрольної послідовності (команди) призводить до того, що вона є *нерозширюваних* що є зовсім іншим поводженням порівняно з контрольними послідовностями, визначеними однією з команд визначення макросів: \def, \edef, \gdef або \xdef. Як зазначалося вище, під час процесу побудови свого списку токенів `\directlua` LuaTeX перевіряє кожен вхідний токен-команду на розширюваність. Якщо токен-команда не є розширюваною, він просто проходить далі до списку токенів, а його текстове представлення згодом знову з’явиться в рядку коду Lua, що утвориться внаслідок перетворення токенів у списку токенів назад у їхню текстову форму.

**Короткі зауваги щодо plain TeX і LaTeX**

Історично оригінальний plain TeX Кнута визначав широко вживані керувальні символи `\%`, `\&`, `\#` та `\$` за допомогою `\chardef`— а не за допомогою однієї зі стандартних команд визначення макросів `\def`, `\edef`, `\gdef` або `\xdef`. Наприклад:

```
   \chardef\#=`\#
   \chardef\$=`\$
   \chardef\%=`\%
   \chardef\&=`\&
```

Дивний `` `\ `` синтаксис \`\\\` є методом TeX для отримання числового значення коду символу. У старому режимі plain TeX ці керувальні символи не є розширюваними (через `\chardef`) але LaTeX (або пакети) можуть перевизначати їх як *макросів* щоб забезпечити розширену функціональність — це зробило б їх розширюваними, тож вам слід це враховувати.

**Як це впливає на \directlua?**

Порівняймо результат виконання наведеного нижче коду в plain TeX і LaTeX. Для простоти ми запишемо результати у `.log` файл за допомогою функції Lua API LuaTeX `texio.write()`.

```
\directlua{
   local x=[[\$150 for the "\#1" product---20\%! more than its competitor, Widget \& Co.]]
   texio.write(x)
}
```

Запуск цього коду з використанням **plain TeX** дає такий вивід у `.log` файлі, показуючи результат будь-яких розширень:

```
\$150 for the "\#1" product---20\%! more than its competitor, Widget \& Co.
```

Очевидно, у plain TeX жоден із керувальних символів`\$`, `\#`, `\%` або `\&` не був розгорнутий — тому що всі вони створені за допомогою `\chardef`.

Запуск цього коду за допомогою **LaTeX** документа:

```
\documentclass{article}
\begin{document}
   \directlua{local x=[[\$150 for the "\#1" product---20\%! more than its competitor, Widget \& Co.]] texio.write(x)}
\end{document}
```

дає такий вивід у `.log` файлу

```
\protect \TU\textdollar 150 for the "\#1" product---20\%! more than its competitor, Widget \& Co.
```

Очевидно, запуск LaTeX дає результат, відмінний від plain TeX, тому що в LaTeX команда `\$` була розгорнута, що вказує на те, що це макрос.

**Примітка:** І в plain TeX, і в LaTeX `\directlua` жоден із керувальних символів не було повністю оброблено `\%`, `\&`, `\#` та `\$` для генерування відповідного символу. Під час процесу розширення, виконаного `\directlua` токени, що представляють ці керувальні символи — або, у LaTeX, їхнє розширення — проходять безпосередньо до головного списку токенів, який будується.

**Примітка:** Керувальні символи утворюються з одного символу, що не має коду категорії 11, наприклад `\#`. Коли токен, що представляє керувальний символ, перетворюється назад у його текстове представлення, рушії TeX не вставляють після цього тексту символ пробілу. Це особливе поводження з керувальними символами є вбудованим правилом роботи рушіїв TeX.

### Нерозгорнуті токени: придушення розширення

`\directlua`попередня обробка — це один із прикладів, коли TeX-рушій виконує розгортання, але ви можете захотіти *запобігти* застосуванню розгортання до одного або кількох токенів, які інакше були б розгорнуті. Як ще один приклад, LuaTeX (і всі TeX-рушії) виконують процес розгортання, подібний до того, що у `\directlua`, коли вони обробляють `\write` команди:

`\write file-number {⟨material⟩}`

\write наказує TeX-рушієві вивести `⟨material⟩`—часто з командами TeX/LaTeX—у текстовий файл (`file-number`); будь-які команди, що розгортаються, всередині `⟨material⟩` будуть, якщо цьому не запобігти, розгорнуті перед `⟨material⟩` фактичним записом у цей файл.

Як і слід було очікувати, TeX-рушії надають команди для придушення або керування розгортанням:

* `\noexpand⟨token⟩`: запобігає розгортанню одного `⟨token⟩`;
* `\unexpanded{⟨material⟩}`: запобігає розгортанню всіх команд (токенів), що розгортаються, у `⟨material⟩`. По суті, це багатотокенова версія `\noexpand`;
* `\protected`: префікс, який додається до визначень макросів і запобігає розгортанню цього макроса за певних обставин (наприклад, під час `\directlua`, `\write` або `\edef`).

Попри назви, що натякають на інше, і `\noexpand` та `\unexpanded` є *розширювані команди* дають добрі приклади того, як розглядати процес розгортання в TeX-рушії як виконання «операцій над токенами»: тут операція полягає в тому, щоб запобігти розгортанню одного або кількох наступних токенів (команд). Оскільки `\noexpand` та `\unexpanded` обидві є командами, що розгортаються, їх вилучають і обробляють (виконують) під час `\directlua`попередньої обробки, коли він будує список токенів із вашого `⟨code⟩`.

#### \noexpand ⟨token⟩

`\noexpand ⟨token⟩` запобігає розгортанню одного `⟨token⟩`. `\noexpand` у `\directlua` буде розгорнуто (вилучено з вхідних даних) і замінено результатами своєї «поведінки розгортання». Результат розгортання `\noexpand` полягає у створенні спеціального (прихованого) `маркерного токена` що розміщується перед оригінальним `⟨token⟩` розгортання якого потрібно придушити: цей `маркерного токена` виступає як прапорець, що означає «не розгортати наступний токен». Оскільки `\directlua` виконує повне розгортання, він повторно обробить будь-які токени, що виникають у результаті «поведінки розгортання» команди, що розгортається. Отже, коли розгортання `\noexpand ⟨token⟩` завершено, LuaTeX повертається, щоб прочитати результати, і бачить двотокенну послідовність `⟨маркерний токен⟩⟨token⟩` яка змушує оригінальний `⟨token⟩` пройти далі, нерозгорнутим, у список токенів, що будується `\directlua`.

**Приклад**

Якщо ми напишемо

```
\directlua{
   local x= "\TeX"
}
```

сайті `\TeX` макрос розгортається у свої складові токени, що в plain TeX призведе до передавання в Lua такого тексту (примітка: Lua не може обробити цей код, це лише приклад для демонстрації процесу):

`local x = "T\kern -.1667em\lower .5ex\hbox {E}\kern -.125emX"`

Якщо ми *придушимо* розгортання `\TeX` макроса за допомогою `\noexpand`

`\directlua{local x= "\noexpand\TeX"}`

буде створено такий код Lua (знову ж таки, Lua не може виконати цей код; це лише приклад для демонстрації `\noexpand`):

`local x= "\TeX "`

Через `\noexpand`, `\directlua` не розгорнеться `\TeX` а просто дозволить значенню токена, що представляє `\TeX` команду, пройти крізь нього неушкодженим, у список токенів, що будується під час першого етапу `\directlua`попередньої обробки.

**Примітка:** Пробіл, що з’являється після `\TeX` вводиться наступним перетворенням у LuaTeX `\TeX` цілочисельного значення токена назад у його текстове представлення (у `tokenlist_to_cstring()` функції).

#### \unexpanded{⟨material⟩}

`\unexpanded` є командою, що розгортається, і придушує розгортання всіх токенів, утворених із `⟨material⟩`. Як ми вже зазначали, коли TeX-рушій виконує розгортання, будь-яка команда, що розгортається, є *видаляється* з вхідних даних і *заміщується* результатами своєї «поведінки розгортання»; тож що це насправді означає для `\unexpanded`? Зазвичай, під час *повне розширення*, після завершення процесу розгортання певної команди TeX-рушій переходить до читання/обробки будь-яких токенів, що виникли з «поведінки розгортання» цієї команди — йому потрібно далі розгортати будь-які токени, які було породжено. Однак, `\unexpanded` *обходить* будь-яке подальше розгортання: ось як це робиться.

Усередині TeX-рушія `\unexpanded` команда спочатку перетворює символи та команди в `⟨material⟩` у тимчасовий список токенів, що складається з *нерозгорнуті* токенів. Після того як усі токени створено й збережено в тому тимчасовому списку токенів, `\unexpanded` команда спричиняє `\directlua` на *пропускати* повернення до їх читання та обробки — навіть попри те, що \directlua виконує повне розгортання. Натомість ці *нерозгорнуті* токени проходять напряму та включаються до головного списку токенів, що будується `\directlua` (у `scan_toks()` функції). Таким чином, усе в `⟨material⟩` перетворюється на токени, а процес розгортання пропускається для цього набору токенів. Дія `\unexpanded{⟨material⟩}` подібна до використання `\the\toks`, про що ми поговоримо нижче.

**Приклад**

`\unexpanded` дає результати подібно до `\noexpand` за винятком того, що може запобігти розгортанню кількох токенів; ось приклад:

```
\directlua{
   local x = "\unexpanded{\foo\bar\foobar}. Але Lua не може обробити цей код!"
}
```

що дає такий текст як код для Lua:

`local x = "\foo \bar \foobar . Але Lua не може обробити цей код!"`

**Примітка**: Після кожної назви команди стоять пробіли. Це знову ж таки наслідок подальшого перетворення в LuaTeX нерозгорнутих токенів `\foo`, `\bar` та `\foobar` назад у текст у `tokenlist_to_cstring()` функції.

#### визначення макросів \protected

Функція `\protected` команда — це префікс, який застосовується до визначення макроса, щоб запобігти розгортанню цього макроса, коли TeX будує розгорнутий список токенів, наприклад список токенів, створений `\directlua`попередньої обробки.

**Приклад**

Припустімо, ви визначаєте такі макроси з використанням і без використання `\protected` префікса:

```
\def\macroA{"Цей незахищений макрос містить рядок"}
\protected\def\macroB{"Цей захищений макрос також містить рядок"}
```

Якщо ви використовуєте оператор конкатенації рядків у Lua (`..`) щоб записати

```
\directlua{
   local x=\macroA..\macroB
}
```

`\directlua`попередня обробка призвела б до такого коду для передавання в Lua:

`local x="Цей незахищений макрос містить рядок"..\macroB`

`\macroA` не визначено за допомогою `\protected` тому він розгортається, утворюючи першу частину рядка для конкатенації, але `\macroB` визначено за допомогою `\protected` тому його не було розгорнуто.

Під час попередньої обробки функція `scan_toks()` LuaTeX створила токен для `\macroA`"Цей незахищений макрос містить рядок" `"Цей незахищений макрос містить рядок"`. Кожен символьний токен передається далі й додається до списку токенів, що будується.

Коли `scan_toks()` створює токен для `\macroB` вона помічає, що команда була визначена як `\protected` і не розгортає її: токен, що представляє `\macroB` проходить далі, незмінним (нерозгорнутим), до списку токенів, що будується. Після того як цей список токенів буде побудовано, наступний етап попередньої обробки у `tokenlist_to_cstring()` функції — це перетворення всіх токенів у списку токенів назад у їх текстове подання. Нерозгорнутий токен, що представляє `\macroB` виявляється і перетворюється на своє текстове подання, унаслідок чого з’являється текст `\macroB` який з’являється в коді, призначеному для Lua. Зауважте, що Lua насправді не може конкатенувати `"Цей незахищений макрос містить рядок"..\macroB` щоб отримати кінцевий рядок, тому що `\macroB` не має значення в синтаксисі Lua, що призводить до помилки `неочікуваний символ біля '\'`.

**Цікавинка**: Пакет `\protected` команду було запроваджено $$\varepsilon\text{-}\mathrm{\TeX}$$LuaTeX, першого великого розширення оригінального програмного забезпечення TeX від Кнута, і підтримується всіма TeX-рушіями, у чиєму родоводі коду є $$\varepsilon\text{-}\mathrm{\TeX}$$.

### Нерозгорнуті токени: використання \the\toks у \directlua

Життя в програмуванні не було б таким самим без тих «особливих випадків», з якими доводиться мати справу, і використання `\the` у поєднанні з `\toks` у `\directlua` команда — один із таких особливих випадків.

#### Коротка довідка про \toks

Примітив TeX `\toks` наказує TeX-рушієві зберегти деякі токени для подальшого використання: замість того щоб передаватися далі для обробки, ці токени відкладаються убік і зберігаються в області пам’яті, заданій за допомогою *реєстра токенів*. Наприклад, ми можемо наказати TeX-рушієві створити деякі токени і зберегти їх у позиції реєстра токенів `100` за допомогою

`\toks100={Hi, \TeX! \hskip 5bp}`

Тут TeX використовує реєстр токенів `100` для доступу до відомого місця у своїй пам’яті: області зберігання, призначеної для зберігання списків токенів.

Токени, що представляють усе між `{` та `}` створюються, *але не розгортаються*, і з’єднуються в список токенів — подібно до списку токенів, який ми розглядали раніше в цій статті. Щоб повторно використати ці токени, ми б написали `\the\toks100` у якому `\the` (команда, що розгортається) наказує TeX отримати збережені токени й вставити їх у місці, де ви написали `\the\toks100`. Інший спосіб мислити про це — `\the\toks` спричиняє вставлення TeX деяких токенів у цьому місці.

Функція `\toks` команду *не розгортає* жодного з токенів, які його просять створити і зберегти: він просто перетворює символи та команди між `{` та `}` на токени й зберігає їх.

#### Повернімося до \directlua

У обговоренні розгортання ми зазначили `\directlua{⟨code⟩}` виконує *повне розширення* значення `⟨code⟩`: вилучення всіх команд, що розгортаються, і заміна їх результатом їхньої поведінки розгортання — з подальшим *подальшим розгортанням* будь-яких токенів, що виникають унаслідок початкового розгортання команди, що розгортається.

`\the` є командою, що розгортається, тому `\directlua` розгорне її; однак, коли `\the` використовується у поєднанні з `\toks` у `\directlua`, як у `\the\toks⟨token register⟩`, вставлені токени *не розгортаються далі*. Розгортання `\the\toks⟨token register⟩` вводить послідовність *нерозгорнуті* токенів, збережених у `⟨token register⟩`, безпосередньо до списку токенів, що будується `\directlua`: така поведінка обходить звичайний процес повного розгортання. Фактично, ці токени проходять далі, *нерозгорнуті*щоб увійти до головного `\directlua`—цей процес пропускання нерозгорнутих токенів подібний за роботою до `\unexpanded`, як уже згадувалося раніше.

**Приклад**

Припустімо, ми визначимо макрос `\mymacro` як `\def\mymacro{\TeX}`. Він містить лише один токен для `\TeX` команди (яка є макросом): отже, маємо команду, що розгортається, `\mymacro` яка містить інший макрос `\TeX`, який також розгортається.

Наступний код призведе до того, що Lua спробує створити рядкову змінну `x`:

```
\def\mymacro{\TeX}
\directlua{
   local x="\mymacro"
}
```

Усередині \\`directlua`, токен для `\mymacro` розгортається, але це призводить до іншого токена, що розгортається, `\TeX`який далі розгортається. У plain TeX ці розгортання дають такий текст, переданий у Lua:

`local x = "T\kern -.1667em\lower .5ex\hbox {E}\kern -.125emX"`

Цей код намагається визначити рядок, який містить текст, що представляє розгорнуту версію `\TeX` макроса. Якщо ви спробуєте запустити цей приклад, Lua спробує створити цей рядок, але зазнає невдачі, згенерувавши помилку:

`недійсна escape-послідовність біля ' "T\k'.`

Пізніше в цій статті ми дослідимо значення «недійсна escape-послідовність».

Тепер порівняймо використання `\mymacro` із розміщенням `\TeX` токена всередині списку токенів, згенерованого `\toks` команди:

```
\toks100={\TeX}
\directlua{
   local x="\the\toks100"
}
```

LuaTeX `\directlua` під час обробки згенерує такий текстовий рядок для Lua:

`local x = "\TeX "`

Символ пробілу після `\TeX` генерується процесом перетворення токена команди в рядок у LuaTeX.

**Але зауважте**: Пакет `\TeX` макрос *не* було розгорнуто у його складові токени. `\the\toks100` спричинило вставлення токенів, збережених у реєстрі 100, але це й усе: вони *не* не розгортаються далі й включаються до головного списку токенів, що будується `\directlua` (у функції `scan_toks()`). Вставляння токенів у список токенів, створений `\toks` —це ще один спосіб запобігти розгортанню токенів.

Якщо ми запустимо цей приклад, він також дасть помилку:

`недійсна escape-послідовність біля ' "\T'.`

Ми розглянемо escape-послідовності Lua пізніше в статті.

## Інші команди/техніки, що використовуються під час розгортання

У цьому розділі ми розглянемо деякі додаткові команди/методи TeX, які можуть бути корисними в ситуаціях, коли застосовується розгортання (наприклад, у `\directlua`).

### \string ⟨token⟩

`\string` є командою, що розгортається, яка перетворює ⟨token⟩ на послідовність символьних токенів, кожен із кодом категорії 12.

Наприклад, `\string\TeX` утворить послідовність із 4 символьних токенів `\`, `T`, `e` та `X` де кожному символу призначається код категорії 12 (включно з початковим `\` символом).

Якщо ми напишемо

```
\directlua{
   local x="Я використаю \string\newcommand"
   print(x)
}
```

сайті `\string` команда буде розгорнута, унаслідок чого утвориться послідовність символьних токенів із кодом категорії 12. Після `\string` розгортання `\newcommand`) вони будуть включені до головного списку токенів, що будується `\directlua`. Після того як `\directlua` закінчить створення свого головного списку токенів, його складові токени перетворюються назад на текстове подання, що дає такий код для передавання інтерпретатору Lua:

`local x="Я використаю \newcommand" print(x)`

Коли цей код передається в Lua, `print(x)` виведе рядок `x` на екран (консоль). Однак ми трохи схитрували й навмисно використали приклад команди, що починається з `\n`. Якщо вам вдасться запустити цей приклад у локальній TeX-інсталяції, ви помітите, що Lua виводить на екран такий текст:

```
   Я використаю
   ewcommand
```

Щоб запустити цей код в Overleaf, ви можете наказати LuaTeX записувати безпосередньо до `.log` файл за допомогою функції Lua API LuaTeX `texio.write(*string*)`:

```
\directlua{
   local x="Я використаю \string\newcommand"
   texio.write(x)
}
```

Якщо ви перевірите отриманий `.log` файл, ви побачите, що він також містить

```
   Я використаю
   ewcommand
```

Цей неочікуваний вивід зумовлений тим, що Lua інтерпретує `\n` на початку `**\n**ewcommand` як escape-послідовність для символа переведення рядка (код символу 10): він припускає, що ви хочете почати новий рядок тексту, який починається з `ewcommand`. Ми обговоримо escape-послідовності Lua пізніше в цій статті.

### \detokenize{⟨material⟩}

`\detokenize` за своїм ефектом є багатотокеновою версією `\string` і теж є командою, що розгортається, яка перетворює все в `⟨material⟩` у послідовність символьних токенів із кодом категорії 12 —*за винятком* символи пробілу (значення ASCII/Unicode 32), яким присвоюється код категорії 10. `\detokenize` також вставляє кінцевий пробіл після назв команд, які *керуючі слова* (наприклад, `\foo`) але пробіл не вставляється після *керуючі символи* (наприклад, `\#`, `\%` тощо).

### Приклад

Навіть якщо макроси `\foohoo`, `\foo`, `\bar` та `\foobar` не визначені, якщо ви напишете таке:

```
\directlua{
   local x = "\string\foohoo\detokenize{\foo\bar\foobar}"
}
```

це дасть такий текст як код для передавання інтерпретатору Lua

`local x = "\foohoo\foo \bar \foobar "`

Якщо ви не використовуєте `\string` та `\detokenize` і пишете:

`\directlua{local x = "\foohoo\foo\bar\foobar"}`

`\directlua` обробить `\foohoo`, розпізнає, що це команда, і спробує розгорнути її; але через те, що `\foohoo` вона не визначена, це призведе до помилки:

```
   ! Невідома керувальна послідовність.
   l.1 \directlua{local x = "\foohoo
                      \foo\bar\foobar"}
         ?
```

Оскільки `\string` та `\detokenize` перетворюють свої аргументи на послідовність символьних токенів, `\directlua`процес розгортання все ж має змогу виявити токени команд, що розгортаються `\foohoo`, `\foo`, `\bar`, або `\foobar`: вони перетворюються на послідовності символьних токенів задовго до того, як зможуть спричинити розгортання.

Як зазначалося раніше, розгортання команди передбачає вилучення її з вхідних даних і заміну результатом її «поведінки розгортання». Результати розгортання (зазвичай токени) згодом читаються TeX-рушієм. Тут «поведінка розгортання» для `\string` та `\detokenize` полягає в поглинанні символьних токенів і токенів команд із вхідних даних та перетворенні їх на послідовності символьних токенів, спочатку збережені в тимчасовому списку токенів, який `\directlua` потім читає. Ці символьні токени включаються до головного списку токенів, що будується `\directlua`.

Наведена нижче схема показує, як `\string` перетворює `\foohoo` команду на послідовність символьних токенів, створюючи тимчасовий список токенів, який потім читається `\directlua` щоб включити ці символьні токени до головного списку токенів, що будується.

![](/files/82671e7aa33364d16bf81ee286f78fc414a07e28)

Якщо `\string` або `\detokenize` зустрічають символи у своєму аргументі, напр., `\string a` або `\detokenize{abc}` ці символи (тут із кодом категорії 11) створюють символьні токени, але з кодом категорії 12.

Примітки:

Якщо ми повернемося до наведеного вище прикладу:

`\directlua{local x = "\string\foohoo\detokenize{\foo\bar\foobar}"}`

що дає такий текст як код для передавання інтерпретатору Lua

`local x = "\foohoo\foo \bar \foobar "`

ми можемо спостерігати таке:

* `\detokenize` вставив пробіл після кожної назви макроса, але `\string` не вставив.
* `\string` діє на один токен.
* У рядку `"\foohoo\foo \bar \foobar "` використаному для визначення `x` ми знову натрапимо на механізм escape-символу Lua (обговорюється нижче):

  * `\bar` починається з `\b` яка є escape-послідовністю Lua, що використовується для представлення [символа backspace](https://en.wikipedia.org/wiki/Backspace) (код символу 8);
  * команди `\foohoo`, `\foo` та `\foobar` усе починається з `\f`, escape-послідовність Lua, що використовується для представлення [символа form feed](https://en.wikipedia.org/wiki/Page_break#Form_feed) (код символу 12).

  Оскільки символьні послідовності `\b` та `\f` використовуються всередині рядка, створеного за допомогою подвійних лапок `"..."` вони даватимуть небажані результати, якщо не вжити заходів, щоб запобігти цьому за допомогою так званих *довгих дужок* рядковим методом: це питання ми тепер можемо обговорити разом із Lua-послідовностями екранування.

## Що таке «Lua-послідовності екранування»?

Мови програмування резервують певні символи для «спеціального використання» як частину синтаксису мови: фактично цим символам надається певний особливий зміст. Однак бувають випадки, коли вам потрібно тимчасово «вимкнути» особливий зміст такого символу, якщо, наприклад, ви хочете вбудувати цей символ як частину довшого рядка, де його стандартна поведінка спричиняла б синтаксичні помилки. По суті, цей символ потрібно обробити *без* так, щоб він пройшов далі непоміченим, не активуючи своє стандартне тлумачення. Для цього програмісти використовують техніку, що називається *екрануванням* у якій «спеціальний символ» подається за допомогою так званої *послідовності екранування*.

Стандартний приклад (також підтримуваний Lua) — використання подвійних лапок усередині рядка, де внутрішні подвійні лапки екрануються за допомогою послідовності `\\"`:

`"Коли їх запитали про LuaTeX, вони відповіли: \\\"Це чудовий TeX-рушій!\\\" Я погодився."`

Мова Lua надає низку механізмів для роботи з послідовностями екранування:

* стандартні послідовності, зокрема `\n` (новий рядок), `\\r` (повернення каретки), `\\` (зворотна скісна риска), `\\"` (подвійна лапка), `\\t` (горизонтальна табуляція), `\\v` (вертикальна табуляція) і `\\'` (одинарна лапка);
* `\\xXX`, де `XX` — це послідовність рівно з двох шістнадцяткових цифр;
* `\\ddd`, де `ddd` — це послідовність із не більше ніж трьох десяткових цифр;
* на момент написання цієї статті (серпень 2019 року) остання версія LuaTeX, хоча ще й недоступна в Overleaf, використовує версію 5.3 Lua, яка запровадила підтримку UTF-8-послідовностей екранування: `\\u{XXX}`. Цей механізм екранування призначений для Unicode-символів, закодованих у UTF-8, де `XXX` — це послідовність із однієї або кількох шістнадцяткових цифр, що позначають кодову точку символу. Зверніть увагу, що дужки, які обрамляють `{ }` є обов’язковими.

### Керування послідовностями екранування

Традиційно рядки визначають за допомогою подвійних лапок, як у `"це рядок"`; у такому рядку можна використовувати послідовності екранування: `"це рядок.\\nТепер я почну з нового рядка."`. Однак Lua має другий і *дуже* зручний механізм визначення рядків: так звані *довгі дужки* механізм, у якому ви визначаєте рядок, обгортаючи текст у `[[` та `]]`:

`[[Я — рядок у довгих дужках]]`

У рядку, створеному методом довгих дужок, механізм екранування символів Lua *вимкнено*: послідовності екранування обробляються як звичайні символи. Наприклад, у рядку

`[[Я — рядок у довгих дужках\\n рядок]]`

сайті `\n` послідовність екранування не розглядається як один символ повернення каретки (ASCII-код 13), а як два звичайні символи: `\` після якого йде `n`.

### Чому рядки в довгих дужках такі корисні?

Як ми згодом побачимо, LuaTeX надає набір спеціалізованих вбудованих функцій Lua, які можна використовувати з `\directlua` для керування верстальною поведінкою LuaTeX. Серед цих численних функцій є одна під назвою `tex.print(*string*)` яка дозволяє передавати `*string*` матеріал із коду Lua назад до LuaTeX для верстки. Дуже простий приклад:

`\\directlua{tex.print("Hello, World!")}`

що призведе до того, що LuaTeX набере `Hello, World!`

Функція `*string*` використане в `tex.print(*string*)` також може містити текст, що представляє команди TeX і LaTeX, для обробки LuaTeX. Однак команди TeX/LaTeX починаються зі `\` символа, що створює проблему для рядків, визначених за допомогою подвійних лапок, тому що Lua спробує розібрати рядок, виявить початковий `\` символ і витлумачить його як початок послідовності екранування. Коли Lua спробує обробити цю послідовність екранування, це зазвичай завершиться невдачею, бо початковий `\` у поєднанні з першим символом багатьох імен команд TeX/LaTeX не утворює дійсної послідовності екранування, відомої Lua. Наприклад, під час обробки рядка на кшталт `"I like \\LaTeX"` Lua побачить `\\L` і завершить роботу з помилкою «invalid escape sequence», і саме це є причиною згаданих вище помилок.

#### На допомогу приходять рядки в довгих дужках!

Метод довгих дужок для створення (визначення) рядків надзвичайно корисний, тому що, хоча команди TeX/LaTeX починаються зі `\` символа, метод рядків у довгих дужках вимикає механізм послідовностей екранування Lua. Ось короткий приклад, пам’ятаючи, що нам потрібно запобігти розгортанню макросів, використовуючи, наприклад, `\protected` або `\noexpand`.

Припустімо, ми визначимо `\\newtest` макрос ось так

`**\\protected**\\def\\newtest#1{Аргумент: #1}`

і використаємо його в `\directlua` з функцією Lua API LuaTeX `tex.print()`:

```
\directlua{
   tex.print("\\newtest{Hello}")
}
```

Через використання `\protected`, макрос `\\newtest` не розгортається, що призводить до такого тексту, переданого до Lua:

`tex.print("\\newtest {Hello}")`

Пробіл, доданий після `\\newtest` і перед відкривною дужкою (`{`) є побічним ефектом `\directlua`перетворення командних токенів назад у їхнє текстове представлення.

Цей код передається до Lua, який згодом виконує функцію LuaTeX `tex.print()` але виникає проблема, яка проявляється по-різному залежно від шрифтів, які ви використовуєте. У LaTeX на Overleaf ви побачили б вивід приблизно такий:

![](/files/36f0a43716cbfde2ac4c9aa6cf1cd338d7dadb14)

разом із попередженням у файлі журналу:

```
   Missing character: There is no
   (U+000A) in font [lmroman10-regular]:+tlig;!
```

У plain TeX ви можете побачити вивід, що виглядає приблизно так:

![](/files/ebaab9a3463fb252376d335325b261f422816013)

В обох випадках `\\newtest` макрос не викликається, і результат не такий, як ми задумували. Помилка спричинена механізмом екранування символів Lua: у тексті `\\newtest {Hello}` назва макросу починається зі `\n` який Lua розпізнає як послідовність екранування для символу переведення рядка, тож він замінює `\n` на ASCII-символ 10, або в шістнадцятковому вигляді 0A. У повідомленні про помилку LaTeX, `U+000A` — це спосіб подати значення Unicode за допомогою 4 шістнадцяткових цифр.

Оскільки `\n` перетворюється на символ переведення рядка, LuaTeX бачить не виклик макросу, а натомість вважає, що йому наказано набрати якийсь текст, що починається з ASCII-символу з кодом 10:

`⟨ASCII 10⟩ewtest {Hello}`

Залежно від використаного шрифту LuaTeX може, а може й не може, набрати `⟨ASCII 10⟩` символ, але решта тексту виводиться як є, а `{` та `}` трактується як група і не друкується.

Plain TeX дає інший результат, оскільки шрифтом за замовчуванням є Computer Modern Roman, який має дивне кодування, через яке при появі коду символу 10 набирається велика омега.

Щоб запобігти цим проблемам, нам потрібно використовувати рядки в довгих дужках, щоб не застосовувалося механізм екранування Lua. Правильний результат отримуємо з

`\\directlua{tex.print([[\\newtest{Hello}]])}`

що дає результат, показаний на наведеному нижче знімку екрана:

![](/files/e91d4ed25bdafdf27d099371c57d382a63ca61c1)

### Розгортання та невиконання незгортуваних команд

Під час обговорення розгортання ми зазначили, що це процес, у якому TeX-рушій *видаляє* розгортну команду (токен) із поточного вхідного потоку та *замінює* її результатом (результатами), які породила ця розгортна команда. Оскільки \\\directlua виконує *лише розгортання* дії (щоб згенерувати список токенів), *не* не може вести обробку LuaTeX далі за це. Після того як розгортну команду прочитано і повністю розгорнуто, результати цього розгортання — які часто включають незгортувані команди (токени) — буде включено до списку токенів, що формується, готового до перетворення назад у текст для передавання до Lua.

Тут діє важливий принцип: під час *лише розгортання* дій, спрямованих на створення списку токенів, TeX-рушії, зокрема LuaTeX, *не виконують* жодних незгортуваних примітивних вбудованих команд TeX.

У випадку `\directlua{⟨code⟩}`, якщо повністю розгорнута версія вашого `⟨code⟩` породжує або містить незгортувані команди TeX/LaTeX, вони *будуть передані до Lua* (як текст).

#### Приклад

Ось приклад, що демонструє, що незгортувані примітиви не виконуються під час обробки лише розгортання (наприклад, усередині `\directlua`). Припустімо, ми визначаємо макрос `\\setcountreg` ось так:

`\\def\\setcountreg#1#2{\\count#1=#2\\relax}`

**Примітка**: Ми використовуємо `\\relax` після параметра `#2` щоб не дозволити LuaTeX зайти надто далеко під час сканування вхідних даних у пошуках числового значення (аргумента), яке має відповідати параметру `#2`.

Якщо, поза межами `\directlua`, ми пізніше викличемо макрос ось так

```
   \\setcountreg{100}{50}
   Значення в регістрі лічильника 100 — \\the\\count100.
```

це виведе

`Значення в регістрі лічильника 100 — 50.`

У цьому контексті будь-який TeX-рушій обробляв би макрос `\\setcountreg`— розгорнув би макрос, визначив аргументи та продовжив би читати *і виконувати* (виконувати) команди, що містяться в тексті заміни макросу (визначенні). Результат тут — присвоїти `50` як значення, збережене в регістрі `\\count100`.

Однак, коли TeX-рушій виконує *лише розгортання* дії, як це відбувається з `\directlua`, він *не виконуватиме* незгортувані команди, що містяться у визначенні макросу.

Якщо ми напишемо

```
\\def\\setcountreg#1#2{\\count#1=#2\\relax}
\directlua{
   local x = [[\\setcountreg{100}{50}]]
}
```

він породжує такий текст як код для Lua:

`local x = [[\\count 100=50\\relax ]]`

Наведений вище код Lua показує, що в межах `\directlua` сайті `\\setcountreg` було розгорнуто, його аргументи визначено та підставлено в відповідний параметр (`#1` та `#2`) але далі цього не йде: незгортувана примітивна команда TeX `\count` було *не виконується* під час `\directlua`розгортання.

Однак LuaTeX виконає код TeX, якщо ми передамо отриманий рядок `x` *назад до LuaTeX* через `tex.print(x)` ось так

```
\\count100=50 % встановлюємо \\count100 початкове значення 50
\\def\\setcountreg#1#2{\\count#1=#2\\relax}
\directlua{
   local x = [[\\setcountreg{100}{250}]]
   tex.print(x)
}
Значення, збережене в регістрі лічильника 100, — \\the\\count100.
```

Після `\directlua` завершення вивід буде таким

`Значення, збережене в регістрі лічильника 100, — 250.`

показуючи, що регістр лічильника `100` тепер справді містить значення `250`.

Отриманий із наведеного вище прикладу код Lua такий:

`local x = [[\\count 100=250\\relax ]] tex.print(x)`

Цей код визначає `x` як рядок, створений методом довгих дужок, який використовується, щоб уникнути помилок із хибними послідовностями екранування. Якби ми використали подвійні лапки `"..."` щоб визначити x, комбінація символів `\\c` на початку `\count` спричинила б помилку: `invalid escape sequence near ' \"\\c'`.

Виклик Lua API LuaTeX `tex.print(x)` призводить до того, що LuaTeX виконує послідовність коду TeX `\\count 100=250\\relax` та `\\count100` отримує значення `250` як видно з набраного виводу:

`Значення, збережене в регістрі лічильника 100, — 250.`

#### Обережно: макроси та Lua API LuaTeX

У наведеному вище прикладі ми побачили, що під час `\directlua`попередньої обробки (розгортання) LuaTeX не виконував код `\\count 100=250`, який містить `нерозширюваних` примітивну команду `\count`: щоб виконати цей код, нам довелося *передати його назад до LuaTeX* через `tex.print()`.

`\directlua` — це лише один випадок, коли LuaTeX виконує обробку лише розгортання для побудови списку токенів. Є інші команди, які виконують подібні дії розгортання та генерації списку токенів, наприклад `\write` та `\edef`: ці команди також не виконують незгортувані примітиви під час своєї обробки розгортання. Загальний принцип полягає в тому, що TeX-рушії не виконують незгортувані примітиви під час побудови списку токенів у процесах обробки лише розгортання.

**Переписуємо наш макрос для використання Lua API LuaTeX**

Ми можемо переписати `\\setcountreg` макрос, використовуючи функцію Lua API LuaTeX під назвою `tex.setcount()`, тим самим уникаючи команд TeX для зміни значення, збереженого в регістрі лічильника `100`:

```
   \\def\\setcount#1#2{\\directlua{tex.setcount(#1,#2)}}
   \\count100=50
   регістр лічильника 100 містить \\the\\count100\\par
   \\setcount{100}{250}
   регістр лічильника 100 тепер містить \\the\\count100\\par
```

Цей код набере:

```
регістр лічильника 100 містить 50
регістр лічильника 100 тепер містить 250
```

Тут ми використовуємо `tex.setcount()`, одну з численних функцій Lua API LuaTeX, щоб *безпосередньо звернутися до* внутрішньої області зберігання даних LuaTeX і помістити значення `250` у комірку пам’яті, що відповідає регістру лічильника `100`. По суті, ми *обійшли* стандартні методи обробки вхідних даних TeX-рушія LuaTeX: читання вхідних даних, створення токенів і виконання примітивних команд TeX. Однак тут є застереження: використовуючи функції Lua API LuaTeX, дія обробки лише розгортання *може спричиняти побічні ефекти*: зміни значень, збережених усередині TeX-рушія, які були б неможливими з чистими командами TeX/LaTeX.

**Приклад: неочікувані побічні ефекти**

Ось приклад, що демонструє *неочікувані* побічні ефекти, які можуть виникати з макросами, що використовують `\directlua`. Припустімо, ми напишемо такий код:

```
\\def\\dochange{\\directlua{tex.setcount(999,12345)}}
\\edef\\careful{\\dochange}
\\the\\count999
```

Запуск цього коду набере `12345`!

Як таке може бути? Ми ж не *явно* викликали жодного коду чи макросів, щоб помістити це значення в регістр лічильника `999`. Чи все ж таки викликали?

Ми визначили `\\dochange` на `\directlua` команду, що використовує `tex.setcount()` для збереження значення `12345` в регістрі лічильника `999`: у TeX-коді це еквівалент `\\count999=12345`. Потім ми використали стандартний примітив TeX `\edef` щоб визначити макрос `\\careful`— саме використання `\edef` спричиняє неочікуваний побічний ефект.

`\edef` повністю розгортає свій аргумент: тут він виявляє розгортний макрос `\\dochange` і розгортає його. Макрос `\\dochange` використовує розгортну команду `\directlua` яка містить виклик Lua API; тож розгортання `\\dochange` призводить до розгортання `\directlua` і це спричиняє `tex.setcount()` до виклику, що змінює значення в регістрі лічильника `999`.

Якщо ми перевизначимо `\\dochange` з використанням команд TeX:

```
   До: регістр лічильника 999 містить \\the\\count999.\\par
   \\def\\dochange{\\count999=12345\\relax}
   \\edef\\careful{\\dochange}
   Після: регістр лічильника 999 містить \\the\\count999.\\par
```

запуск цього коду набере

```
До: регістр лічильника 999 містить 0.
Після: регістр лічильника 999 містить 0.
```

Очевидно, на `\\count999`не було жодного впливу. Коли `\edef` визначає `\\careful` він розгортає його, `\\dochange` але це розгортання породжує лише нерозгортні примітиви TeX: вони не *не виконується* а просто *зберігаються* у списку токенів, що становить визначення `\\careful`.

Для повноти картини той самий принцип пояснює, чому це породжує набраний вивід:

```
\\def\\dochange{\\directlua{tex.print("Hello")}}
\\edef\\careful{\\dochange}
```

## Короткий вступ до Lua API LuaTeX

Як ми бачили, `\directlua` не лише дає змогу писати звичайний код Lua або суміш коду Lua та TeX/LaTeX, а й надає доступ до набору додаткових функцій Lua (специфічних для LuaTeX), які можна використовувати (викликати) для взаємодії з внутрішньою роботою програмного забезпечення верстки LuaTeX або безпосереднього керування нею. У цій статті ми використали кілька функцій Lua, `tex.print()`, `texio.write()`, `tex.setcount()` і вони, разом із *багатьма* іншими, задокументовані в [Довіднику з LuaTeX](http://www.pragma-ade.com/general/manuals/luatex.pdf) де групи споріднених функцій називаються *бібліотеками*.

Ці функції Lua можна вважати Lua API LuaTeX (**Скопійований**нтерфейс **І** прикладного програмування **I**) які надають інструменти для створення складних рішень з верстки та інженерії документів шляхом керування верстальною поведінкою LuaTeX за допомогою Lua як рушія.

Як зазначалося, LuaTeX організовує свій API в набір функцій, які він називає бібліотеками: групи функцій, пов’язані між собою своїм призначенням або діями. Кожен набір функцій призначений для надання доступу до певного аспекту внутрішніх процесів LuaTeX, структур даних, сховищ даних і алгоритмів верстки. Внутрішньо LuaTeX складається з кількох компонентів: програмних бібліотек/інструментів (здебільшого написаних на C), які не лише утворюють сам TeX-рушій, а й інші підсистеми, зокрема Lua, MetaPost, Kpathsea, FontForge, libpng і zlib. Ці бібліотеки інтегровані для створення можливостей і функцій виконуваного ПЗ LuaTeX, і саме через Lua API користувачі отримують доступ до функціональності LuaTeX, що випливає з інтеграції та узгодження цих численних програмних компонентів.

## Деякі приклади та пастки

У цьому розділі ми подаємо кілька додаткових прикладів, що використовують теми, поняття та пояснення, наведені в цій статті.

### Використання символу тильди (\~)

Мова Lua використовує `~` символ (який називається тильдою) як частину свого синтаксису, зокрема синтаксису для виконання перевірки на «не дорівнює»; наприклад, щоб перевірити, чи змінна `x` не дорівнює `4` ми могли б написати:

```
   local x=3
   if x ~= 4 then
   print("x не дорівнює 4")
   end
```

Якщо ми спробуємо запустити цей простий код Lua через `\directlua`:

```
\directlua{
   local x=3
   if x ~= 4 then
   print("x не дорівнює 4")
   end
}
```

ми отримуємо помилку:

`[\directlua]:1: 'then' expected near '\'.`

Це дивно, бо наш код правильний: ми використали `'then'` і в ньому немає `\` символа в нашому коді, тож що пішло не так? Щоб це зрозуміти, треба пам’ятати, що для TeX/LaTeX, `~` зазвичай визначається як «спеціальний символ» з кодом категорії 13: так звані активні символи, які є міні-макросами і тому підлягають розгортанню. Коли `\directlua` виявляє `~` символ, він розгортається *видаляючи його* з вхідних даних і *замінивши її* і замінюючи його результатом розгортання. Використовуючи plain TeX, отриманий текст (код), який LuaTeX створює й передає інтерпретатору Lua, фактично не містить `~` символа і виглядає так:

`local x=3 if x \penalty \@M \ = 4 then print("x не дорівнює 4") end`

Функція `~` символ було *видаляється* та *розгорнуто* на його складові команди — наведений вище код Lua є результатом визначення активного символа у plain TeX `~`. Тепер ми бачимо, чому Lua відповідає помилкою `'then' expected near '\'`—він починає розбирати цей код, але натрапляє на слово `\penalty` яке для Lua нічого не означає й спричиняє синтаксичну помилку.

Щоб це виправити, `~` символ має мати безпечний код категорії в момент `\directlua` обробки вашого коду; наприклад, ми можемо тимчасово змінити код категорії `~` на 11 (літера), якщо вмістити код у групу:

```
\begingroup
\catcode`\~=11
\directlua{
   local x=3
   if x ~= 4 then
   print("x не дорівнює 4")
   end
}
\endgroup
```

Цей код працює як очікується, і `x не дорівнює 4` виводиться в консоль. Є й інші варіанти: ми можемо використати розгортні команди `\noexpand` або `\string`.

#### Використання \string⟨токена⟩

Ми можемо застосувати `\string` до однобуквеного `⟨token⟩` `~` який має код категорії 13 (активний символ); `\string` перетворює `~` символ, щоб згенерувати символьний токен, який має код категорії 12. Якщо ми зробимо

```
\directlua{
   local x=3
   if x \string~= 4 then
   print("x не дорівнює 4")
   end
}
```

це породжує потрібний нам код Lua:

`local x=3 if x ~= 4 then tex.print("x не дорівнює 4") end`

#### Використання \noexpand⟨токена⟩

Ми можемо використати `\noexpand~` щоб придушити розгортання активного символа `~`

```
\directlua{
   local x=3
   if x \noexpand~= 4 then
   print("x не дорівнює 4")
   end
}
```

Нерозгорнутий `~` токен проходить до списку токенів, який створюється в `\directlua` і буде знову перетворений на текст, що дає робочий код Lua.

### Використання символу \#

У мові Lua `#` символ можна використовувати для визначення довжини таблиці. Однак якщо ми спробуємо такий код

```
\directlua{
   local tbl = {}
   tbl[1] = "Hello"
   tbl[2] = "World"
   tex.print("Довжина таблиці дорівнює 2")
}
```

ми могли б очікувати, що LuaTeX набере

`Довжина таблиці дорівнює 2`

але він породжує помилку:

`\directlua]:1: attempt to get length of a number value`

Ця помилка виникає тому, що `#` символ зазвичай має код категорії 6 (параметр макроса) — `#` символ має два призначення в TeX/LaTeX: позначати параметри макросів (`#1`, `#2`… `#9`) і замінний текст у шаблонах вирівнювання (для `\halign` та `\valign`).

Коли `\directlua` коли він генерує токени для побудови свого списку токенів, він бачить `#` символ із кодом категорії 6 і створює відповідний символьний токен для його представлення. Коли настає час перетворити кінцевий список токенів назад у текстову форму, символьний токен для # (з кодом категорії 6) отримує особливу обробку: він виводиться як *два послідовні символи*: `##`, унаслідок чого до Lua передається такий код:

`local tbl = {} tbl[1] = "Hello" tbl[2] = "World" print(##tbl)`

Під час перетворення в код Lua оригінальний `#` було подвоєно, і це породжує помилку:

`\directlua]:1: attempt to get length of a number value`

Ця проблема виникає через синтаксис TeX, який використовує подвоєний символ решітки `##` щоб позначити або згенерувати один `#` токен; цей синтаксис використовується в макросах, які визначають інші макроси, що приймають параметри, або в макросах, які використовують для створення шаблонів для `\halign` або `\valign` команд побудови таблиць. Це досить заплутано, тож давайте подивимося на приклад.

#### Приклад

Припустімо, ми визначаємо макрос `\mymacro` який приймає один параметр, `#1`, але він також визначає другий макрос `\foo` який сам приймає один параметр. Щоб розрізнити параметр `#1` що використовується з `\mymacro` і потребу визначити `\foo` щоб використовувати власний параметр `#1` синтаксис TeX вимагає, щоб ви використовували `##1` всередині `\mymacro` для позначення параметра, який потрібно використовувати з `\foo`:

`\def\mymacro#1{\def\foo##1{#1 Hello##1}}`

Якби ви написали `\mymacro{Hey!}` це визначило б макрос `\foo` як

`\def\foo#1{Hey! Hello#1}`

Зауважте, що `\mymacro`параметр `#1` (`Гей!`) було включено у визначення `\foo` а послідовність `##1` була перетворена на `#1` у визначенні `\foo`. Тож ми можемо використати `\foo` ось так:

`\foo{, World!}`

щоб було набрано `Гей! Привіт, світе!`

Ми можемо розв’язати `\directlua`обробку символа `#` символ, тимчасово змінивши його код категорії перед тим, як LuaTeX обробить код. Наприклад:

```
\begingroup
   \catcode`\#=11
   \directlua{
   local tbl = {}
   tbl[1] = "Hello"
   tbl[2] = "World"
   tex.print("Довжина таблиці дорівнює 2")
}
\endgroup
```

Це генерує код Lua

```
local tbl = {} tbl[1] = "Hello" tbl[2] = "World" tex.print("Довжина таблиці дорівнює 2")
```

що набирає результат, якого ми очікували:

`Довжина таблиці дорівнює 2`

### Використання символу %

У TeX/LaTeX символ `%` зазвичай використовується для додавання однорядкових коментарів у ваш код: щоб повідомити рушію TeX, що він має ігнорувати все від цієї точки до кінця рядка, у якому `%` він записаний. Однак у мові Lua символ `%` використовується в деяких дуже корисних функціях обробки рядків, таких як `string.format(...)`, `string.gmatch(...)`, і `string.gsub(...)` у яких `%` символ відіграє важливу роль як частина синтаксису цих функцій.

Коли використовується з TeX/LaTeX, `%` він виступає символом коментаря, бо йому призначено код категорії 14. Щоб він поводився як звичайний символ і вимкнути його звичну поведінку в TeX/LaTeX, потрібно змінити його код категорії на безпечний, наприклад 12. Наведений `\directlua` нижче приклад використовує кілька методів, розглянутих раніше в статті, разом з одним, який ми ще не згадували: ``\catcode`\^^M=12``, що дозволяє нам використовувати коментарі Lua в нашому коді; це обговорюється нижче.

#### Приклад

Наведені нижче приклади запозичено з [lua-users.org](http://lua-users.org/wiki/StringLibraryTutorial), належним чином модифіковані для використання в `\directlua`.

```
\documentclass{article}
\begin{document}
\begingroup
\ttfamily
\let\\\relax
\catcode`\^^M=12 %<---ми далі розглянемо це нижче!
\catcode`\%=12
\directlua{
   local str -- оголосити локальну змінну для збереження результату

   tex.print("Використання string.format():".."\\par")

   str=string.format("%s %q", "Hello", "Lua user!") -- рядок і рядок у лапках
   tex.print(str.."\\par")
   str = string.format("%c%c%c", 76, 117, 97) -- символ
   tex.print(str.."\\par")
   str=string.format("%e, %E", math.pi, math.pi) -- експонента
   tex.print(str.."\\par")
   str=string.format("%f", math.pi) -- число з плаваючою комою
   tex.print(str.."\\par")
   str=string.format("%g, %g", math.pi, 10^9) -- число з плаваючою комою або експонента
   tex.print(str.."\\par")
   str = string.format("%o, %x, %X", 99, 125, 125)  -- вісімкова, шістнадцяткова, шістнадцяткова
   tex.print(str.."\\par")

   tex.print("\\vskip3mm".."Використання string.gmatch():".."\\par")

   for word in string.gmatch("Привіт, користувачу TeX", "%a+") do
      tex.print(word.."\\par")
   end

   tex.print("\\vskip3mm".."Використання string.gsub():".."\\par")
   str=string.gsub("banana", "(an)", "%1-") -- захопити всі входження "an" і замінити
   tex.print(str.."\\par")
}
\endgroup
\end{document}
```

Наведений нижче знімок екрана показує результат набору наведеного вище коду:

![Використання рядкових функцій Lua в \directlua](/files/dee838d5d0789a092af217de7843805cc1027218)

## Чому код Lua показано в один рядок?

Як ви могли помітити, усі фрагменти (згенерованого) коду Lua, показані в прикладах цієї статті, подані як один рядок тексту: розриви рядків, що спочатку були у `\directlua` фрагментах коду, не зберігаються. Чому так? Це тому, що розриви рядків у коді Lua було *видалено* під час препроцесингу LuaTeX у `\directlua`, через що код Lua перетворюється на один довгий рядок тексту. Таку поведінку можна простежити до способу, яким рушії TeX обробляють символи кінця рядка — позначені як `\\r` (повернення каретки) та `\n` (переведення рядка) у програмній літературі. Чому нам може знадобитися турбуватися про ці дрібні деталі, стане зрозуміло, коли ми обговоримо використання механізмів Lua для закоментування частин коду.

Коли програмне забезпечення записує (зберігає) текстовий файл, кожен окремий рядок тексту завершується так званими символами «нового рядка» — фактичний(і) символ(и) нового рядка залежать від програми та операційної системи, які використовуються для запису цього файла. Вікіпедія має [цікаву статтю](https://en.wikipedia.org/wiki/Newline) яка досліджує історію/еволюцію символів нового рядка, що використовуються сьогодні.

У будь-якому текстовому файлі окремі рядки можуть завершуватися різними комбінаціями символів, які називаються поверненням каретки (символ ASCII/Unicode 13) та/або переведенням рядка (символ ASCII/Unicode 10), що позначаються як `\\r` та `\n` відповідно. Оскільки рушії TeX розроблені як незалежні від платформи, їм потрібен спосіб обійти властиву залежність закінчень рядків у текстових файлах від платформи. Звісно, у рушіїв TeX є вбудований (але налаштовуваний) спосіб роботи з символами завершення рядка.

### Як рушії TeX обробляють закінчення рядків

Коли LuaTeX обробляє `\directlua{⟨code⟩}` він читає текст, що міститься у вашому `⟨code⟩` і застосовує стандартні методи рушія TeX для обробки будь-яких закінчень рядків, що містяться у вашому `⟨code⟩`. За замовчуванням ці стандартні методи TeX призводять до того, що всі символи завершення рядка (повернення каретки та переведення рядка) видаляються і замінюються символами пробілу. Ми кажемо «за замовчуванням», тому що обробку символів завершення рядка в рушії TeX можна змінити за допомогою параметра, який налаштовується користувачем, під назвою `\endlinechar`. Тут ми коротко розглянемо це в два кроки, але докладніші відомості можна знайти в статті Overleaf [Вступ до \endlinechar: як TeX читає рядки з текстових файлів](/latex/uk/dokladni-statti/05-an-introduction-to-endlinechar-how-tex-reads-lines-from-text-files.md).

#### Крок 1: TeX вставляє власний символ кінця рядка

Після прочитання рядка тексту з вхідного файла рушії TeX негайно видаляють будь-які `\\r` або `\n` символи з кінця цього рядка. Далі рушії TeX *вставляють* (додають назад) власний символ завершення рядка в кінець цього рядка. Цей символ визначається значенням параметра TeX, який налаштовується користувачем, під назвою `\endlinechar` і саме завдяки цьому механізму рушії TeX можуть обробляти символи кінця рядка незалежно від платформи: вони вибирають і встановлюють символ кінця рядка незалежно від того, що було спочатку вхідному текстовому файлі.

Зазвичай рушії TeX використовують значення

`\endlinechar=13`

який є символом повернення каретки (`\\r`). Однак користувачі завжди можуть призначити інший код символу як значення `\endlinechar`—це ми побачимо пізніше в цій статті.

Отже, будь-які символи завершення рядка, що містяться у вашому `⟨code⟩` які мають оброблятися `\directlua{⟨code⟩}` видаляються і замінюються одним символом, який визначає сам рушій TeX. Зауважте, що рушії TeX виконують таку обробку кінця рядка одразу після прочитання нового рядка тексту з файла і *до того, як* обробки будь-яких символів у цьому рядку (для генерації токенів). Однак на цьому історія не закінчується: те, що рушій TeX *не* робить із цими символами кінця рядка (він їх вставив) пояснює, чому код Lua стає одним-єдиним рядком.

#### Крок 2: TeX перетворює свій символ кінця рядка на пробіл

Окрім вставляння власного символу завершення рядка, визначеного значенням `\endlinechar`, рушії TeX також використовують код категорії 5 для символів, які мають бути *розглянуті як* символ кінця рядка. Унаслідок цього рушії TeX зазвичай працюють із:

1. символом кінця рядка, визначеним `\endlinechar`;
2. тим самим символом *зазвичай* якому призначено код категорії 5.

Саме те, що TeX робить із цим символом кінця рядка, пояснює нашу проблему з однорядковим кодом Lua. Коли рушій TeX обробляє рядок введення, він зрештою виявляє останній символ у цьому рядку: символ, визначений `\endlinechar`. Зазвичай цей символ має код категорії 5, що змушує TeX *замінити його* символом пробілу: тобто наприкінці рядків TeX, по суті, видаляє свій символ завершення рядка і замінює його пробілом. До речі, рушії TeX також використовують символи з кодом категорії 5 для виявлення порожніх рядків і початку нового абзацу, але тут ми цього не розглядатимемо.

Звісно, будучи TeX, ви можете виконувати різноманітні спеціальні трюки програмування макросів, перевстановлюючи `\endlinechar` на інший символ та/або надаючи символу, призначеному для `\endlinechar` код категорії на ваш вибір.

Якщо ви хочете запобігти перетворенню коду Lua на один рядок тексту, ви можете або (тимчасово) змінити значення, призначене для `\endlinechar` або змінити код категорії стандартного символу завершення рядка `\\r`.

### Дивна нотація ^^ у TeX

У наступних розділах ми зустрінемо незвичну для TeX `^^` нотацію, відому як «механізм розширених символів». Її розробив Кнут як спосіб полегшити введення «керуючих символів», таких як символи завершення рядка, табуляції тощо. Наприклад:

* `^^J` позначає код символу 10 (`\n`, переведення рядка);
* `^^M` позначає код символу 13 (`\\r`, повернення каретки).

Послідовності символів на кшталт `^^M` перетворюються на відповідні коди символів на ранньому етапі процесу сканування вхідних даних TeX, коли TeX читає вхідні символи для генерації відповідних символьних токенів.

### Зміна символу, призначеного для \endlinechar

Пам’ятаючи, що нам усе ще потрібно запобігти розгортанню `~` символа, ми можемо написати

```
\begingroup
\endlinechar=10 % Змінити символ кінця рядка на \n
\directlua{
   local x=3
   if x \noexpand~= 4 then
   print("x не дорівнює 4")
   end
}% не хочемо, щоб тут з’являвся \n
\endgroup% або \n тут
```

Наведене вище значення для `\endlinechar` змушує LuaTeX додавати код символу 10 (`\n`, переведення рядка) в кінець кожного рядка, який він читає. Ми робимо це тому, що `\n` (переведення рядка) зазвичай має код категорії 12, що ви можете перевірити, написавши ``\the\catcode`\^^J``. Оскільки `\n` не має коду категорії 5, LuaTeX не перетворюватиме його на символ пробілу, тому він залишатиметься в кінці кожного рядка, прочитаного LuaTeX. Унаслідок цього символ із кодом 10 залишається в кінці кожного рядка, а отже, потрапляє до списку токенів, який будується `\directlua` і згодом знову з’являється в коді Lua, коли список токенів перетворюється на текст. Після наведених змін код Lua надсилається інтерпретатору Lua як така послідовність символів:

**\n**local x=3\*\*\n**if x \~= 4 then**\n**print("x не дорівнює 4")**\n**end**\n\*\*

де **\n** нотація має на увазі позначення коду символу 10 *не* якийсь невідомий макрос `\n`. Тепер інтерпретатор Lua бачитиме розриви рядків у коді точно так, як він був спочатку написаний у `\directlua` команди:

```
   local x=3
   if x ~= 4 then
   print("x не дорівнює 4")
   end
```

До речі, зауважте, що перший символ у рядку коду Lua є `\n` (перед ключовим словом `локальною` ). Це `\n` виникає через рядок

`\directlua{`

тому що одразу після відкривної `{` і це теж зберігається. Щоб цього уникнути, ви можете написати

`\directlua{%`

### Зміна коду категорії \r

Щоб зберегти розриви рядків у нашому коді Lua, ми також можемо змінити код категорії `\\r` на щось інше, ніж 5, щоб `\\r` більше не розпізнавався (не розглядався) як символ кінця рядка. За цією технікою LuaTeX усе ще використовує `\endlinechar=13` і й надалі додаватиме `\\r` до кінця кожного рядка; однак, оскільки `\\r` вже не має коду категорії 5, LuaTeX не розпізнаватиме `\\r` символ як символ кінця рядка: він не перетворить його на пробіл і пропустить його без змін, щоб він з’явився в коді Lua.

Пам’ятаючи, що нам усе ще потрібно запобігти розгортанню `~` символа, ми можемо написати

```
\begingroup
\catcode`\^^M=12 % змінити код категорії \r на 12
\directlua{
   local x=3
   if x \noexpand~= 4 then
   print("x не дорівнює 4")
   end
}
\endgroup
```

У цьому випадку код Lua надсилається інтерпретатору Lua так:

**\\\r**local x=3\*\*\r**if x \~= 4 then**\\\r**print("x не дорівнює 4")**\\\r**end**\r\*\*

де `\\r` нотація має на увазі позначення коду символу 13, а не якогось невідомого макроса TeX `\\r`. Як і в `\endlinechar` прикладі, інтерпретатор Lua тепер бачитиме розриви рядків у коді точно так, як він був спочатку написаний у `\directlua` команди:

```
   local x=3
   if x ~= 4 then
   print("x не дорівнює 4")
   end
```

До речі, знову зауважте, що перший символ у рядку коду Lua є `\\r` (перед ключовим словом local): це теж виникає через рядок

`\directlua{`

#### Чому \r використовував код категорії 12, а не код категорії 11?

Відповідь полягає в ризику випадкового внесення помилок, спричинених `\\r` (з кодом категорії 11), який додається в кінець команд TeX/LaTeX, прочитаних із нашого вхідного файла. Розгляньте цей приклад:

```
\begingroup
\catcode`\^^M=11 % змінити код категорії \r на 11
\directlua{
   local x=3
   if x \noexpand~= 4 then
   print("x не дорівнює 4")
   end
}
\endgroup
```

що породжує помилку:

```
   ! Невідома керувальна послідовність.
   l.9 \endgroup
```

Як це може бути правдою, якщо `\endgroup` є стандартною примітивною командою TeX? Причина помилки досить тонка: коли LuaTeX читав останній рядок тексту — той, що містив `\endgroup`—він також додав `\endlinechar` символа `\\r` в кінець цього рядка. Тепер у своїй пам’яті LuaTeX бачить послідовність символів

`\endgroup\r`

де ми використовуємо `\\r` щоб позначити символ із кодом 13 — а не назву якогось невідомого макроса TeX `\\r`.

На момент, коли LuaTeX прочитав цей рядок із нашого текстового файла, початковий `\begingroup` ще діє: ми знаходимося всередині групи, яку ще не було закрито виконанням відповідної `\endgroup` команди — що призвело б до того, що `\\r` повернеться до попереднього значення коду категорії 5.

Коли LuaTeX починає обробляти (створювати токени) з рядка тексту `\endgroup\r` він розпізнає перший символ `\` як символ екранування, який змушує LuaTeX почати шукати ім’я команди. Щоб визначити ім’я команди, LuaTeX шукає послідовність символів із кодом категорії 11, але оскільки `\\r` також має код категорії 11, LuaTeX вважає, що `\\r` символ (також із кодом категорії 11) утворює *частину команди* під назвою `\endgroup\r` якої, звісно, не існує, тож LuaTeX повідомляє про `Невизначена керувальна послідовність` помилку. Саме тому ми використали код категорії 12, а не 11.

Оскільки повідомлення про помилку LuaTeX було виведене в консоль, ми не могли легко побачити/помітити `\\r` символ, тож не було очевидно, що спричинило помилку.

### Чому ми переймаємося кінцями рядків?

Причина — щоб у вашому коді можна було використовувати спосіб коментування Lua! Ви можете використовувати стандартний механізм LuaTeX додавання `%` символів для закоментування окремих рядків у вашому коді; однак мова Lua має власні, дуже корисні, *багаторядкові* механізми коментування, які вам може захотітися використати.

Почнімо з того, що подивимося, що станеться, якщо ми спробуємо використовувати однорядкові коментарі мови Lua, не розв’язавши проблему розривів рядків. Якщо TeX використовує `%` символ % для коментування окремих рядків коду, то Lua використовує подвійний дефіс: `--`.

Що станеться, якщо ми спробуємо запустити це:

```
\directlua{
   local x=3
   if x \noexpand~= 4 then
   -- Я виводитиму результат цього складного тесту
   print("x не дорівнює 4")
   end
}
```

Ми отримуємо помилку:

`[\directlua]:1: 'end' expected near <eof>`

Ця помилка спричинена відсутністю розривів рядків у коді Lua, переданому інтерпретатору, який бачить лише один безперервний рядок, у якому коментар починається десь посередині цього рядка:

```

local x=3 if x ~= 4 then -- Я виводитиму результат цього складного тесту print("x не дорівнює 4") end
```

Усе після `**local x=3 if x ~= 4 then**` розглядається як закоментоване, через що інтерпретатор бачить неповний фрагмент коду Lua, унаслідок чого виникає помилка

`'end' expected near <eof>`.

де `<eof>` означає кінець файла.

Як ви, мабуть, здогадалися, ми повинні виправити це, забезпечивши передавання розривів рядків до отриманого коду Lua, чого можна досягти, наприклад, змінивши код категорії `\\r` на 12:

```
\begingroup
\catcode`\^^M=12 % змінити код категорії \r на 12
\directlua{
   local x=3
   if x \noexpand~= 4 then
   -- Я збираюся вивести результат цього складного тесту
   print("x не дорівнює 4")
   end
}
\endgroup
```

Тепер інтерпретатор Lua бачить рядок, але він містить `\\r` розриви рядків, як записано в `\directlua` фрагменті:

**\\\r**local x=3\*\*\r**if x \~= 4 then**\r\*\*-- Я збираюся вивести результат цього складного тесту\*\*\r**tex.print("x is not equal to 4")**\\\r**end**\r\*\*

Фактично це еквівалентно запису

```
   local x=3
   if x \noexpand~= 4 then
   -- Я збираюся вивести результат цього складного тесту
   print("x не дорівнює 4")
   end
```

що означає, що Lua може правильно обробити цей код і проігнорувати рядок, який ми закоментували.

**Блокові коментарі**

Мова Lua також підтримує синтаксис, який вона називає [«блоковий коментар»](https://www.lua.org/pil/1.3.html) (або *довгий коментар*): вони починаються з `--[[` і діють до відповідного `]]`. Ми можемо використовувати цей зручний синтаксис, щоб писати багаторядкові коментарі або закоментовувати фрагменти коду, які хочемо тимчасово прибрати:

```
\begingroup
\catcode`\^^M=12 % змінити код категорії \r на 12
\directlua{
   local x=3
   if x \noexpand~= 4 then
   --[[ Я збираюся вивести результат цього складного тесту
   просто тому, що це й справді
   такий приголомшливий висновок]]
   print("x не дорівнює 4")
   end
}
\endgroup
```

## На завершення

По-перше, вітаємо вас, якщо ви змогли прочитати цю об’ємну статтю до кінця! Ми намагалися створити досить вичерпний посібник із пов’язаних із TeX концепцій і тем, які забезпечують необхідний контекст, щоб отримати максимум від LuaTeX через `\directlua` команду. Ми сподіваємося, що створили статтю, яка є повчальною та додає щось корисне й цінне для спільноти користувачів Overleaf і не тільки. Як завжди, ми будемо раді отримати відгуки, тож, будь ласка, не соромтеся [зв’яжіться з нами](https://www.overleaf.com/contact) залишати коментарі до цієї статті або пропозиції щодо інших тем, про які ви хотіли б, щоб ми написали.

Щасливого $$\text{Lua}\mathrm{\TeX}\text{-ing!}$$ від Грема Дугласа та команди Overleaf.

### І нарешті... просто використовуйте пакет luacode

Хоча TeX і Lua працюють принципово по-різному, ці мови мають низку символів, які мають «спеціальні значення» в контексті кожної мови — такі як \\\\, %, \~, #, ^, & — звісно, Lua і TeX надають цим спеціальним значенням *дуже* різних цілей. Наш розгляд проблемних символів показує, чому можуть виникати труднощі та як їх можна розв’язати; однак вручну виправляти багато невеликих фрагментів коду Lua може бути досить нудно, тож більшість користувачів віддають перевагу пакетам LaTeX, які усувають ці труднощі. Один із таких пакетів — [`luacode`](https://ctan.org/pkg/luacode?lang=en) який надає набір можливостей, покликаних спростити роботу з `\directlua`, але принаймні тепер ви, можливо, краще розумієте проблеми, `luacode` які він вирішує за вас.


---

# 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/uk/dokladni-statti/09-an-introduction-to-luatex-part-2-understanding-directlua.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.
