Условный 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>", сырой spintaxtruthy

И всё. 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-сущность &#124;:

{?A?{x|y}}             /# внутренний | глубины 1, не разделитель #/
{?A?x &#124; 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 нужен литеральный | — оберните в {…} или используйте &#124;.
  • Truthy = есть хотя бы один непробельный символ. Точка. Особую truthiness считайте в ассемблере.
  • Готовые HTML-куски секций кладите в переменные. Условия их гасят и включают.

Попробовать вживую

В песочнице в дефолтном шаблоне есть пример {?HasFreeTier?…|…}. Переключайте %HasFreeTier% между 1 и пустым — увидите обе ветки без правки кода.


Продолжить серию