条件 spintax:{?VAR?then|else}

有时两种说法之间的选择不是抛硬币,而是取决于一个事实。产品有免费档,或者没有;商户收加密货币,或者只收法币。条件 spintax 是 {a|b} 枚举分支的按值对应物:不随机挑,而是看变量是否为真。

三种形式

条件语法在 GTW spintax 家族之上新增三个标记。三个都在枚举和排列解析之前求值——它是前置工序,不是运行时分支。

形式含义
{?VAR?then} %VAR% 为真时渲染 then;否则什么都不渲染。
{?VAR?then|else} 为真渲染 then;为假渲染 else
{?!VAR?then[|else]} 取反:%VAR%时渲染 then

! 前缀翻转判断。没有单独的 {?VAR??else} 形式——只要假值分支就写 {?!VAR?else}

真值与假值

真值规则有意比 JavaScript 简单。你不该需要背强制转换的边界情况。

%VAR% 的值为真?
根本没有声明
空字符串 ""
只有空白(空格、制表、换行)
"0"(字符串零非空)
"false"(仍是非空字符串)
"x""<p>…</p>"、原始 spintax

规则就这么多。真 = 至少一个非空白字符。需要 JavaScript 风味的真值判断,就在拼装器里先算好再传进来。

真值读的是原始值

查的是变量存储的原始值。值里嵌套的 %var% 引用在真值判断时展开——要等 expandVariables 作为独立工序运行时才展开。

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

即使 %Other% 展开后是空串,%X% 的原始值也是字面的 %Other%——非空,所以为真。想要感知取值的真值判断,就把 %X% 直接写成干净的 '1'''——守卫在拼装器里算好再传。

两遍流水线

引擎按工序处理模板。条件有两次求值

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

第一遍处理直接写在模板体里的条件。它在变量展开之前运行,假值分支被丢弃时不会在其中的 %var% 上浪费循环。

第二遍处理变量的值本身含有条件的情况:

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

一点澄清:第二遍不循环。选中的分支里如果有新的 %var% 引用,它保持字面——没有第三遍展开。对绝大多数真实模板这没问题:条件分支里用的守卫(%HasCrypto%%HasFiat%)通常就是第一遍已解析过的那些。

演练:支付方式小节

这就是这套语法为之设计的场景。产品页渲染“支付方式”小节。有的商户收加密货币,有的只收法币,有的两者都收,少数还没配置任何支付集成。

变量拼装器算出两个守卫加三块预渲染 HTML:

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

编排模板用条件给“没有支付数据”的条目把守可选的兜底行:

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

底下那行读作:如果没有加密也没有法币,渲染兜底段落。嵌套条件由外向内短路:%HasCrypto% = "1" 时,外层假值判断立刻失败,内层条件根本不会被求值。

对比旧的变通法——用 spintax 按加权概率在空串和兜底段落之间随机挑的守卫变量。能用,但渲染输出非确定,模板还得多穿一个变量。条件语法说的就是它的意思。

抽取与条件——别搞混

反模式

1. 布尔运算符

条件 spintax 里没有 &&||!===。语法有意保持极简。需要复合逻辑,就在变量拼装器里算好布尔值:

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

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

拼装器可以用任何宿主语言和任何逻辑。spintax 保持是模板工具,不是编程语言。

2. 分支里的 #set

#set 指令在任何条件工序之前被提取——那是流水线的第 2 步。写在 {?…?…} 分支里的 #set 无条件生效;条件只控制那一行剩下的空壳是否留在选中分支的文本里。

/# Both #set lines fire. The second wins. #/
{?A?
#set %x% = first
|}{?A?
#set %x% = second
|}%x%
→ always "second", regardless of A

需要条件赋值,就在拼装器里做。

3. then 里当字面用的顶层 |

正文里第一个深度为 0 的 |thenelse 分开。之后深度 0 的 | 保持字面——但落在 else 分支里,不在 then 里。

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

then 分支需要字面的 | 时,用嵌套花括号包住,或用 HTML 实体 &#124;

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

4. 把前置工序和运行时搞混

条件是在枚举和排列解析之前的前置工序。也就是说假值分支被整个丢弃——里面的随机选择永远不会触发。你的模板在假值分支深处埋了个 %RandomQuirk% 排列?条件为假时那个排列根本不被求值。很好,这正是设计目的。

宽容解析——写坏的形式不致命

散文里的裸 ?("How? Like this?")很常见。解析器有意宽容:任何不符合文法的 {?… 保持字面,不抛错。

本站的校验器(以及演练场)会把括号配平但形式坏掉的形状标为警告,让你在编辑器里就抓到:

形式处理
{?VAR?then ——没有闭合的 }警告:未配对的开括号(和任何 { 一样)
{??yes} ——名字为空警告:坏掉的条件(名字为空)
{?VAR} ——缺 ? 分隔符警告:坏掉的条件(缺分隔符)
How? Like this?普通散文,无警告

运行时,所有坏掉的形式都不抛错。引擎继续走常规的枚举/排列工序;括号配平的坏标记可能被后面的工序吃掉,所以不要指望字面保留。

快速检查清单

  • 选择取决于值时用 {?VAR?…},绝不用 {a|b}
  • {?!VAR?…},别发明单独的 “no” 守卫变量。
  • 复合布尔在拼装器里算。spintax 条件是原子的。
  • 绝不把 #set 放进分支——它无条件生效。
  • then 需要字面 | 时,用 {…} 包或用 &#124;
  • 真 = 非空白,就这么多。特殊真值判断在拼装器预先算。
  • 随租户变形状的小节先预渲染成变量,条件负责把门。

现场试试

演练场EN的默认模板自带 {?HasFreeTier?…|…} 示例。把 %HasFreeTier%1 和空之间切换,什么都不用多打就能看到两个分支。


继续本系列