diff --git a/.gitignore b/.gitignore index aa051f5..6814212 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ api.pdf +nytl.pdf diff --git a/nytl.typ b/nytl.typ new file mode 100644 index 0000000..90b4ef9 --- /dev/null +++ b/nytl.typ @@ -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 ... %}` +(ключевое слово 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 %}`, который эквивалентен оператору `{% PUT str2text X %}` +Есть такой оператор: +`{% ROUGHINSERT %}`, который эквивалентен `{% PUT str2code X %}` + +#Heading([Пустой волшебный блок], 3) + +Вы можете вставить в код пустой волшебный блок `{%%}` и он абсолютно ничего не выведет. Он нужен только для корректировки табуляции. + +#Heading([REF], 3) + +Если надоело какой-то длинное выражение каждый раз по долгу писать, то можно воспользоваться "REF-блоком": +``` +{% REF AS %} + ... содержимое блока ... +{% ENDREF %} +``` + +Благодаря нему можно завести новую переменную, значение которой будет равно вычисленному выражению. Содержимое блока имеет такую +же семантику и возможности, что и кодовый кусок, куда был вставлен блок, но там ещё можно использовать введённое обозначение. + +#Heading([FOR], 3) + +FOR цикл позволяет вычислить какое-то выражение и, если оно оказалось JSON-массивом/словарём позволяет проитерироваться по его членам. +Есть синтаксис, где мы итерируемся только по значениям: +``` +{% FOR IN %} + ... Повторяемый код... +{% ENDFOR %} +``` +А есть синтаксис, где мы можем итерироваться по объекту и извлекать пару: ключ значение. +``` +{% FOR : IN %} + ... 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, два потока не могут одновременно чиать из одного темплейтера. +Такова цена безопасного оператора сравнения. +