merged nytl doc repo (don't you hate it when there is 1000 repos for no reason)

This commit is contained in:
Андреев Григорий 2024-08-17 14:13:53 +03:00
parent d1a09efd00
commit 21e895cfb8
2 changed files with 165 additions and 0 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
api.pdf api.pdf
nytl.pdf

164
nytl.typ Normal file
View File

@ -0,0 +1,164 @@
#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, два потока не могут одновременно чиать из одного темплейтера.
Такова цена безопасного оператора сравнения.