Spintax condicional: {?VAR?then|else}

Às vezes a escolha entre duas redações não é cara ou coroa — depende de um fato. O produto tem plano gratuito, ou não tem. O lojista aceita criptomoeda, ou só moeda comum. O spintax condicional é a contraparte guiada por valor do ramo {a|b}: em vez de sortear, ele escolhe conforme a variável ser truthy ou não.

As três formas

A sintaxe condicional acrescenta três tokens à família spintax do GTW. Os três são avaliados antes de enumerações e permutações resolverem — são uma passagem prévia, não um desvio em tempo de execução.

FormaSignificado
{?VAR?then} Renderiza then se %VAR% for truthy; caso contrário, não renderiza nada.
{?VAR?then|else} Renderiza then se truthy; renderiza else se falsy.
{?!VAR?then[|else]} Invertida: renderiza then quando %VAR% é falsy.

O prefixo ! inverte a checagem. Não existe uma forma separada {?VAR??else} — se você só quer o ramo falsy, escreva {?!VAR?else}.

Truthy e falsy

A regra de truthy é de propósito mais simples que a do JavaScript. Você não deveria precisar decorar casos de borda de coerção.

Valor de %VAR%Truthy?
não declaradafalsy
string vazia ""falsy
só espaços em branco (espaços, tabs, quebras de linha)falsy
"0"truthy (o zero como string não é vazio)
"false"truthy (continua sendo uma string não vazia)
"x", "<p>…</p>", spintax crutruthy

É toda a regra. Truthy = pelo menos um caractere que não seja espaço em branco. Se algum dia você precisar de uma truthiness com sabor de JavaScript, faça essa checagem no montador, antes de passar a variável.

A truthiness lê o valor cru

A consulta é feita sobre o valor cru armazenado na variável. Referências %var% aninhadas dentro desse valor não são expandidas para a checagem — isso só acontece depois, quando expandVariables roda como etapa separada.

#set %X% = %Other%
{?X?yes|no}     → "yes"

Mesmo que %Other% fosse expandir para uma string vazia, o valor cru de %X% é a string literal %Other% — que não é vazia e, portanto, é truthy. Para ter uma truthiness ciente do valor, escreva %X% diretamente como '1' ou ''. Calcule a guarda no montador antes de passá-la.

O pipeline de duas passagens

O motor processa um template em etapas. Os condicionais ganham duas passagens de avaliação:

1. strip comments
2. extract #set / #def directives
3. merge variables
4. apply conditionals       ← pass 1
5. expand %var% references
6. apply conditionals       ← pass 2
7. resolve enumerations
8. resolve permutations
9. post-process

A passagem 1 cuida dos condicionais escritos direto no corpo do template. Ela roda antes da expansão de variáveis, então um ramo falsy é descartado sem gastar ciclos com os %var% dele.

A passagem 2 cuida do caso em que o valor de uma variável contém um condicional:

#set %CTA% = {?HasBonus?Claim bonus|Deposit now}
%CTA%                  /# pass 2 sees the conditional after expansion #/

Um esclarecimento: a passagem 2 não entra em laço. Se o ramo escolhido contiver uma nova referência %var%, essa referência fica literal — não existe uma terceira passagem de expansão. Para a maioria dos templates reais isso está de bom tamanho; as guardas usadas em ramos condicionais (%HasCrypto%, %HasFiat%) costumam ser as mesmas já resolvidas na passagem 1.

Exemplo prático: seção de meios de pagamento

Este é o caso para o qual a sintaxe foi desenhada. Uma página de produto renderiza uma seção "Meios de pagamento". Alguns lojistas aceitam cripto, outros só moeda comum, alguns aceitam os dois, e uns poucos ainda não têm integração de pagamento configurada.

O montador de variáveis calcula duas guardas e mais três blocos de HTML pré-renderizados:

%HasCrypto%    "1" or ""
%HasFiat%      "1" or ""
%CryptoSection%   /# already-rendered <h3> + <ul> #/
%FiatSection%
%LimitsSection%

O template orquestrador usa condicionais para liberar a linha de fallback dos registros sem dados de pagamento:

%FiatSection%%CryptoSection%%LimitsSection%
{?!HasCrypto?{?!HasFiat?<p>Payment details will be published shortly.</p>}}

