168 lines
14 KiB
Plaintext
168 lines
14 KiB
Plaintext
#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:
|
||
- `jsinsert`. Тип: `EL(JSON)`. Напрямую вставляет сериализованный JS-объект в код.
|
||
Полезно если нужно через тег ```html <script>``` передать переменные с сервера
|
||
на страничку.
|
||
- `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, два потока не могут одновременно чиать из одного темплейтера.
|
||
Такова цена безопасного оператора сравнения.
|
||
|