iu9-ca-chat-api/nytl.typ

165 lines
14 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pagebreak(weak: true)
#set text(size: 13pt)
#align(center, text([Документация к New York Transit Line (набросок для своих)],
size: 30pt))
#let Heading(c, d) = {
heading(c, depth: d, numbering: "1.");
}
#let bold(txt) = text(txt, weight: "bold")
#Heading([Интродакшн], 1)
New York Transit Line - это система шаблонов. Она рассчитана под html, но в теории может использоваться для чего угодно.
Чтобы её использовать нужно завести папку с нашим кодом nytl (здесь и далее я называю кодом html код и html код вперемешку с nytl инструкциями).
Допустим папка называется `nytl_dir`.
Нужно создать объект `nytl::Templater`, настроить, обновить его, и можно уже "рендерить" наши странички (выбрать какой-то один элемент, объявленный в
`nytl_dir` и передать ему какие-то аргументы)
```cpp
nytl::Templater t{{{nytl_dir}}};
t.update();
json::JSON var1;
json::JSON var2;
std::string result = t.render(el_name, {&var1, &var2});
```
Элемент - это функция, возвращающая код. Элемент может принимать json::JSON аргументы (весь nytl базируется на libjsonincpp) аргументы и
другие элементы как аргументы. У него есть чётко заданная сигнатура. Элементы идентифицируются по имени:
Имя состоит из нескольких "лексических имён" (Непустых последовательностей букв, цифр, дефисов, подчеркиваний, не начинающихся с цифры),
разделённых точками. "лексическое имя" не может быть одним подчёркиванием.
Все "части" имени символа кроме подследнего символизируют путь до файла с символом (от папки `nytl_dir`). Последняя часть идентифицирует элемент
ВНУТРИ этого файла.
NYTL индексирует файлы следующим образом: `nytl::Templater` получает объект `nytl::TemplaterSettings` (назовём его `settings`) и он
содержит два важных поля: `settings.det.postfix_rule_for_element_cont` ( по дефолту `".nytl.html"` ) и `settings.det.postfix_rule_for_static_files`
(по дефолту `.html`). Сначала темплейтер смотрит, оканчивается ли имя файла на `settings.det.postfix_rule_for_element_cont`,
если да, то от имени отбрасывается постфикс, составляется имя самого файла (вместо / ставятся точки) и далее nytl парсит файл согласно обычному
nytl синтаксису (где файл - это набор элементов). Если первый постфикс не подходит, проверяется второй (`settings.det.postfix_rule_for_static_files`),
если подходит то файл не парсится, а всё его содержимое прямо запихивается в один элемент с именем как у файла. Если оба правила не применимы, ничего не
происходит.
#Heading([Синтаксис], 1)
#Heading([ELDEF], 2)
Файл nytl (Который скорее всего будет иметь расширение .nytl.html) состоит из объявления элементов, в которые входит:
- Название элемента относительно файла (одно лексическое имя). Если в качестве относительного имени файла выбрано main, имя элемента становится равным
имени фала.
- Сигнатура элемента. Нужна чисто чтобы меньше путаться.
- Содержиое элемента, где КОД намешан с NYTL ИНСТРУКЦИЯМИ.
Nytl инструкции всегда находятся в так называемых "волшебных блоках". Они начинаются со строки `settings.magic_block_start` и заканчиваются
на `settings.magic_block_end`. По дефолту волшебный блок таков: `{% ... %}`.
Объявление элемента выглядит так:
```
{% ELDEF element-name type-of-arg-1 arg-1-name type-of-arg-2 arg-2-name ... %}
... Содержимое элемента
{% ENDELDEF %}
```
Здесь я обозначил `ELDEF` и `ENDELDEF` большими буквами, поскольку это ключевое слово (имя оператора). Для них регистр не важен, но
мне кажется так писать их красивее. Аргументов может быть сколько угодно в сигнатуре. Имя аргумента "это лексическое имя". Если оно равно `"_"`,
то аргументу как бы не присваевается переменная. _Это как в Go_. Тип аргумента это либо `JSON`, либо `EL()` (регистр не важен).
В скобках для типа `EL` надо указать сигнатуру желанного элемента. Она состоит из списка типо аргументов (как в объявлении элемента, но без имён переменных).
Пример: `EL(EL() EL(JSON EL(EL) EL(EL() JSON EL()) EL()) JSON EL(EL(EL(EL(JSON)) JSON EL(EL()))))`. Это корректный тип.
Внутри элемента мы можем прямо писать _[html]_код и можно добавить специальные инструкции (конечно же, в волшебных блоках), что бы
добавить сложное поведение.
#Heading([NYTL выражения (внутри волшебных блоков)], 2)
Сперва нужно познакомиться с "nytl expressions". Начинаются они с имени переменной. Это может быть локальная переменная, а может быть
имя пакета с самого высокого уровня (так я называю фалы и саб-директории в корневой директории nytl). Оно тоже должно быть "лексическим именем".
Оно не может быть `"_"`. Объявление локальной переменной (хотя нет в них ничего переменного, в nytl все переменные константы) может "закрыть собой"
имя пакета верхнего уровня. Они приоритетнее. Выражение может состоять из одного такого имени. Но к выражению можно приписать "доступ к члену":
$<"expr"> = <"expr">.<"us integer">$ (это доступ по индексу),
$<"expr"> = <"expr">.<"lexical name">$ (это доступ по ключу),
$<"expr"> = <"expr">[ <"expr"> ]$ (это доступ по значению (которое является результатом выполениея выражения))
Доступ по ключу может проиходить для элемента (для взятия пакета элементов / элемента из пакета элементов). Это работает именно так, как ты подумал.
Так же доступ по ключу может обращаться к члену JSON-словаря. Доступ по индексу обращается к члену JSON-массива.
Доступ по выражению может делать всё это, но нужно чтобы тип результата выполнения выражения-ключа подходил для задумываемой операции.
Если это ещё не стало понятно: переменные это либо JSON, либо элемент (либо пакет - часть пути для элемента, отражающий папочную структуру
`nytl_dir`).
Пример nytl выражения: `boba.aboba.biba[bibina.0.222[din.don].zavletayu.v_tvoy_dom ][ odichal.vizovi.sebe-vracha]`
#Heading([Вкрапления nytl инструкций], 2)
#Heading([PUT], 3)
Синтаксис оператора PUT:
`{% PUT <expr (вызываемый элемент)> <expr (передаваемый аргумент 1)> <expr (передаваемый аргумент 2)> ... %}`
(ключевое слово PUT регстронечувствительно). Оператор `PUT` вызвает какой-то элемент. Какой - определяется первым переданным выражением.
Следуюущие выражения указывают передаваемые аргументы.
Есть ряд "встроенных элементов", определяемых самим nytl:
- `jesc`. Тип: `EL(JSON)`. Вставляет один сериализованный JSON-объект в ваш код как текст (обрабатывая текст функцией settings.escape).
- `jesccomp`. То же самое, что и `jesc`, но этот элемент выводит JSON компактно, а `jesc` красиво.
- `str2text`. Тип: `EL(JSON)`. Передаваемый JSON-аргумент должен быть строкой. `str2text` выводит это текст в код, предварительно обработав
`settings.escape` (процедура санитайзинга).
- `str2code`. То же самое что и `str2text`, но без санитайзинга.
По дефолту `settings.escape` будет содержать процедуру санитайзинга для html. Она заменяет `<>&"'` на их безопасные версии.
Есть такой оператор:
`{% WRITE <expr (X)> %}`, который эквивалентен оператору `{% PUT str2text X %}`
Есть такой оператор:
`{% ROUGHINSERT <expr (X)> %}`, который эквивалентен `{% PUT str2code X %}`
#Heading([Пустой волшебный блок], 3)
Вы можете вставить в код пустой волшебный блок `{%%}` и он абсолютно ничего не выведет. Он нужен только для корректировки табуляции.
#Heading([REF], 3)
Если надоело какой-то длинное выражение каждый раз по долгу писать, то можно воспользоваться "REF-блоком":
```
{% REF <variable-name> AS <expr (X)> %}
... содержимое блока ...
{% ENDREF %}
```
Благодаря нему можно завести новую переменную, значение которой будет равно вычисленному выражению. Содержимое блока имеет такую
же семантику и возможности, что и кодовый кусок, куда был вставлен блок, но там ещё можно использовать введённое обозначение.
#Heading([FOR], 3)
FOR цикл позволяет вычислить какое-то выражение и, если оно оказалось JSON-массивом/словарём позволяет проитерироваться по его членам.
Есть синтаксис, где мы итерируемся только по значениям:
```
{% FOR <var-value> IN <expr (X)> %}
... Повторяемый код...
{% ENDFOR %}
```
А есть синтаксис, где мы можем итерироваться по объекту и извлекать пару: ключ значение.
```
{% FOR <var-key> : <var-value> IN <expr (X)> %}
... text. ...
{% ENDFOR %}
```
Код внури FOR-блока может использовать новые переменные. Если X - это словать, то что такое ключ - это ясно. Если X - это массив, то
ключ - это индекс. Имя переменной это "лексическое имя nytl". Имя любой переменной можно заменить на `"_"`, что бы не создавать эту переменную.
Блок-FOR делает именно то, что вы думаете.
В конец закрывающего волшебного блока можно описать ключевые слова `LF` или `NOLF`: `{% ENDFOR LF %}` / `{% ENDFOR NOLF %}`.
Эти ключевые слова указывают, нужно ли вставлять перенос строки между элементами. По дефолту Line Feed вставляется.
#Heading([Фичи], 1)
- NYTL очень умно расставляет табуляцию. Объяснить механизм стоящий за ней очень сложно, поэтому легче просто пользоваться и не думать о том,
что происходит в nytl.
- NYTL НЕ ИСПОЛЬЗУЕТ РЕКУРСИЮ. НИГДЕ.
#Heading([Замечания], 1)
Из-за того, что New York Transit Line построен на libjsonincpp, два потока не могут одновременно чиать из одного темплейтера.
Такова цена безопасного оператора сравнения.