Spintax 语法参考
Spintax 模板标记的完整参考。
枚举 { }
从列表中随机选择一个选项。
{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
规则
- 分隔符:
{和} - 分隔符:
| - 支持任意深度嵌套
- 空选项是有效的(产生空字符串)
- 从最内层表达式向外解析
排列 [ ]
选择 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
自动空格:单词分隔符如 <and> 或 <or> 会自动添加空格:<and> 产生 and 。标点分隔符(<,>)不会自动添加。
带组合的排列
可配置的最小/最大元素数量和分隔符:
[<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所在的行会从输出中删除- 深入了解: 见 变量指南(作用域与重抽陷阱)与 语法安全的同义替换(格变体家族)
变量作用域
宿主可以从多个来源提供变量。同一个名字出现在多处时,最强的一方获胜:
- 运行时变量(最强)— 宿主传入渲染调用的值:
@spintax/core中的context,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 |
条件规则
- 变量名遵循与
%var%相同的 regex(不区分大小写) !前缀反转检查:{?!VAR?缺失}- 0 层深度的第一个
|分隔then和else;后续的保持字面量在else中 - 嵌套条件由外向内求值 — falsy 分支短路
- 不支持组合逻辑(
&&、||、比较) — 在汇编器中预先计算守卫变量 - 畸形形式(
{??yes}、{?VAR})永不抛出 — 演练场将其标记为警告 - 深入了解: 见 条件 spintax 指南 含示例与反模式
复数 {plural %n%: language|languages}
根据数字选择语法正确的词形。计数放在冒号前,词形放在冒号后,用 | 分隔。
词形由渲染语言环境决定,而非模板本身,因此需要提供几种词形取决于该语言环境:英语需要两种,俄语需要三种。
{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 - 非数字或未定义的计数会清除该块,而不是猜测
- 深入了解: 见 复数 spintax 指南,含俄语三词形规则与完整示例
包含 #include
在指令所在位置嵌入另一个模板。#include 是引擎唯一无法独自完成的结构:它不保存模板,因此由宿主提供一个 resolver,把引用变成模板文本。在没有安装 resolver 的地方 — 本站的 playground 和 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 - 没有 resolver 时该行以字面形式留在输出中;resolver 找不到该模板时,该行反而会被删除 — 未知目标会悄悄让你丢掉整块内容
- 被包含的模板可以有自己的变量和 spintax,也可以有自己的
#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、电子邮件、域名、小数和缩写不被大写化
- 消除重复空格和制表符
- 移除标点符号前的空格(
,.!?) - 在缺少空格的标点符号后添加空格
- 将输出的第一个字母大写(跳过 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} | truthy 时渲染 then,falsy 时渲染 else |
| 复数 | {plural %n%: language|languages} | 按语言环境使词形与数字一致 |
| 包含 | #include "slug" | 嵌入另一个模板 — 引用由宿主解析 |
| 注释 | /#...#/ | 从输出中移除 |
这门语言已超越其原型 Generating The Web (GTW):为 GTW 编写的模板至今无需修改即可运行。