Leia a última linha assim: se não tem cripto e não tem moeda comum, renderize o parágrafo de fallback. Condicionais aninhados fazem curto-circuito de fora para dentro, então quando %HasCrypto% = "1" a checagem falsy externa falha na hora e o condicional interno nunca é avaliado.

Compare com a gambiarra antiga — uma variável de guarda preenchida por spintax que sorteia entre string vazia e parágrafo de fallback com probabilidade ponderada. Funcionava, mas a saída era não determinística e o template precisava carregar mais uma variável. A sintaxe condicional diz exatamente o que quer dizer.

Sorteio x condicional — não confunda

Antipadrões

1. Operadores booleanos

Não existe &&, ||, != nem == no spintax condicional. A sintaxe é minimalista de propósito. Se você precisa de lógica composta, calcule o booleano no montador de variáveis:

/# wrong: not supported #/
{?HasCrypto && HasLicense?…}

/# right: compose in the assembler #/
#set %ShowCryptoBlock% = {?HasCrypto?{?HasLicense?1}}
{?ShowCryptoBlock?…}

O montador pode usar qualquer linguagem e qualquer lógica. O spintax continua uma ferramenta de template, não uma linguagem de programação.

2. #set dentro de um ramo

As diretivas #set são extraídas antes de qualquer passagem condicional — é o passo 2 do pipeline. Uma linha #set dentro de um ramo {?…?…} dispara incondicionalmente; o condicional só controla se a sobra vazia da linha permanece no texto do ramo escolhido.

/# wrong: both #set lines fire — the second wins #/
{?A?
#set %x% = first
|}{?A?
#set %x% = second
|}%x%
→ always "second", regardless of A

Se você precisa de atribuição condicional, faça no montador.

3. | de nível superior como literal no then

O primeiro | de profundidade 0 dentro do corpo separa then de else. Qualquer | seguinte na profundidade 0 fica literal — mas no ramo else, não no then.

{?A?x|y|z}     /# A truthy → "x"; A falsy → "y|z" #/

Se o ramo then precisa de um | literal, envolva em chaves aninhadas ou use a entidade HTML &#124;:

{?A?{x|y}}             /# inner | is depth 1, not a separator #/
{?A?x &#124; y}         /# explicit entity, renders as "x | y" #/

4. Confundir passagem prévia com tempo de execução

Condicionais rodam como passagem prévia, antes de enumerações e permutações resolverem. Isso quer dizer que um ramo falsy é completamente descartado — nenhum sorteio dentro dele chega a acontecer. Se o seu template tem uma permutação %RandomQuirk% lá no fundo do ramo falsy, essa permutação nunca é avaliada quando o condicional é falsy. Ótimo. É esse o ponto.

Parsing tolerante — formas malformadas não são fatais

Um ? solto no texto ("Como? Assim?") é comum. O parser é tolerante de propósito: qualquer {?… que não case com a gramática fica literal em vez de estourar.

O validador deste site (e o playground) sinaliza as formas balanceadas-porém-malformadas como avisos, para você pegá-las ainda no editor:

FormaTratamento
{?VAR?then — sem } de fechamentoaviso: chave aberta sem par (como qualquer outro {)
{??yes} — nome vazioaviso: condicional malformado (nome vazio)
{?VAR} — falta o separador ?aviso: condicional malformado (separador ausente)
How? Like this?texto comum, sem aviso

Em tempo de execução, nenhuma forma malformada estoura. O motor segue para as passagens normais de enumeração e permutação; tokens malformados mas balanceados podem acabar consumidos por etapas posteriores, então não conte com a preservação literal.

Checklist rápido

  • Use {?VAR?…} quando a escolha depende de um valor, nunca {a|b}.
  • Use {?!VAR?…} em vez de inventar uma variável de guarda "não".
  • Calcule booleanos compostos no montador. Os condicionais do spintax são atômicos.
  • Nunca ponha #set dentro de um ramo — ele dispara incondicionalmente.
  • Se o then precisa de um | literal, envolva em {…} ou use &#124;.
  • Truthy = não é espaço em branco, ponto final. Truthiness especial se calcula no montador.
  • Pré-renderize fragmentos de HTML em variáveis para seções que mudam de formato por inquilino. Os condicionais liberam a passagem.

Teste ao vivo

O playgroundEN já vem com um exemplo {?HasFreeTier?…|…} no template padrão. Alterne %HasFreeTier% entre 1 e vazio para ver os dois ramos sem digitar mais nada.


Continuar a série