Условный spintax: {?VAR?then|else}
Иногда выбор между двумя формулировками — не подбрасывание монетки, а зависимость от факта. У казино есть крипта или нет. У продукта есть бесплатный тариф или нет. Условный spintax — это value-driven аналог {a|b}: вместо случайного выбора движок выбирает по тому, truthy ли переменная.
Три формы
Условный синтаксис добавляет к семейству GTW три новых токена. Все три раскрываются до enum-ов и permutations — это pre-pass, а не рантайм-ветка.
| Форма | Смысл |
|---|---|
{?VAR?then} |
Рендерит then, если %VAR% truthy; иначе ничего. |
{?VAR?then|else} |
Рендерит then, если truthy; иначе else. |
{?!VAR?then[|else]} |
Инверсия: then рендерится, когда %VAR% falsy. |
Префикс ! переворачивает проверку. Отдельной формы {?VAR??else} нет — если нужна только falsy-ветка, пишется {?!VAR?else}.
Truthy и falsy
Правило truthy сделано проще, чем в JavaScript. Не нужно помнить про крайние случаи приведения.
Значение %VAR% | Truthy? |
|---|---|
| не объявлена вовсе | falsy |
пустая строка "" | falsy |
| только пробельные символы (пробелы, табы, переносы) | falsy |
"0" | truthy (строка «0» непустая) |
"false" | truthy (строка тоже непустая) |
"x", "<p>…</p>", сырой spintax | truthy |
И всё. Truthy — это хотя бы один непробельный символ. Если когда-нибудь понадобится JavaScript-style truthiness — посчитайте её в ассемблере до того, как передавать переменную в шаблон.
Проверка читает сырое значение
Truthy-проверка идёт по сырому значению переменной. Вложенные %var% внутри этого значения не раскрываются для проверки — это произойдёт позже, на стадии expandVariables.
#set %X% = %Other%
{?X?yes|no} → "yes"
Даже если %Other% раскроется в пустую строку, сырое значение %X% — это строка %Other%, а она непустая. Хотите value-aware проверку — задавайте %X% как чистую '1' или ''. Считайте guard в ассемблере и передавайте уже вычисленным.
Двухпроходный пайплайн
Движок обрабатывает шаблон по стадиям. У условий — два прохода:
1. вырезать комментарии
2. извлечь #set
3. слить переменные
4. применить условия ← проход 1
5. раскрыть %var%
6. применить условия ← проход 2
7. раскрыть перечисления
8. раскрыть permutations
9. post-process
Проход 1 обрабатывает условия, написанные напрямую в теле шаблона. Идёт до раскрытия переменных — falsy-ветка отбрасывается, не тратя циклы на свои %var%.
Проход 2 нужен, если значение переменной само содержит условие:
#set %CTA% = {?HasBonus?Заберите бонус|Перейти к депозиту}
%CTA% /# проход 2 видит условие после раскрытия #/
Уточнение: проход 2 не зацикливается. Если выбранная ветка содержит свежую %var%-ссылку, она остаётся литералом — третьего прохода нет. На реальных шаблонах это нормально: guard-переменные в ветках обычно те же самые (%HasCrypto%, %HasFiat%), которые уже разрешились на проходе 1.
Боевой пример: блок банкинга
Это та задача, под которую синтаксис и проектировался. Шаблон обзора казино рендерит секцию «Депозиты и выводы». Где-то принимают крипту, где-то фиат, где-то и то, и другое, а где-то ничего не известно.
Ассемблер переменных считает два guard-флага и три заранее отрендеренных HTML-куска:
%CasinoHasCrypto% "1" или ""
%CasinoHasFiat% "1" или ""
%CasinoCryptoSection% /# готовый <h3> + <ul> #/
%CasinoFiatSection%
%CasinoLimitsSection%
Шаблон-оркестратор использует условия, чтобы аккуратно обработать казино без банкинговых данных:
%CasinoFiatSection%%CasinoCryptoSection%%CasinoLimitsSection%
{?!CasinoHasCrypto?{?!CasinoHasFiat?<p>Информация о платёжных методах будет добавлена.</p>}}
Нижняя строка читается так: если нет ни крипты, ни фиата — рендерить запасной абзац. Вложенные условия короткозамкнуты outer-first: при %CasinoHasCrypto% = "1" внешняя инверсия даёт falsy, и внутренняя проверка вообще не выполняется.
Сравните со старым обходом — guard-переменной, заполняемой spintax-перечислением с весами, которое случайно выбирало между пустой строкой и запасным абзацем. Работало, но рендер был недетерминированным, и в шаблон тащилась лишняя переменная. Условный синтаксис говорит ровно то, что значит.
Random pick vs условие — не путайте
Анти-паттерны
1. Булевы операторы
В условном spintax нет &&, ||, != или ==. Синтаксис намеренно минимальный. Если нужна составная логика — считайте булеву в ассемблере переменных:
/# не работает: не поддерживается #/
{?HasCrypto && HasLicense?…}
/# правильно: композиция в ассемблере #/
#set %ShowCryptoBlock% = {?HasCrypto?{?HasLicense?1}}
{?ShowCryptoBlock?…}
Ассемблер может использовать любой язык и любую логику. Spintax остаётся инструментом шаблонизации, а не языком программирования.
2. #set внутри ветки
Директивы #set извлекаются до любого прохода условий — это шаг 2 пайплайна. Строка #set внутри ветки {?…?…} отрабатывает безусловно; условие управляет только тем, попадёт ли пустой остаток этой строки в выбранную ветку.
/# Обе #set строки сработают. Победит вторая. #/
{?A?
#set %x% = first
|}{?A?
#set %x% = second
|}%x%
→ всегда "second", независимо от A
Если нужна условная присваивающая логика — делайте её в ассемблере.
3. Литеральный | на верхнем уровне в then
Первый | на глубине 0 внутри тела — разделитель then и else. Любой следующий | на глубине 0 остаётся литералом, но в ветке else, а не then.
{?A?x|y|z} /# A truthy → "x"; A falsy → "y|z" #/
Если в then нужен литеральный |, оберните в фигурные скобки или используйте HTML-сущность |:
{?A?{x|y}} /# внутренний | глубины 1, не разделитель #/
{?A?x | y} /# явная сущность, рендерится как "x | y" #/
4. Путаница pre-pass и runtime
Условия — это pre-pass до раскрытия enum-ов и permutations. Falsy-ветка полностью отбрасывается — никакой random pick из неё не сработает. Если в falsy-ветке зашита %RandomQuirk%-permutation, при falsy условии она вообще не вычисляется. Это и есть смысл.
Терпимый парсер — кривое не падает
Голый ? в обычной прозе («Как? Вот так?») встречается часто. Парсер намеренно мягкий: любой {?…, не подходящий под грамматику, остаётся литералом, а не падает с ошибкой.
Валидатор на этом сайте (и в песочнице) предупреждает про сбалансированно-кривые формы — чтобы вы заметили проблему в редакторе:
| Форма | Поведение |
|---|---|
{?VAR?then — нет закрывающей } | предупреждение: незакрытая { (как любая другая) |
{??yes} — пустое имя | предупреждение: пустое имя в условии |
{?VAR} — нет ? после имени | предупреждение: нет разделителя ? |
Как? Вот так? | обычная проза, без предупреждений |
В рантайме все кривые формы не бросают исключение. Движок продолжает обычные стадии enum/permutation; сбалансированно-кривые токены могут быть подобраны более поздними стадиями, так что не рассчитывайте на сохранение их буквой.
Короткий чеклист
- Используйте
{?VAR?…}, когда выбор зависит от значения. Никогда не пишите{a|b}в надежде, что движок поймёт по переменной. - Для «отсутствия» используйте
{?!VAR?…}, не заводите отдельную «no» guard-переменную. - Составные булевы — в ассемблере. Условия в spintax атомарные.
- Никогда не кладите
#setвнутрь ветки — она отрабатывает безусловно. - Если в
thenнужен литеральный|— оберните в{…}или используйте|. - Truthy = есть хотя бы один непробельный символ. Точка. Особую truthiness считайте в ассемблере.
- Готовые HTML-куски секций кладите в переменные. Условия их гасят и включают.
Попробовать вживую
В песочнице в дефолтном шаблоне есть пример {?HasFreeTier?…|…}. Переключайте %HasFreeTier% между 1 и пустым — увидите обе ветки без правки кода.