Call / Jump
シーンからシーンへ――話の流れを操る術を授けますわ。
Pasta は if も while も持たない宣言的な言語ですけれど、ご心配なく。
シーン定義と Call だけで、十分に豊かな会話の分岐が描けますのよ。
Pasta DSL は宣言的言語であり、命令型の制御構文(if / while 等)を持たない。制御フローはシーン定義と Call(> / >)で実現する。Call は指定したシーンを呼び出し、実行後に元の場所へ戻る。
Call の基本
Call 行はインデントした行頭に >(または >)を置き、呼び出すターゲットを続ける。
*メイン
ぱすた:自己紹介を始めます
>自己紹介
ぱすた:自己紹介が終わりました
・自己紹介
ぱすた:私はぱすたです
Call ターゲットの形式
Call は 2 つのターゲット形式をサポートする。
| 形式 | 構文 | 説明 |
|---|---|---|
| シーン参照 | >シーン名 | スコープから見えるローカル/グローバルシーンを前方一致検索して呼び出す |
| 動的ターゲット | >$変数名 | 変数の値をシーン名として解決して呼び出す |
*スタート
>次のステップ # シーン参照
・次のステップ
ぱすた:次のステップに来ました
*動的呼び出し
$target:挨拶朝
>$target # 変数値「挨拶朝」をシーン名として解決
前方一致によるターゲット解決
Call のターゲット解決は前方一致で候補を列挙する。次のシーンがあるとき:
*挨拶朝
*挨拶昼
>挨拶→ 「挨拶朝」「挨拶昼」の両方が候補>挨拶朝→ 「挨拶朝」のみが候補
候補が複数あれば、そこからランダムに 1 つが選択される。これにより、毎回同じにならない自然な会話を作れる。
スコープ解決アルゴリズム
Call 文と単語参照(@)は同一のスコープ解決アルゴリズムを使う。Call にスコープ修飾子は不要である。
- ローカル検索: 現在のグローバルシーンスコープ内のローカルシーンを、指定プレフィックスで前方一致検索
- グローバル検索: すべてのグローバルシーンを、指定プレフィックスで前方一致検索
- マージ: 両検索結果を結合して候補リストを生成
- 選択: 候補リストからランダムに 1 つを選択(キャッシュベースの順次消費)
異なるグローバルシーンスコープでは、同じシーン名でも見えるローカルシーンが異なるため、検索結果が変わり得る。
重要: Call(
>)はシーン辞書のみを検索し、単語辞書は参照しない。逆に単語参照(@)はシーン辞書を参照しない。両者は同じスコープ解決アルゴリズムを共有するが、検索対象の辞書は分離されている。
条件分岐の実現
条件分岐は Lua ブロック内で実装し、act:call() で分岐先を選ぶ。
*メイン
$スコア:75
```lua
function SCENE.分岐判定(act)
local save, var = act:init_scene(SCENE)
local スコア = var.スコア
local label
if スコア >= 90 then
label = "優秀"
elseif スコア >= 50 then
label = "合格"
else
label = "不合格"
end
return act:call(SCENE.__global_name__, label, {})
end
```
>@分岐判定
・優秀
ぱすた:優秀です!
・合格
ぱすた:合格です
・不合格
ぱすた:不合格です
フィルター(将来変更あり)
Call ターゲットの後ろに &key=value 形式のフィルターを付けて、属性で候補を絞り込む構文が予約されている。
>シーン名&author=Alice&genre=comedy
フィルターは比較・条件判定のため、コロンではなく比較演算子(= > < など)を使う。
将来変更あり: フィルター機能は将来用に宣言されているだけで、現状では無視される。比較演算子ベース(
&score>50など)への拡張も将来予定である。
引数リスト
Call や関数呼び出しには、括弧で名前付き引数を渡せる。引数どうしは空白で区切る。
@calculate(x:10 y:20)
実装状態: 引数リストは AST レベルで解釈されるが、トランスパイラ層以降の処理は順次対応予定である。
これで会話に「流れ」と「分岐」が生まれますわ。 シーンを呼び合うだけで物語が動く――なかなか痛快でしょう? 次は値の正体「リテラル型」へ。
権威的仕様: Call の厳密な定義は doc/spec/04-call-spec.md を参照。スコープ解決の共通仕様は doc/spec/10-words.md も参照。