Справочник синтаксиса Spintax
Полный справочник по формату spintax, он же spin syntax: каждая конструкция, её правила и её ловушки.
Перечисления {a|b|c}
Случайным образом выбирает один вариант из списка.
{option1|option2|option3}
Примеры
{blue|grey|clear}
{|free|paid} plan ← empty option = sometimes nothing
{Acme {Pro|Lite}} ← nested enumerations
{order {|#42-A} confirmed} ← nesting with empty option
Правила
- Разделители:
{и} - Разделитель вариантов:
| - Поддерживает вложенность произвольной глубины
- Пустые варианты допустимы (дают пустую строку)
- Разрешение идёт от самого внутреннего выражения наружу
Перестановки [a|b|c]
Выбирает N элементов, перемешивает их и объединяет с разделителями.
Простые перестановки
Все элементы включены, разделены пробелами:
[1|2|3|4]
Примеры результата: 1 4 3 2, 2 3 4 1, 3 2 4 1
С разделителем
Единый разделитель указывается в < > в начале:
[<, > 1|2|3|4]
Примеры результата: 2, 1, 4, 3 · 4, 3, 2, 1
Важно: Нет пробела между [ и <разделитель>.
Разделители для каждого элемента
У каждого варианта может быть свой разделитель, заданный через <sep> перед предшествующим |. Разделитель перемещается вместе с элементом при перемешивании.
[<, > 1|2|3 < and >|4]
Примеры результата: 1, 3, 2 and 4 · 3, 1, 2 and 4
Авто-пробелы: Словесные разделители, такие как <и> или <или>, автоматически дополняются пробелами: <и> даёт и . Знаки препинания (<,>) не дополняются.
Перестановки с комбинациями
Настраиваемое мин./макс. количество элементов и разделители:
[<minsize=1;maxsize=3;sep=", ";lastsep=" and "> apple|plum|orange|apricot]
Примеры результата: apple, plum and orange · apple and apricot · orange
Параметры конфигурации
| Параметр | По умолчанию | Описание |
|---|---|---|
minsize | количество всех | Минимальное количество выбираемых элементов |
maxsize | количество всех | Максимальное количество выбираемых элементов |
sep | " " (пробел) | Разделитель между элементами (кроме последнего) |
lastsep | как sep | Разделитель перед последним элементом |
Правила перестановок
- Разделители:
[и] - Блок конфигурации
<...>должен следовать сразу за[ - Параметры конфигурации разделяются точкой с запятой
- Строковые значения в конфигурации берутся в кавычки:
sep=", " - Перечисления и перестановки могут быть вложены в варианты
- HTML-элементы могут быть вариантами
sepсоединяет всё до последней пары,lastsep— саму пару: при двух выбранных элементах появляется толькоlastsep, при одном — ни тот, ни другой
Переменные %var%
Определяет переменную для многократного использования, которая подставляется везде, где встречается. Объявить её можно двумя директивами, и выбор не косметический: #set — макрос, его значение подставляется заново при каждой ссылке, и spintax внутри перекатывается; #def раскатывает значение один раз за рендер и отдаёт этот единственный результат всем ссылкам. (Один рендер — это один вывод; тот же seed воспроизводит его.) Пока значение — простой текст, разницы нет; она появляется в тот момент, когда внутри появляется выбор.
#set %VARIABLE_NAME% = value or spintax structure
#def %VARIABLE_NAME% = value or spintax structure
Примеры
#set %name% = John
#set %greeting% = {Hello|Hi|Hey}
#set %items% = [<minsize=2;maxsize=3;sep=", ";lastsep=" and "> apples|oranges|bananas]
Some text with %name% and %greeting%, also %items%.
And once more, %greeting% — a second reference.
/# %greeting% above may differ between the two references — #set re-rolls.
#def picks once and keeps it: #/
#def %tone% = {friendly|warm|upbeat}
A %tone% intro, and a %tone% outro — always the same word.
Правила переменных
#setи#defдолжны начинаться с начала строки- Имена переменных заключаются в
%:%name% - Имена состоят из букв, цифр и подчёркиваний, а ссылки нечувствительны к регистру:
%Tone%и%tone%— одна переменная - Значения могут содержать любой синтаксис spintax (перечисления, перестановки, другие переменные)
#set— макрос: раскрывается при обращении, а не при определении, и его значение — вместе со spintax внутри — подставляется заново и перекатывается при каждой ссылке#defраскатывает значение один раз за рендер и держит результат везде. Именно этим держатся повторы: существительное и построенные от него падежные формы, число для блока{plural}, любая фраза, которая обязана повторяться слово в слово#defделает переменную согласованной с самой собой, но не связывает две переменные.#def %Noun%и#def %NounGen%— два независимых раската, они могут выпасть на разные слова: формы, которые обязаны согласоваться, должны идти из одного раската — одна основа в#def, на которую ссылается каждая падежная форма, либо синонимы, склоняющиеся одинаково, с окончанием, вынесенным за определение- Имя определяется один раз. Второе определение того же имени помечается как
definition.duplicate-name, но рендер всё равно идёт: между двумя одинаковыми директивами побеждает последняя, а если имя делят#setи#def, побеждает#def— независимо от того, какая из них шла первой - Ссылка без определения печатает саму себя:
%missing%остаётся в выводе, а не исчезает - Ни одна из директив не переходит через
#include: включённый шаблон не видит локальных переменных родителя, а его собственные не утекают наверх. Раскатанная форма попадает внутрь только как переменная времени выполнения - Строки
#setи#defудаляются из результата - Подробно: см. руководство по переменным — области видимости и подвох с перекатом, и грамматически безопасную синонимизацию — падежные семьи
Области видимости переменных
Хост может подавать переменные из нескольких мест. Если одно и то же имя есть в нескольких, побеждает сильнейшее:
- Переменные времени выполнения (сильнейшие) — то, что хост передаёт в вызов рендера:
contextв@spintax/core, атрибуты шорткода в плагине WordPress:[spintax slug="greeting" name="Alice"] - Локальные переменные — объявленные через
#setили#defвнутри шаблона - Глобальные переменные (слабейшие) — значения по умолчанию на уровне хоста, например страница настроек плагина
Условия {?VAR?then|else}
Условный синтаксис — собственная конструкция языка: в прототипе GTW ничего подобного не было. Если {a|b} — это равномерный случайный выбор, не смотрящий на переменные, то {?VAR?then|else} выбирает по тому, есть ли значение у %VAR%.
Используйте для решений по значению: показывать строку про бесплатный тариф только при наличии тарифа, рендерить блок про-возможностей только когда пользователь на платном тарифе, прятать CTA, который сейчас не применим.
Предварительный проход отрабатывает до раскрытия %var% и до случайного выбора веток, поэтому falsy-ветка отбрасывается полностью — ничего внутри неё не вычисляется.
Формы
{?VAR?then} ← truthy ⇒ then; falsy ⇒ empty
{?VAR?then|else} ← truthy ⇒ then; falsy ⇒ else
{?!VAR?then|else} ← inverted
{?HasFreeTier? — free tier available since %founded%|, trusted since %founded%}
Truthy и falsy
Правило проще, чем в JavaScript — truthy = хотя бы один непробельный символ:
Значение %VAR% | Truthy? |
|---|---|
| не объявлена | falsy |
| пустая строка | falsy |
| только пробелы | falsy |
"0", "false" | truthy (непустые строки) |
| любой другой текст или HTML | truthy |
Правила условий
- Имена переменных соответствуют тому же regex, что и
%var%(регистронезависимы) - Префикс
!инвертирует проверку:{?!VAR?нет данных} - Первый
|на нулевой глубине разделяетthenиelse; последующие|остаются литералами вelse - Вложенные условия раскрываются outer-first — falsy-ветки короткозамыкаются
- Составная логика (
&&,||, сравнения) не поддерживается — считайте guard-переменную в ассемблере - Кривые формы (
{??yes},{?VAR}) не падают — песочница отмечает их предупреждениями - Подробно: см. руководство по условному spintax с примерами и анти-паттернами
Плуралы {plural %n%: язык|языка|языков}
Подбирает грамматически верную форму слова под число. Счётчик идёт до двоеточия, формы — после, через |.
Форму выбирает локаль рендера, а не шаблон, поэтому количество форм зависит от локали: русскому нужно три, английскому — две.
{plural %n%: form1|form2} ← 2-form locale (en, de, es…)
{plural %n%: form1|form2|form3} ← 3-form locale (ru, uk, sr…)
#def %LangCount% = 5
supports %LangCount% {plural %LangCount%: language|languages}
← supports 5 languages
Формы по локалям
Локаль сопоставляется по языковому субтегу, так что ru-RU и ru ведут себя одинаково:
| Локаль | Форм | Выбор по числу |
|---|---|---|
ru, uk, be, sr, hr, bs | 3 | 1 · 2–4 · 5 и больше |
все остальные, включая en | 2 | ровно 1 · всё остальное |
Если форм не столько, сколько нужно локали, движок отдаёт plural.arity и оставляет блок видимым в полноширинных скобках — молча неправильное склонение в продакшен не уедет.
Правила плуралов
- Открывающая часть литеральна, вместе с пробелом:
{plural.{plural: x}и{pluralN: x}плуралами не являются - Двоеточие обязательно — оно отделяет счётчик от форм
- Счётчик — это ссылка
%Var%или целочисленный литерал; переменные в счётчике подставляются до выбора формы - Отрицательные числа берутся по модулю;
0получает форму «всё остальное» (по-русски — «языков») - Переменная-счётчик должна быть
#def, а не#set—#setэто макрос, поэтому значение вида{1|4|9}на момент выбора формы всё ещё неразрешённый spintax, и блок отрендерится пустым. В песочнице этоplural.count-macro - Нечисловой или неопределённый счётчик стирает блок, а не угадывает форму
- Подробно: см. руководство по плуралам — русские правила трёх форм и разборы примеров
Включения #include
Встраивает другой шаблон в позицию директивы. #include — единственная конструкция, которую движок не может обеспечить сам: хранилища шаблонов у него нет, поэтому резолвер, превращающий ссылку в текст шаблона, даёт хост. Там, где резолвер не установлен — в песочнице и на MCP-сервере этого сайта, и то и другое намеренно, — директива инертна и остаётся в выводе обычным текстом.
#include "hero-text"
/# wrong: text before the directive on the same line leaves it literal #/
Intro: #include "hero-text"
Правила включений
- Директива должна занимать всю строку. Отступ слева допустим, текст после ссылки — нет:
Текст #include "hero"остаётся литералом - Ссылка берётся в двойные кавычки; одинарные кавычки или их отсутствие — уже не директива
- Разрешение ссылки — дело хоста: плагин WordPress ищет по slug или числовому ID, JavaScript-хост передаёт
includeResolver - Без резолвера строка остаётся в выводе литералом; если у резолвера такого шаблона нет, строка вместо этого удаляется — неизвестная цель молча стоит вам целого блока
- Включённые шаблоны могут содержать свои переменные и spintax, а также свои
#include - Включения разрешаются после того, как в родителе разыграны перечисления и перестановки: include в победившей ветке встраивается, а в отброшенной не случается вовсе
- Цепочки работают (шаблон включает шаблон, который включает следующий); шаблон, включающий сам себя — напрямую или по циклу, — обрывается на первом повторе, без ошибки и без диагностики
- Дочерние шаблоны наследуют глобальные переменные и переменные времени выполнения, но не локальные
#set/#defродителя, и свои наверх не отдают - Включение не может быть значением определения:
#def %x% = #include "y"отклоняется какdef.include-in-value - До рендера
validate()сообщает о неизвестной цели, только если хост передал список известных ссылок;extract()возвращает ссылки, нужные шаблону, — так хост их предзагружает - Подробно: см. руководство по композиции шаблонов — паттерн ассемблера, которым пользуется большинство пайплайнов
Комментарии /#...#/
Текст между маркерами комментариев удаляется из результата до любой другой обработки.
/#
This is a comment section.
It can span multiple lines.
It won't appear in output.
#/
Правила комментариев
- Открывающий разделитель:
/# - Закрывающий разделитель:
#/ - Может занимать несколько строк
- Вложенность не поддерживается
- Удаляются до начала любой другой обработки
Вложенность
Все элементы синтаксиса могут быть вложены друг в друга на произвольную глубину:
{option1|[<, > sub1|sub2|sub3]|option3}
[<minsize=2;maxsize=3;sep=", ";lastsep=" and "> {red|blue} apples|{big|small} oranges|bananas]
#set %var% = {a|[b|c]}
Постобработка
Движок автоматически корректирует текст после генерации:
- Защита URL, email-адресов, доменов, десятичных чисел и аббревиатур от изменения регистра
- Удаление дублирующихся пробелов и табуляций
- Удаление пробелов перед знаками препинания (
,.!?) - Добавление пробела после знаков препинания, где он отсутствует
- Заглавная буква в начале результата (с пропуском HTML-тегов)
- Заглавная буква после знаков конца предложения
- Заглавная буква после блочных HTML-тегов
- Заглавная буква после переносов строк
- Восстановление защищённых заполнителей
Сводка синтаксиса
| Возможность | Синтаксис | Поведение |
|---|---|---|
| Перечисление | {a|b|c} | Выбрать один случайный вариант |
| Перестановка | [a|b|c] | Выбрать N, перемешать, объединить |
| Разделитель | [<sep> a|b|c] | Перестановка с единым разделителем |
| Разделитель элемента | [<, > a|b <x>|c] | Перестановка с пользовательскими разделителями |
| Комбинации | [<config> a|b|c] | Перестановка с мин./макс. количеством |
| Переменная | #set %var% = {a|b} | Подставляется заново при каждой ссылке — spintax внутри перекатывается |
| Переменная (раз за рендер) | #def %var% = {a|b} | Один раскат за рендер, результат держится везде — так согласуются формы и окончания |
| Условие | {?VAR?then|else} | then, если truthy; иначе else |
| Плурал | {plural %n%: язык|языка|языков} | Согласует форму слова с числом по локали |
| Включение | #include "slug" | Встраивание другого шаблона — ссылку разрешает хост |
| Комментарий | /#...#/ | Удаляется из результата |
Язык вырос из своего прототипа — Generating The Web (GTW); шаблоны, написанные для GTW, по-прежнему работают без изменений.