条件付きspintax:{?VAR?then|else}
2つの言い回しのどちらを出すかが、コイン投げではなく事実で決まることがあります。その製品に無料プランがあるか、ないか。その店舗が暗号資産を受け付けるか、法定通貨だけか。条件付きspintaxは {a|b} の列挙分岐に対する値駆動の相方です:ランダムに選ぶのではなく、変数がtruthyかどうかで選びます。
3つの形
条件付き構文は、GTW系spintaxに3つのトークンを追加します。3つとも、列挙と順列が解決される前に評価されます — 実行時の分岐ではなく、前段のパスです。
| 形 | 意味 |
|---|---|
{?VAR?then} |
%VAR% がtruthyなら then を出力し、そうでなければ何も出しません。 |
{?VAR?then|else} |
truthyなら then、falsyなら else を出力します。 |
{?!VAR?then[|else]} |
反転:%VAR% がfalsyのときに then を出力します。 |
接頭辞 ! は判定を反転します。{?VAR??else} という別形はありません — falsy側だけが欲しいなら {?!VAR?else} と書いてください。
truthyとfalsy
truthyのルールは、JavaScriptのそれより意図的に単純です。型変換の細かい例外を覚える必要はありません。
%VAR% の値 | truthy? |
|---|---|
| そもそも宣言されていない | falsy |
空文字列 "" | falsy |
| 空白のみ(スペース、タブ、改行) | falsy |
"0" | truthy(文字列のゼロは空ではない) |
"false" | truthy(やはり空でない文字列) |
"x"、"<p>…</p>"、生のspintax | truthy |
ルールはこれだけです。truthy=空白以外の文字が1つ以上ある。JavaScript風の真偽判定が必要なら、変数を渡す前にアセンブラー側で判定してください。
真偽判定は生の値を読む
参照されるのは、変数に格納された生の値です。その値の中に入れ子になった %var% 参照は、真偽判定のためには展開されません — 展開は後段で expandVariables が別ステージとして走るときだけです。
#set %X% = %Other%
{?X?yes|no} → "yes"
仮に %Other% が空文字列に展開されるとしても、%X% の生の値は文字列リテラル %Other% です — 空ではないので truthy になります。値を見た真偽判定が欲しいなら、%X% を直接 '1' か '' にしてください。ガードはアセンブラーで計算してから渡します。
2パスのパイプライン
エンジンはテンプレートを段階的に処理します。条件には評価パスが2回あります:
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
パス1は、テンプレート本文に直接書かれた条件を処理します。変数展開より前に走るので、falsyの分岐はその中の %var% に手間をかけずに捨てられます。
パス2は、変数の値自体が条件を含む場合を処理します:
#set %CTA% = {?HasBonus?Claim bonus|Deposit now}
%CTA% /# pass 2 sees the conditional after expansion #/
1点だけ補足すると、パス2はループしません。選ばれた分岐に新しい %var% 参照が含まれていた場合、その参照はそのまま残ります — 3回目の展開パスはありません。現実のテンプレートではたいてい問題になりません。条件分岐で使うガード(%HasCrypto%、%HasFiat%)は、普通はパス1ですでに解決済みのものだからです。
実例:決済手段のセクション
この構文が設計された、まさにその用途です。製品ページが「決済手段」のセクションを描画します。暗号資産を受け付ける店舗、法定通貨だけの店舗、両方の店舗、そして決済連携をまだ設定していない店舗があります。
変数アセンブラーは2つのガードと、3つの事前レンダリング済み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" のときは外側のfalsy判定が即座に外れ、内側の条件は評価されません。
昔の回避策と比べてみてください — 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 行は無条件に実行され、条件が制御するのは、その行の空の残りが選ばれた分岐のテキストに残るかどうかだけです。
/# wrong: both #set lines fire — the second wins #/
{?A?
#set %x% = first
|}{?A?
#set %x% = second
|}%x%
→ always "second", regardless of 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}} /# inner | is depth 1, not a separator #/
{?A?x | y} /# explicit entity, renders as "x | y" #/
4.前段パスと実行時を混同する
条件は、列挙と順列が解決される前の前段パスとして走ります。つまりfalsyの分岐は完全に捨てられます — その中のランダム選択が発火することは一度もありません。falsy分岐の奥に %RandomQuirk% の順列があっても、条件がfalsyなら評価されません。それでいいのです。それが狙いです。
寛容なパース — 壊れた形も致命傷にならない
本文中の単独の ?(「どうやって? こうやって?」)はよくあります。パーサーは意図的に寛容です:文法に合わない {?… は、エラーを投げずにそのまま残します。
一方このサイトのバリデーター(とplayground)は、対応は取れているが壊れた形を警告として報告します。エディタの段階で気づけるようにするためです:
| 形 | 扱い |
|---|---|
{?VAR?then — 閉じ } がない | 警告:対応の取れていない開きかっこ(他の { と同じ扱い) |
{??yes} — 名前が空 | 警告:壊れた条件(名前が空) |
{?VAR} — 区切りの ? がない | 警告:壊れた条件(区切りがない) |
How? Like this? | ただの本文。警告なし |
実行時には、壊れた形はどれも例外を投げません。エンジンは通常の列挙・順列のパスへ進みます。対応が取れていて壊れているトークンは後段で飲み込まれることがあるので、リテラルのまま残ると当てにしないでください。
クイックチェックリスト
- 選択が値に依存するなら
{?VAR?…}を使い、{a|b}は使わない。 - 「否定用」のガード変数を作らず、
{?!VAR?…}を使う。 - 複合的な真偽値はアセンブラーで計算する。spintaxの条件は最小単位。
- 分岐の中に
#setを置かない — 無条件で実行される。 thenにリテラルの|が必要なら{…}で包むか|を使う。- truthyは「空白以外」だけ。特殊な真偽判定はアセンブラーで先に計算する。
- テナントごとに形が変わるセクションは、HTML断片を変数に事前レンダリングする。条件がその出し分けを担う。
その場で試す
playgroundENの既定テンプレートには {?HasFreeTier?…|…} の例が入っています。%HasFreeTier% を 1 と空で切り替えれば、他に何も打たずに両方の分岐を見られます。