はじめに
ようこそ、pasta の世界へ。わたくし Claudia が、このマニュアルの案内役を務めますわ。 ゴーストづくりは初めて? ……フンッ、心配なさらなくてよろしくてよ。手順から文法、Lua の書き方まで、 最後まできっちりお付き合いいたしますから。さあ、肩の力を抜いて、最初の一歩を踏み出しましょう。
pasta は、「伺か」のようなデスクトップマスコットを動かすための対話スクリプトエンジンである。 作者が書いた Pasta DSL の辞書を Lua にトランスパイルし、「ゴースト」の頭脳として動作させる。 本マニュアルは、ゴースト作者が pasta で辞書を書けるようになることを目的とした利用者向けガイドである。
このマニュアルが対象とするバージョン
| 項目 | 内容 |
|---|---|
| 対象 pasta バージョン | v0.2 系列 |
| Lua 方言(ランタイム) | LuaJIT 2.1(Lua 5.1 系+有効化された拡張) |
| 対象プラットフォーム | Windows |
本マニュアルは pasta の v0.2 系列を対象として記述する。パッチ番号(v0.2.x)の差異には依存せず、 系列単位での安定した文法・API を主たる記述対象とする。
ランタイムの Lua 方言は LuaJIT 2.1 である。これは Lua 5.1 系の言語仕様を基礎とし、
LuaJIT 独自の拡張(goto/ラベルや一部の 5.2 互換機能など)が有効化されたものである。
そのため、Lua のコードを書く際は Lua 5.1 系+ LuaJIT 拡張の文法・標準ライブラリを前提とすること。
バージョンの離れた Lua 5.5 等の他バージョンの仕様と混同しないよう注意する。外部 Lua リファレンスを
参照するときも、LuaJIT 2.1 / Lua 5.1 系に対応した資料を用いること(詳細は外部リンク集)。
安定機能と「将来変更あり」の区別について
本マニュアルは、安定した文法・API を主たる記述対象とする。一方で pasta は開発が継続しており、 未確定・実装予定・将来変更の可能性がある機能も存在する。それらは本文中で次のように明示し、 安定機能と視覚的・記述的に区別する。
⚠ 将来変更あり このように引用ブロックと「将来変更あり」の見出しを付した箇所は、未確定・実装予定、 または将来の版で仕様が変わる可能性がある機能を示す。安定版として依存する前に、 最新の情報を確認すること。
注記のない通常の本文は、現行の v0.2 系列で安定して利用できる機能として記述している。
このマニュアルの歩き方
目的に応じて、以下の各章へ進むとよい。
- 入門ガイド — pasta を初めて触る人向け。前提環境の準備から、最初の動くゴーストを作るまでを手順で示す。
- 文法リファレンス概要 — Pasta DSL の文法。シーン定義、アクション行、変数、単語定義などを章ごとに解説する。
- Lua マニュアル概要 — ランタイムが公開する Lua API とコーディングパターン。DSL では足りない複雑なロジックを書くときに参照する。
- 外部リンク集 — Lua 言語リファレンスなど、本マニュアル外の参照先をまとめている。
なお、Pasta DSL の厳密な文法仕様は本リポジトリの doc/spec/ が権威的ソースである。
本マニュアルは利用者向けにかみ砕いた説明と例を提供し、厳密な定義の所在は各文法章からのリンクで示す。
リポジトリ・開発者向け情報 pasta 本体のソースコード・ビルド方法・正式言語仕様などの開発者向け情報は、GitHub リポジトリ ekicyou/pasta にまとまっている。あわせて README も参照されたい。
準備はよろしくて? どこから読み始めても構いませんけれど、ゴーストづくりが初めてなら、 まずは入門ガイドから順に。……別に、あなたのためを思って言っているわけではありませんわよ。 さあ、熱く参りましょう!
入門ガイド
ようこそ、ゴースト作りの世界へ。わたくし Claudia が、最初の一歩から「動くゴースト」の完成まで、しっかり手を引いてさしあげますわ。プログラミングなんて触ったこともない――そんな方でも大丈夫。むずかしい顔をなさらないで、わたくしについていらっしゃいまし。
この入門ガイドは、pasta を初めて触る人が、ゼロから動作する最小限のゴーストを完成させるための章である。プログラミング初心者でも順番に手を動かせば最後まで到達できるよう、専門用語にはそのつど説明を添える。
このガイドで作るもの
最終的に作るのは、SSP 上で起動して挨拶やランダムな会話を返す「hello-pasta」という小さなゴーストである。pasta 公式が配布しているサンプルゴーストと中身が一致するように構成しており、途中の段階ではなく最終形が確実に起動する完全な一式になっている。
用語: 「ゴースト」とは、デスクトップ上に表示されてしゃべるキャラクターのこと。「伺か」と呼ばれる文化圏で使われる呼び名である。pasta はこのゴーストの「セリフと振る舞い」を記述するための仕組みを提供する。
対象読者
- プログラミング経験を問わず、自分のゴーストを作ってみたい人
- すでに伺か文化に親しんでいて、pasta でゴーストを書きたい人
- 旧来の辞書形式(里々・YAYA 等)から pasta へ移行したい人
このガイドの進め方
| 章 | 内容 |
|---|---|
| 前提環境と準備 | 必要な OS・ベースウェア・エディタと、文字コードの注意 |
| 最初のゴーストを作る | ゼロから起動可能な最小ゴーストまでの手順 |
まずは 前提環境と準備 で道具をそろえることから始める。
このガイドの先にあるもの
ゴーストが起動できるようになったら、次は中身を自由に書き換えたくなるはずである。会話やシーンの書き方を体系的に知りたくなったら Pasta DSL 文法 へ、より複雑なロジックを Lua で書きたくなったら Lua API / コーディング へ進むとよい。
準備はよろしくて? 完成までわたくしがついておりますから、ご安心なさいまし。……フンッ、別にあなたが心配だからではありませんわよ。さあ、参りましょう。
前提環境と準備
道具をそろえる時間ですわ。腕のいい職人ほど、いきなり手を動かさず、まず道具を整えるもの。少々退屈に思えるかもしれませんけれど、ここを丁寧にやっておけば、あとがずっと楽になりますのよ。
このページでは、ゴースト作りを始める前にそろえておくものと、最初につまずきやすい文字コードの注意を説明する。
動作環境
pasta ゴーストは、ベースウェアと呼ばれるソフトの上で動く。ベースウェアはゴーストを画面に表示し、会話を進行させる土台となるプログラムである。
| 項目 | 推奨 | 備考 |
|---|---|---|
| OS | Windows | 本マニュアルは Windows を対象とする |
| ベースウェア | SSP(推奨) | 最も広く使われている伺か互換ベースウェア |
| その他のベースウェア | 概ね動作 | 他の伺か互換ベースウェアでも概ね動作する |
用語: 「ベースウェア」はゴーストを動かす本体ソフト。ゴースト単体では動かず、必ずベースウェアの上にインストールして使う。SSP(Sakura Script Player)はその代表格である。
本ガイドの手順や画面例は SSP を前提に記述する。SSP は伺か関連サイトから入手できる。インストールと初期設定は SSP の案内に従うこと。
必要なもの
ゴーストを作るために、次のものを用意する。
- テキストエディタ
.pastaファイルやプレーンテキストの設定ファイルを編集する。UTF-8 で保存できるエディタであれば何でもよい(Visual Studio Code、メモ帳など)。 - pasta の SHIORI(
pasta.dll)と Lua ランタイム ゴーストの「頭脳」にあたる部分である。これは自分で書くものではなく、pasta が提供する実行ファイルを配置して使う。本ガイドでは、入手済みのpasta.dll一式を後の手順で配置する。
用語: 「SHIORI(しおり)」とは、ベースウェアからの問い合わせ(「起動したよ」「クリックされたよ」など)を受け取り、ゴーストの返事を組み立てるモジュールのこと。pasta では
pasta.dllがこの役割を担い、.pasta辞書に書いた会話を解釈して返す。
pasta.dll と Lua ランタイムは、公式のサンプルゴースト hello-pasta の配布物に含まれている。最も手軽なのは、起動可能な状態に組み上げられた hello-pasta を入手し、それを下敷きに自分の辞書を書き換えていく方法である。配布物の入手やビルド手順は pasta_sample_ghost の README を参照すること。
文字コードは必ず UTF-8
pasta の辞書ファイル(.pasta)と設定ファイルは、必ず UTF-8 で作成・保存する。これは最も重要な約束事である。
- 辞書本文には日本語を多用する。文字コードを誤ると、起動時に文字化けしたり、辞書が読み込めずゴーストが動かなくなる。
- 設定ファイル(
descript.txtなど)の先頭にもcharset,UTF-8を明記し、中身も UTF-8 で保存する。
用語: 「文字コード」とは、文字をコンピュータ上の数値に対応づける方式のこと。同じ「あ」でも方式が違えば別の数値になり、食い違うと文字化けする。UTF-8 は世界中の文字を扱える現代の標準的な方式である。
Shift_JIS の辞書から移行するときの注意
旧来の伺か辞書(里々や YAYA など)は、Shift_JIS という古い文字コードで書かれていることが多い。これらを pasta へ持ち込むときは、次の点に注意する。
- pasta の辞書は UTF-8 で書く前提である。Shift_JIS のまま
.pastaに貼り付けると文字化けする。 - 既存の Shift_JIS テキストを再利用する場合は、エディタの「文字コードを指定して開く / 保存する」機能で UTF-8 に変換してから保存すること。
- 変換後は、機種依存文字や半角・全角の取り違えがないか、実際に表示して確認するとよい。
道具はそろいましたわね。文字コードの落とし穴さえ越えれば、あとは怖いものなどございませんわ。さあ、いよいよ最初のゴーストを組み上げにまいりましょう。熱く参りますわよ!
最初のゴーストを作る
いよいよ本番ですわね。ゼロから一式を組み上げて、あなたのデスクトップに小さな住人を迎える――そのための手ほどきを、わたくしが最後まで付き添っていたしますわ。一行ずつ確実に。怖がらなくてよろしくてよ。
このチュートリアルでは、ゼロから起動可能な最小限のゴーストを組み立てる。最終的に出来上がるのは、pasta 公式のサンプルゴースト hello-pasta と同じ構成の一式である。
ここで重要なのは、完成形は確実に起動する完全なファイルセットだという点である。途中の段階だけを取り出しても起動しないことがあるため、最後まで一式をそろえてから SSP に読み込ませること。
前提となる環境やエディタの準備は 前提環境と準備 で済ませてあるものとする。すべてのファイルは UTF-8 で保存すること。
ゴーストのフォルダ構成
伺かのゴーストは、決まったフォルダ構成を持つ。これから作るのは次の構成である。
hello-pasta/
├── install.txt インストール情報
├── ghost/
│ └── master/
│ ├── pasta.dll SHIORI 本体(配置するだけ)
│ ├── pasta.toml pasta の動作設定
│ ├── descript.txt ゴーストの定義
│ └── dic/ 辞書(ここに会話を書く)
│ ├── actors.pasta
│ ├── boot.pasta
│ ├── talk.pasta
│ ├── click.pasta
│ └── choice.pasta
└── shell/
└── master/
├── descript.txt 見た目(シェル)の定義
├── surfaces.txt 表情と画像の対応
└── surface*.png 表情画像
役割を整理すると次のようになる。
| パート | 役割 |
|---|---|
install.txt | このフォルダが「ゴースト」であることと、インストール先を示す |
ghost/master/ | 会話・振る舞いを司る「中身」 |
shell/master/ | 画面に表示される「見た目」 |
用語: 「マスター(master)」は、ゴーストの標準的な中身・見た目を置く既定のフォルダ名である。1 体のゴーストに複数の見た目を持たせることもできるが、本ガイドでは
masterの 1 つだけを扱う。用語: 「シェル」とは、ゴーストの見た目(画像一式)のこと。会話の中身(辞書)とは分かれて管理される。
それでは、hello-pasta というフォルダを作り、その中にこの構成を組み立てていく。
ステップ 1: シェル(見た目)を用意する
ゴーストには表情画像が必要である。hello-pasta は「女の子」「男の子」の 2 キャラクターを持ち、それぞれ複数の表情画像(surface*.png)を使う。
これらの画像と surfaces.txt、shell/master/descript.txt は、サンプルゴーストの配布物にそのまま含まれている。自前で絵を用意するのは大変なので、まずは hello-pasta のシェル一式をそのまま使う。
shell/master/descript.txt は次の内容で、シェルの基本設定を表す。
charset,UTF-8
type,shell
name,master
craftman,ekicyou
craftmanw,どっとステーション駅長
seriko.use_self_alpha,1
sakura.balloon.offsetx,64
sakura.balloon.offsety,0
kero.balloon.offsetx,64
kero.balloon.offsety,0
surfaces.txt は、\s[0] のような番号と実際の画像ファイルを対応づける。次のように、各サーフェス番号に画像を割り当てている(抜粋)。
charset,UTF-8
surface0
{
element0,overlay,surface0.png,0,0
}
surface1
{
element0,overlay,surface1.png,0,0
}
用語: 「サーフェス(surface)」は、ゴーストの 1 枚 1 枚の表情画像のこと。
\s[0]のように番号で指定して切り替える。
画像と surfaces.txt は、pasta のサンプルクレートが自動生成する仕組みになっている。実ファイルは配布物から取得できる。シェル一式(shell/master/ 配下)は hello-pasta のシェル を参照すること。
ステップ 2: アクター辞書を書く(actors.pasta)
ここからが pasta 辞書の執筆である。最初に書くのはアクター辞書で、登場キャラクターと「表情の名前」を定義する。
ghost/master/dic/actors.pasta を作り、次のように書く。
# actors.pasta - アクター辞書(共通定義)
# 全ての .pasta ファイルで共有されるアクター定義
# pasta DSL ローダーが dic/*.pasta パターンで自動読み込み
# 女の子(sakura)- 赤色ピクトグラム surface0-8
%女の子
@笑顔:\s[0]
@通常:\s[1]
@照れ:\s[2]
@驚き:\s[3]
@泣き:\s[4]
@困惑:\s[5]
@キラキラ:\s[6]
@眠い:\s[7]
@怒り:\s[8]
# 男の子(kero)- 青色ピクトグラム surface10-18
%男の子
@笑顔:\s[10]
@通常:\s[11]
@照れ:\s[12]
@驚き:\s[13]
@泣き:\s[14]
@困惑:\s[15]
@キラキラ:\s[16]
@眠い:\s[17]
@怒り:\s[18]
ここでのポイントを説明する。
- 行頭の
#(全角シャープ)から始まる行はコメントで、動作には影響しない。説明用のメモである。 %女の子の%(全角パーセント)はアクター(登場キャラクター)の定義を始めるマーカーである。- インデントした
@笑顔:\s[0]は、「このキャラクターの『笑顔』という表情はサーフェス 0 番」という対応づけである。\s[0]がステップ 1 で見た表情画像の指定にあたる。 - これにより、以降の辞書では
@笑顔のような分かりやすい名前で表情を切り替えられる。番号を直接覚える必要はない。
用語: 「アクター辞書」は、ゴーストに登場するキャラクターと、その表情名を一覧定義したもの。会話辞書はこの定義を共有して使う。
インデントは、行頭に全角スペースを置いて表現している。pasta DSL ではインデントがブロックの内側であることを示す。
actors.pasta はすべての辞書から共有される。一度書けば、他の辞書ファイルから自由に表情名を参照できる。
ステップ 3: 起動・終了のあいさつを書く(boot.pasta)
次に、ゴーストが起動したときと終了するときの会話を書く。ghost/master/dic/boot.pasta を作る。
# boot.pasta - 起動/終了イベント用シーン定義
# pasta DSL では「シーン関数フォールバック」機能を利用
# シーン名とSHIORIイベント名を一致させることで、自動ディスパッチされる
# ※アクター辞書は actors.pasta で共通定義
# グローバル単語定義(ランダム選択用)
@終了挨拶:またね~!、お疲れ様!、ばいばーい!
# OnBoot イベント - 決定的動作(テスト安定性のため単一シーン)
*OnBoot
女の子:@通常 起動したよ~。
男の子:@通常 さあ、始めようか。
# OnFirstBoot イベント - 初回起動時
*OnFirstBoot
女の子:@笑顔 初めまして!\nわたしは女の子、よろしくね。
男の子:@笑顔 ぼくは男の子。ちゃんと使ってよね。
# OnClose イベント - 終了時
*OnClose
女の子:@通常 @終了挨拶
男の子:@通常 また呼んでよね。
>ゴースト終了(300)
# OnClose イベント - 別パターン
*OnClose
女の子:@眠い おやすみなさい...
男の子:@通常 じゃあね。
>ゴースト終了(300)
ここでの要点は次のとおりである。
*OnBootの*(全角アスタリスク)はシーンを定義するマーカーである。シーンは「会話のひとまとまり」を表す。- シーン名を
OnBoot、OnCloseのようにイベント名と同じ名前にしておくと、ベースウェアからそのイベントが届いたとき、対応するシーンが自動的に呼び出される。OnBootは起動時、OnCloseは終了時のイベントである。 - インデントした
女の子:@通常 起動したよ~。はアクション行で、「誰が・どんな表情で・何を話すか」を表す。@通常でステップ 2 で定義した表情を指定し、続けてセリフを書く。 @終了挨拶:またね~!、お疲れ様!、ばいばーい!は単語定義である。読点(、)で区切った候補の中から 1 つがランダムに選ばれる。アクション行で@終了挨拶と書くと、その場でどれか 1 つに置き換わる。>ゴースト終了(300)は、ジャンプ表記で終了処理を呼び出している。- 同じ名前のシーン(ここでは
OnClose)を複数定義すると、実行時にそのうち 1 つがランダムに選ばれる。これで毎回同じ反応にならない自然さが生まれる。 - セリフ中の
\nは改行を表す。
用語: 「シーン」は会話のひとまとまり。「イベント」はベースウェアからゴーストへ届く通知(起動した、終了する、クリックされた等)。シーン名をイベント名に合わせると、そのイベントで自動的に呼ばれる。
ステップ 4: ランダムトークと時報を書く(talk.pasta)
ゴーストは、何もしないときも時々ひとりごとを言う。これをランダムトークと呼ぶ。ghost/master/dic/talk.pasta を作る。
# talk.pasta - ランダムトーク/時報用シーン定義
# OnSecondChange (毎秒) → 仮想イベントディスパッチャ → ランダムトーク/時報
# ※アクター辞書は actors.pasta で共通定義
# ランダムトーク用単語(ランダム選択)
@雑談:何か用?、暇だなあ...、ねえねえ、聞いてる?、うーん、眠くなってきた...
# ランダムトーク - 仮想イベント OnTalk
*OnTalk
%女の子、男の子
女の子:@通常 @雑談
*OnTalk
%女の子、男の子
女の子:@笑顔 Pasta DSL、使ってみてね!
男の子:@笑顔 Lua 側も触ってみなよ。
*OnTalk
%女の子、男の子
女の子:@眠い 今日は何しようかな...
男の子:@通常 宿題やったの?
*OnTalk
%女の子、男の子
女の子:@通常 ねえ、今日の天気どう思う?
男の子:@困惑 さあ、外見てないからわかんないや。
*OnTalk
%女の子、男の子
女の子:@笑顔 一緒にお話しよう!
男の子:@照れ しょうがないなあ。
*OnTalk
%女の子、男の子
女の子:@眠い ふわあ...ちょっと眠いかも。
男の子:@通常 寝てていいよ、ぼくが見てるから。
# プロパティスコープ統合テスト($% 構文)
*OnTalk
%女の子、男の子
$ゴースト名=$%currentghost.name
女の子:@笑顔 わたしの名前は$ゴースト名だよ!
男の子:@通常 名前、ちゃんと取れてる?
# 時報 - 仮想イベント OnHour
# $時12 変数は onhour-date-var-transfer により自動設定される(12時間表記)
# 4段階フォールバック: 時報12 → OnHour12 → 時報その他 → OnHourOther
# 時刻別時報の例(正午専用)
*時報12
%女の子、男の子
女の子:@笑顔 お昼だよ!お腹すいた~。
男の子:@笑顔 もうお昼か、ご飯にしよう。
# 汎用時報(他の時刻別シーンがない場合のフォールバック)
*時報その他
%女の子、男の子
女の子:@笑顔 $時12 だよ!時報だよ~。
男の子:@笑顔 もう $時12 か、早いね。
*時報その他
%女の子、男の子
女の子:@通常 今 $時12 だって。
男の子:@通常 へえ、そうなんだ。
*時報その他
%女の子、男の子
女の子:@通常 $時12 ...時間が経つのって不思議だね。
男の子:@通常 哲学的だね。
新しく出てきた要素を説明する。
- 各シーンの先頭にある
%女の子、男の子は、そのシーンに登場するアクターを宣言している。読点で複数を並べられる。 *OnTalkを複数並べているのは、ランダムトークの候補をたくさん用意するためである。実行時にどれか 1 つが選ばれる。@雑談のように単語定義を使うと、さらに細かいバリエーションを足せる。$ゴースト名=$%currentghost.nameは変数への代入である。$から始まる名前が変数で、ここではゴースト名を取得して$ゴースト名に入れ、セリフ中で$ゴースト名と書いて差し込んでいる。*時報12*時報その他は時報(毎正時のあいさつ)用のシーンである。$時12には現在時刻(12 時間表記)が自動的に入る。特定の時刻専用のシーンがなければ「その他」がフォールバックとして使われる。
変数やシーンの呼び出しについてさらに詳しく学びたくなったら、変数・スコープ や Call / Jump を参照するとよい。
ステップ 5: クリックへの反応を書く(click.pasta)
ゴーストをダブルクリックしたときの反応を書く。ghost/master/dic/click.pasta を作る。
# click.pasta - ダブルクリック反応用シーン定義
# OnMouseDoubleClick イベントに反応
# ※アクター辞書は actors.pasta で共通定義
# ダブルクリック反応(ランダム選択)7種以上
*OnMouseDoubleClick
%女の子、男の子
女の子:@驚き わっ、びっくりした!
男の子:@笑顔 どうしたの?
*OnMouseDoubleClick
%女の子、男の子
女の子:@笑顔 なあに?呼んだ?
男の子:@通常 こっちに用があるんじゃない?
*OnMouseDoubleClick
%女の子、男の子
女の子:@照れ え、なに?
男の子:@キラキラ 照れてるの?
*OnMouseDoubleClick
%女の子、男の子
男の子:@驚き うわっ!なに!?
女の子:@笑顔 反応してくれたね。
*OnMouseDoubleClick
%女の子、男の子
女の子:@怒り もう、そんなにクリックしないで!
男の子:@驚き お、怒った怒った。
*OnMouseDoubleClick
%女の子、男の子
女の子:@笑顔 わ〜い、遊んでくれるの?
男の子:@通常 まあ、暇だしね。
*OnMouseDoubleClick
%女の子、男の子
男の子:@キラキラ ふふん、ぼくのことが気になる?
女の子:@驚き えっ?そんなんじゃないよ!
OnMouseDoubleClick はゴーストがダブルクリックされたときのイベントである。ステップ 3・4 と同じく、同名シーンを複数並べてランダムに選ばせている。書き方の文法はこれまでと変わらない。
ステップ 6: 選択肢を出す(choice.pasta)
最後に、会話の中で選択肢を出して分岐させる例を書く。ghost/master/dic/choice.pasta を作る。
# choice.pasta - 選択肢デモ辞書
# @?マーカーによる選択肢定義と自動ルーティングのデモ
# ※アクター辞書は actors.pasta で共通定義
# 選択肢デモ - ダブルクリックから呼び出し可能
*OnMouseDoubleClick
%女の子、男の子
女の子:@笑顔 何について話す?
@?挨拶
@?天気「天気を聞く」
!select(10)
# コールバックシーン - 省略形「挨拶」の選択時
*挨拶
%女の子、男の子
女の子:@笑顔 こんにちは!挨拶を選んでくれたんだね。
男の子:@通常 よろしく!
# コールバックシーン - 括弧形「天気」の選択時
*天気
%女の子、男の子
女の子:@通常 今日はいい天気だね!
男の子:@笑顔 散歩にでも行こうか。
新しい要素を説明する。
@?挨拶は選択肢を表すマーカーである。@?に続けてシーン名を書くと、その名前のボタンが表示され、押すと同名のシーン(ここでは*挨拶)へ飛ぶ。@?天気「天気を聞く」のように「」で表示名を指定すると、ボタンには「天気を聞く」と表示され、押すと*天気のシーンが呼ばれる。!select(10)は選択待ちの設定(タイムアウト等)を表す。
これで「ダブルクリックすると選択肢が出て、選んだ内容に応じて会話が分岐する」という振る舞いが完成する。
ステップ 7: 設定ファイルを置く
辞書がそろったので、ゴーストとしての設定ファイルを用意する。
install.txt
フォルダ直下に install.txt を置く。これはインストール時の情報である。
type,ghost
name,hello-pasta
directory,hello-pasta
accept,
ghost/master/descript.txt
ゴーストの定義ファイルである。shiori,pasta.dll の行で、頭脳として pasta を使うことを宣言している。
charset,UTF-8
type,ghost
name,hello-pasta
sakura.name,女の子
kero.name,男の子
craftman,ekicyou
craftmanw,どっとステーション駅長
shiori,pasta.dll
homeurl,https://github.com/ekicyou/pasta
ghost/master/pasta.toml
pasta の動作設定ファイルである。辞書ファイルの読み込みパターンや、ランダムトークの間隔、アクターの配置位置などを指定する。
起動に必須なのは [actor] セクションだけである。他の全セクション([loader] / [ghost] / [talk] / [logging] など)は省略でき、省略すると pasta が無難なデフォルト値を自動補完する。[package] は SHIORI 用途では不要で、書いても無視される。したがって、最小構成は次のように [actor] だけで完結する。
# 最小構成: 必須の [actor] のみ。他は SHIORI デフォルトで補完される。
[actor."女の子"]
spot = 0
[actor."男の子"]
spot = 1
"女の子"/"男の子"は、descript.txtのsakura.name/kero.nameと一致させる。spotはバルーン位置(0=sakura 側 /1=kero 側)で、ゴースト固有のためデフォルト化できず、各アクターで必ず指定する。- 辞書は
[loader]を書かなくても、pasta_patternsのデフォルト["dic/**/*.pasta"]によりdic/配下の.pastaファイル(ステップ 2〜6 で書いた 5 つ)がすべて自動的に読み込まれる。
この最小構成をそのまま ghost/master/pasta.toml として保存すれば、hello-pasta は起動する。各セクションの分類(省略可 / 必須 / エンジンプロファイル専用)と全フィールドのデフォルト値、フルリファレンステンプレートは設定ファイルリファレンスにまとまっている。ランダムトーク間隔([ghost])やログ設定([logging])などを明示したくなったら、そちらを参照して必要な項目だけ書き足すとよい。配布版 hello-pasta の完全な設定例は hello-pasta の pasta.toml を参照すること。
pasta.dll と Lua ランタイム
ghost/master/ に pasta.dll を置く。これが SHIORI 本体である。前提環境で触れたとおり、これは自分で書くものではなく、配布物に含まれる実行ファイルを配置する。Lua ランタイム(scripts/ 配下)も同様に配置する。
これらの実行ファイルの入手・ビルド・配置手順は pasta_sample_ghost の README にまとまっている。
ステップ 8: ゴーストを起動する
これでフォルダ構成の冒頭で示した一式がそろった。最後に SSP に読み込ませて起動を確認する。
hello-pastaフォルダ全体を、SSP のゴーストフォルダ(SSP インストール先のghost/配下)にコピーする。- SSP を起動し、ゴースト切り替えメニューから
hello-pastaを選ぶ。 - 起動すると、
OnBoot(または初回はOnFirstBoot)のセリフが表示される。 - ゴーストをダブルクリックすると、
OnMouseDoubleClickの反応や選択肢が出る。 - しばらく待つと、ランダムトーク(
OnTalk)が始まる。
配布物として
.nar形式にまとめてインストールする方法もある。配布パッケージの作り方は pasta_sample_ghost の README を参照すること。
起動して女の子と男の子があいさつを返したら成功である。ここまでで作ったのは、pasta 公式サンプル hello-pasta と同じ、起動可能な最小限のゴースト一式である。
うまく起動しないときは
- 文字化けする / 辞書が読めない: ファイルが UTF-8 で保存されているか確認する。設定ファイル先頭の
charset,UTF-8も確認する。 - 何も表示されない:
ghost/master/にpasta.dllが置かれているか、descript.txtにshiori,pasta.dllがあるか確認する。 - 辞書が反映されない:
dic/フォルダの中に.pastaファイルがあるか確認する。pasta_patternsを省略していればデフォルトの["dic/**/*.pasta"]で読み込まれる。明示している場合はそのパターンがdic/配下を網羅しているか確認する。
おめでとうございますわ! あなたのデスクトップに、ちゃんと住人が生まれましたわね。……フンッ、これくらいできて当然ですわよ。けれど、よくぞ最後までついてらして。
ここから先は、あなたの番。会話をもっと豊かにしたくなったら Pasta DSL 文法 で書き方を体系的に学び、込み入った処理を書きたくなったら Lua API / コーディング へ。さあ、あなただけのゴーストを、熱く育ててまいりましょう!
文法リファレンス概要
ごきげんよう。わたくし Claudia が、Pasta DSL の文法を隅から隅までご案内いたしますわ。 このセクションは読み物ではなく、辞書のように引いて使う参照型リファレンスですの。 「あの構文、どう書くんだったかしら」と迷ったとき、該当の章をめくればよろしくてよ。 ……フンッ、別にあなたが忘れっぽいなんて言っていませんわよ。
このセクションは、Pasta DSL の実装済み文法を網羅する参照型ドキュメントである。手を動かして覚える入門は「入門ガイド」に譲り、ここでは各文法要素を要素ごとに整理し、試せる具体例と権威的仕様への導線を示す。
Pasta DSL の文法モデル
Pasta DSL は、里々/さとりにインスパイアされた対話スクリプト記述言語であり、次の基本原則に立つ。
- 行指向文法: 各行は改行で終わり、行頭の数文字(マーカー)によって行の種類が確定する。唯一の例外は複数行にわたる Lua コードブロックである。
- 全角・半角の両対応: マーカー・演算子・括弧は全角と半角のいずれでも記述でき、両者は同等に扱われる(例:
*と*、(と()。 - インデントは有無のみで判定: 行頭に空白があるか否かでグローバルレベル/下層レベルを区別する。インデントの深さは判定に使われない。
- 式(Expression)のサポート: 変数代入や関数引数で算術式を記述できる。複雑な処理は Lua ブロックに委ねられる。
- 宣言的言語:
if/whileのような命令型制御構文は持たない。制御フローはシーン定義と Call で表現する。
ファイル構造の俯瞰
ファイル
├─ グローバル単語定義 (@)
├─ アクター辞書 (%)
├─ グローバルシーン (*)
│ ├─ 属性行 (&)
│ ├─ ローカル単語定義 (@)
│ ├─ アクション行 (アクター:内容)
│ ├─ 変数代入 ($)
│ ├─ Call 行 (>)
│ ├─ 選択肢行 (@?)
│ ├─ キューコマンド行 (!)
│ ├─ ローカルシーン (・)
│ └─ Lua コードブロック
└─ コメント行 (#)
式の基本例
$count=10 + 5 # 算術式
$result=$a * $b # 変数を含む式
$nested=($a + $b)* 2 # 括弧による優先順位制御
$=@副作用関数() # 式文: 結果を代入せず式を評価のみ
対応する算術演算子は次のとおり(全角・半角は同等)。
| 種別 | 演算子(全角/半角) |
|---|---|
| 加算 | + / + |
| 減算 | - / - |
| 乗算 | * / * / × |
| 除算 | / / / / ÷ |
| 剰余 | % / % |
このセクションの読み方
各章は「軽い導入 → 普通文体の本体(試せる例つき)→ ひとことの締め」のリズムで構成され、章末には対応する doc/spec/ の権威的仕様へのリンクを置いている。仕様の厳密な定義が必要になったら、章末リンクをたどること。
| 章 | 扱う内容 | 権威的仕様 |
|---|---|---|
| キーワード・マーカー | 全マーカーと演算子・区切り文字の一覧 | doc/spec/02-markers.md |
| 行とブロック構造 | 行種別・グローバル/ローカルブロック・インデント | doc/spec/03-block-structure.md |
| Call / Jump | シーン呼び出し・前方一致・スコープ解決 | doc/spec/04-call-spec.md |
| リテラル型 | 型変換ルール・文字列・数値・真偽値 | doc/spec/05-literals.md |
| アクション行 | 発言行・インライン要素・行継続・改行 | doc/spec/06-action-line.md |
| さくらスクリプト | \ で始まるコマンドの字句構造と透過処理 | doc/spec/07-sakura-script.md |
| 変数・スコープ | ローカル/グローバル/プロパティ変数 | doc/spec/09-variables.md |
| 単語定義 | 単語の定義・参照・前方一致・複数キー | doc/spec/10-words.md |
| アクター辞書 | % によるアクター単位の単語辞書 | doc/spec/11-actor-dictionary.md |
網羅範囲についての注記
このリファレンスは実装済みの文法要素のみを扱う。次の要素は意図的に除外、または注記つきで扱う。
- 属性(
&、doc/spec ch08): 構文はパーサーで受理されるが、トランスパイラ・ランタイムでの処理は将来予定である。本セクションでは「行とブロック構造」の中で構文と配置ルールのみ触れ、「将来変更あり」の注記を添える。 - 将来仕様(doc/spec ch12): 未確定事項は本セクションの対象外とする。
さあ、準備はよろしくて? お目当ての章へお進みなさいまし。 迷ったらこの概要に戻ってくればよろしくてよ。熱く参りましょう!
権威的仕様: 文法モデル全体の厳密な定義は doc/spec/01-grammar-model.md を参照。各文法要素の権威は、章別に分割された doc/spec/ 各章が担う。
キーワード・マーカー
まずは Pasta DSL の「文字の意味」を覚えていただきますわ。 行頭に置く一文字――マーカー――が、その行の運命を決めるのですの。 この章は早見表として手元に置いておくとよろしくてよ。
Pasta DSL は行指向文法であり、行頭の文字(マーカー)によって行の種類が確定する。ここではマーカー・演算子・区切り文字・基本要素を一覧する。すべてのマーカー・演算子・括弧は全角と半角の両方を許容し、両者は同等に扱われる。
マーカー一覧
| マーカー | 全角 | 半角 | 用途 |
|---|---|---|---|
| グローバルシーン | * | * | シーン定義 |
| ローカルシーン | ・ | - | サブシーン定義 |
| アクター辞書 | % | % | アクター辞書の定義・アクタースコープ指定 |
| 属性 | & | & | メタデータ(処理は将来予定) |
| 単語/関数 | @ | @ | 単語定義・参照・関数呼び出し |
| 選択肢 | @? | @? | 選択肢定義(ジャンプ先+表示テキスト) |
| 変数 | $ | $ | 変数宣言・参照 |
| Call | > | > | シーン呼び出し |
| キューコマンド | ! | ! | 演出キュー(dola 側で処理) |
| コメント | # | # | コメント行 |
| コロン | : | : | キー:値の区切り |
基本要素
改行(NEWLINE)
改行は \r\n / \n / \r のいずれか。行指向文法では改行により行属性が確定し、すべての行要素は改行で終わる。
空白(WHITE_SPACE)
Pasta は Unicode の White_Space カテゴリの文字から改行コード(\r \n)を除いたものを空白として扱う。半角スペース・タブ・全角スペースなどが含まれる。空白は有効なトークン区切り文字であり、自動スキップされるわけではない。改行は意図的に空白から除外され、行の区切り文字として機能する。
コロン(: / :)
コロンはキー:値の関係を表す汎用区切り文字である。比較・フィルター条件には使わない(それらには = > < などの比較演算子を用いる)。
$var_name:value # 変数代入
@word_name:value1、value2 # 単語定義
アクター:こんにちは # アクション行
識別子(Identifier)
識別子は Unicode の XID_START で始まり XID_CONTINUE が続く。ASCII の a-z A-Z _、および平仮名・カタカナ・漢字なども使用できる。
- 予約パターン
__*__(例:__start__)はシステム予約であり使用できない。 - 最長一致で切り出される。空白による区切りがない場合、識別子に含まれない文字が現れるまでを一括して取り込む。
@挨拶、みんな! # 識別子は「挨拶」、以降「、みんな!」は通常テキスト
インデント(Indent)
行頭の 1 つ以上の連続した空白。有無のみが判定基準であり、深さは判定しない。
- インデントなし = グローバルレベル(グローバルシーン、グローバル単語定義など)
- インデントあり = 下層レベル(グローバルシーン直下のすべての行)
*会話 ← インデントなし(グローバルレベル)
&author:Alice ← インデントあり(下層レベル)
Alice:おはよう ← インデントあり(下層レベル)
演算子
算術演算子
| 演算 | 全角 | 半角 |
|---|---|---|
| 加算 | + | + |
| 減算 | - | - |
| 乗算 | * × | * |
| 除算 | / ÷ | / |
| 剰余 | % | % |
* は乗算とシーンマーカーで同じ文字を使う点に注意する(文脈で区別される)。
比較演算子
| 演算 | 全角 | 半角 |
|---|---|---|
| 等値 | == | == |
| 不等 | != | != |
| より小さい | < | < |
| より大きい | > | > |
| 以下 | <= | <= |
| 以上 | >= | >= |
> は比較と Call マーカーで同じ文字を使う点に注意する。
括弧
| 種類 | 全角 | 半角 |
|---|---|---|
| 左括弧 | ( | ( |
| 右括弧 | ) | ) |
単語値の区切り文字
単語定義の値リストは、全角読点(、)・全角コンマ(,)・半角カンマ(,)のいずれかで区切る。前後の空白は無視される。
@fruits:apple、banana、orange
@numbers:1、2、3
マーカー詳細への導線
各マーカーの詳細なセマンティクスは、それぞれ専用の章で扱う。
- 単語・関数
@の用法 → 単語定義 - 変数
$のスコープ修飾 → 変数・スコープ - Call
>のターゲット形式 → Call / Jump - アクター辞書
%→ アクター辞書 - さくらスクリプトのエスケープ
\→ さくらスクリプト
ね、一覧があれば怖くありませんでしょう? フンッ、覚えるのはあなた次第ですけれど。 次は、これらのマーカーが組み合わさる「行とブロック構造」へ参りましょう。
権威的仕様: マーカー・キーワードの厳密な定義は doc/spec/02-markers.md を参照。
行とブロック構造
Pasta の世界は、行が積み重なって「ブロック」を作りますの。 シーンという大きな器の中に、台詞や呼び出しがきちんと収まる――その仕組みを見ていきましょう。 構造を掴めば、辞書ファイル全体が手に取るように分かりますわよ。
Pasta は行指向文法であり、各行は改行で終わる。行頭のマーカーとインデントの有無により行の種類が決まり、それらが階層的なブロック構造を形成する。
行の種類
行はインデントの有無で大きく二分される。
インデント不要の行(行頭にインデントなし=グローバルレベル)
| 行種 | マーカー | 説明 |
|---|---|---|
| グローバルシーン | * / * | グローバルブロックを開始 |
| グローバル単語定義 | @ / @ | ファイル全体で参照可能な単語定義 |
| アクター辞書 | % / % | アクター単位の単語辞書を定義 |
| Lua ブロック | ``` / ```lua | 直前のグローバルシーンに属する複数行コード |
| コメント | # / # | 処理されない |
インデントが必要な行(行頭にインデントあり=下層レベル)
| 行種 | マーカー | 説明 |
|---|---|---|
| ローカルシーン | ・ / - | グローバルシーン配下のローカルブロック開始 |
| 属性定義 | & / & | シーンにメタデータを付与(処理は将来予定) |
| アクション行 | (なし) | キャラクターの発言 |
| Call | > / > | シーンを呼び出す |
| 選択肢行 | @? / @? | プレイヤー選択肢の定義 |
| 変数代入 | $ / $ | 変数を宣言・代入 |
| ローカル単語定義 | @ / @ | 親シーン内で参照可能な単語定義 |
Note: コメント行はインデントの有無にかかわらず、どの位置にも置ける(常に「コメント」として解釈される)。
ブロック構造
グローバルシーン
グローバルシーンは行頭が *(または *)で始まる。シーン本体はインデントした行で記述する。
*挨拶
ぱすた:こんにちは!
ラザニア:ごきげんよう!
同名のグローバルシーンを複数定義でき、呼び出し時に前方一致で候補が列挙され、そこからランダムに 1 つが選択される(詳細は Call / Jump)。
*挨拶
ぱすた:おはよう!
*挨拶
ぱすた:こんばんは!
グローバルシーンの内部構造
グローバルシーンブロックは、宣言行に続いて次の要素で構成される。
- グローバルシーン行:
*グローバル名 - 属性行: 0 個以上の
&key:value(シーン全体のメタデータ/処理は将来予定) - 暗黙ローカル開始ブロック(
__start__): ローカルシーン宣言なしで、Lua ブロック・アクション行・Call 行・変数代入行を格納できる暗黙のブロック - ローカルブロック: 0 個以上の明示的ローカルシーン(
・ローカル名)
グローバルシーンが Call の対象になると、まず暗黙の __start__ ブロックが実行される。
ローカルシーン
ローカルシーンは親シーン内でのみアクセス可能で、インデント+・(または -)で始まる。
*メインメニュー
ぱすた:何をしますか?
>選択肢1
・選択肢1
ぱすた:選択肢1が選ばれました
・選択肢2
ぱすた:選択肢2が選ばれました
ローカルシーンブロックは、宣言行・属性行(0 個以上)・コンテンツ行(変数代入/アクション行/Call 行/選択肢行)で構成される。
Lua ブロックの配置
Lua ブロックは暗黙ローカル開始ブロック(__start__)内に置かれる。インデント不要だが、構造上は直前のグローバルシーンに属する。
*会話
```lua
function SCENE.initialize(act)
local save, var = act:init_scene(SCENE)
save.talked = true
end
```
こんにちは
Lua ブロックには次の制約がある。
- 関数定義のみ許可:
function ...で始まる関数定義のみ記述できる。local x = 10のような変数宣言・トップレベルのステートメントは許可されない。 - パーサーは内部を解釈せず透過する。関数定義のみで成立するかはトランスパイラ層が検証し、誤りはトランスパイラ層以降でコンパイルエラーとなる。
詳細は Lua セクションを参照。
属性(将来変更あり)
属性は &属性名:値 の形式で、シーン定義の直後にのみ記述できる。
*会話
&author:Alice
&genre:comedy
ぱすた:こんにちは!
配置ルール:
- グローバルシーンの直後 → グローバルシーンに付与
- ローカルシーンの直後 → ローカルシーンに付与
- アクション行や変数代入行の後には配置できない
将来変更あり: 属性の構文は現在パーサーで受理されるが、トランスパイラ・ランタイムでの処理は将来予定である。現状では属性を指定しても処理には反映されない。仕様は将来変更され得る(権威は doc/spec/08-attributes.md)。
コメント
# または # から行末までがコメントとして扱われ、構造・セマンティクスに影響しない。どの位置にも置ける。
*テスト
# これはコメントです
ぱすた:これは実行されます
# 全角シャープでもOK
インデントの判定
インデントは「有るか・無いか」のバイナリ判定のみで扱われ、深さは判定されない。インデントレベルが一貫している必要もない。
*会話 ← インデントなし(グローバルレベル)
&author:Alice ← インデントあり(下層レベル)
・選択肢1 ← インデントあり(下層レベル)
Alice:こんにちは ← インデントあり(下層レベル)
構造が見えてくると、辞書ファイルがぐっと書きやすくなりますでしょう? 次は、シーンとシーンをつなぐ「Call / Jump」を覚えていきましょう。熱く参りますわよ!
権威的仕様: 行・ブロック構造の厳密な定義は doc/spec/03-block-structure.md を参照。
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 も参照。
リテラル型
値には「型」がございますの。数なのか、文字なのか、真偽なのか。 Pasta は書かれたリテラルを、決まった優先順位で自動的に見分けますわ。 このルールを知っておけば、「なぜか文字列扱いされる」といった戸惑いとは無縁ですわよ。
リテラル値は、変数代入・関数引数・属性値などで使用される。Pasta は書かれた値を次の優先順位で型変換する。
型変換ルール
| 優先度 | 値 | 型 |
|---|---|---|
| 1 | true / false | bool |
| 2 | 「...」 または "..."(引用符あり) | String(引用符あり) |
| 3 | 小数点を含む数値 | f64 |
| 4 | 小数点なしの数値 | i64 |
| 5 | その他 | String |
上から順に判定され、最初に一致した型が採用される。
true → bool
false → bool
「こんにちは」 → String(空白も保持)
Hello → String
3.14 → f64
42 → i64
真偽値(bool)
true / false はそのまま真偽値として解釈される。
$is_active:true
$done:false
文字列(String)
文字列は引用符の有無で扱いが変わる。
- 引用符あり: 全角の
「...」または半角の"..."で囲む。空白も文字列の一部として保持される。 - 引用符なし: 空白は区切り文字として認識され、文字列に含まれない。
# 日本語文字列
$greeting:「こんにちは」
# 英語文字列
$message:"Hello, World!"
空白を含む文字列は必ず引用符で囲む。引用符の有無で値の分割が変わる点に注意する。
hello world → String "hello" と String "world"(2 つの値)
「hello world」 → String "hello world"(1 つの値)
文字列エスケープ
半角ダブルクォート文字列内では、バックスラッシュ \ でエスケープできる。
| エスケープ | 意味 |
|---|---|
\n | 改行 |
\\ | バックスラッシュ |
\" | ダブルクォート |
数値(i64 / f64)
数値は符号(- / -)と数字、任意の小数部からなる。小数点を含めば f64、含まなければ i64 となる。数字は ASCII 数字(0-9)と全角数字(0-9)の両方が使える。
$count:10 # i64
$offset:-5 # i64
$pi:3.14 # f64
$delta:-2.5 # f64
単語値での引用符エスケープ
単語定義の値にリテラルの引用符を含めたい場合は、二重の括弧を使う(詳細は 単語定義)。
@会話:「「セリフ」」 # 実行時に「セリフ」として展開される
型の見分け方が分かれば、値の扱いで悩むことはございませんわ。 さあ、いよいよキャラクターが喋り出す「アクション行」へ参りましょう!
権威的仕様: リテラル型の厳密な定義は doc/spec/05-literals.md を参照。文字列・数値リテラルの字句構造は doc/spec/02-markers.md も参照。
アクション行
さあ、いよいよキャラクターたちが口を開きますわ。 「誰が、何を話すか」――それを書くのがアクション行ですの。 ここがゴーストの命、いちばん楽しいところですわよ。張り切って参りましょう!
アクション行は、キャラクター(アクター)の発言や演出を記述する行である。基本形は「アクター名 : アクション内容」であり、内容には通常テキストとインライン要素を自由に組み合わせられる。
基本構文
アクター名:アクション内容
コロンの前がアクター名、後ろがアクション内容となる。
*会話
ぱすた:今日はいい天気だね
ラザニア:そうですわね、散歩に参りましょうか
アクター名は会話だけでなく、さくらスクリプトによる表情指定などアクション全般を制御する。
アクターの省略
一度アクターを指定すると、続く行ではアクター名を省略し、コロンだけで前の発話を継続できる。
*長い会話
ぱすた:今日はね
:とてもいい天気なんだ
:だから散歩に行こうと思ってるんだ
ラザニア:よろしくてよ!
:ご一緒いたしますわ!
インライン要素
アクション内容には次の要素を埋め込める。
| 要素 | 構文 | 説明 |
|---|---|---|
| 通常テキスト | こんにちは | 任意の文字列 |
| 単語参照 | @word_name | 登録済み単語からランダム選択 |
| 単語参照(動的) | @$var_name | 変数値を単語名として参照 |
| @エスケープ | @@ | リテラルの「@」を 1 文字出力 |
| 変数参照 | $var_name | 変数値を展開 |
| 関数呼び出し | @func_name() | Lua 関数を呼び出し |
| 関数呼び出し(引数) | @func(x:10) | 名前付き引数で呼び出し |
| さくらスクリプト | \n \w8 \s[0] | 表情・タイミング制御 |
*いろいろ
ぱすた:こんにちは、$player_name さん!\w8
ぱすた:今日の天気は@weather_words ですね\n
ぱすた:@greet(time:morning)
重要: アクション内では変数の参照(
$var)のみ可能。変数の宣言・代入($var:value)は専用の変数代入行で行う(変数・スコープ)。
インライン要素の区切り
行は左から右へ走査され、マーカー文字(\ @ $)で分岐して各要素が最長一致で切り出される。識別子の終端は、空白による区切り、または識別子に含まれない文字の出現で決まる。
空白による区切り
空白はトークン区切りとして認識され、空白の数に関係なく 1 つの区切りとして扱われる。区切りの空白は出力に含まれない。
@挨拶 みんな! → 単語参照「挨拶」+ 通常テキスト「みんな!」
@挨拶 みんな! → 同じ出力(空白数は無関係)
$name さん → 変数参照「name」+ 通常テキスト「さん」
空白がない場合(最長一致)
空白で区切られない場合、識別子に含まれない文字が現れるまでを最長一致で取り込む。
@挨拶、みんな! → 単語参照「@挨拶」+ 通常テキスト「、みんな!」(読点は識別子外)
$name! → 変数参照「$name」+ 通常テキスト「!」
@weather_wordsですね → 識別子「@weather_wordsですね」として吸収(意図しない例)
意図せず後続が吸収されるのを防ぐには、空白で区切る(@weather_words ですね)か、読点など識別子に含まれない記号で区切る(@weather_words、ですね)。
行継続
インデントした行を続けると、前行の発話に連結される。
*長文
ぱすた:長い台詞は
複数行に分けて
記述できます
継続行は行マーカー($ @ > & * ・)で始めてはならない。これらで始まると別の行種として解釈される。
改行
アクション行内の改行には 2 通りがある。
- さくらスクリプトの
\n: 常に「改行」として解釈され、出力に改行文字が挿入される。 - 継続行内の空行(糖衣構文): 行継続の領域で、インデントのみで内容のない行(空白だけ、または空行)は 1 改行として解釈される。連続する空行はその数だけ連続改行になる。
*改行例
ぱすた:1行目。
1行目の続き。
2行目。
上の例の出力は「1行目。1行目の続き。\n2行目。」となる(空行が 1 改行として解釈される)。継続行以外の空行(インデントなしの空行など)はレイアウト用として無視され、改行にはならない。
細かいタイミング制御には、さくらスクリプトの \w 等を使うのが推奨される(さくらスクリプト)。
これでキャラクターが生き生きと喋り出しますわ。 変数や単語を混ぜれば、表現は無限に広がりますのよ。次は表情を操る「さくらスクリプト」へ。
権威的仕様: アクション行の厳密な定義は doc/spec/06-action-line.md を参照。インライン要素の各論は 変数・スコープ・単語定義・さくらスクリプト も参照。
さくらスクリプト
表情を変え、間(ま)を作り、演出を添える――それがさくらスクリプトですわ。 Pasta はこの記法を「解釈せず、そのまま通す」のが流儀。だからこそ自由が利きますの。 おほほ、賢いやり方でしょう?
さくらスクリプトは、アクション行内にインラインで埋め込む「\」から始まるコマンドトークンである。Pasta はコマンドの意味を解釈しない。必要なのは字句構造(記述ルール)のみで、検出したコマンドはそのまま透過し、解釈は areka 層が担う。
エスケープ文字
エスケープ文字は厳密に半角バックスラッシュ \ のみ。全角は使えない。
コマンドの字句構造
sakura_command ::= "\" ~ sakura_token ~ bracket_content?
sakura_token ::= [!\-+*?&_a-zA-Z0-9]+
bracket_content ::= "[" ~ bracket_chars ~ "]"
bracket_chars ::= ( "\]" | [^\]] )*
sakura_tokenは ASCII 英数字 +_!-+*?&の連続。先頭が数字でもよい。- 角括弧内容は非ネスト前提で、最初の「非エスケープな
]」で閉じる。\]は内容文字としての]を表す。 - 角括弧は半角の
[]のみが対象(全角括弧はコマンド括弧として扱わない)。 - 未知のトークンも受理される(Pasta は中身を構文解析しない)。
使用例
*表情変更
ぱすた:\s[0]こんにちは!
ぱすた:\s[4]元気だよ
ぱすた:\s[1]\w8それで\w8ね...
*改行とウェイト
ぱすた:最初の行\n次の行\n\w9最後の行
*ブラケットエスケープ
ぱすた:配列参照\s[a\]b]
*コマンドのみ
Bob:\![happy]了解しました。
角括弧内のエスケープと引用
角括弧内で値に特殊文字を含めたいときは、ukadoc の引用規則がそのまま透過する。
\]で]を文字として含める。- カンマを含む値はダブルクォートで囲む:
\![raise,OnTest,"100,2"] - ダブルクォートを含める場合は二重にする:
\![call,ghost,"the ""Name"""]
複雑なケースの方針
Pasta はさくらスクリプトの複雑な構文(入れ子、特殊な引数構成、高度なエスケープの相互作用)を検出・解釈しない。複雑さが必要な場合は、Lua 関数でトークン列(さくらスクリプトを含む文字列)を生成し、アクション行へ挿入する運用が推奨される。
*複雑な演出
ぱすた:@emit_complex_sakura() どうぞ
@emit_complex_sakura() が "\![embed,OnTalk]" のような文字列を返し、それがアクションに連結される。
解釈を areka 層に任せる潔さ、お分かりいただけまして? 表情も間も思いのまま。次は会話を動かす「変数・スコープ」へ参りますわよ!
権威的仕様: さくらスクリプトの字句構造の厳密な定義は doc/spec/07-sakura-script.md を参照。アクション行内での位置づけは doc/spec/06-action-line.md も参照。
変数・スコープ
会話に「記憶」を持たせたいときは、変数の出番ですわ。 一時的なもの、永続するもの、ベースウェアと共有するもの――三種を使い分けますの。 スコープを制する者がゴーストを制する、と申しておきましょう。
変数は $(または $)マーカーで宣言・参照する。Pasta には有効範囲(スコープ)の異なる 3 種類の変数がある。代入の値には式や関数呼び出しも使える。
変数の種類とスコープ
| スコープ | 代入 | 参照 | 有効範囲 |
|---|---|---|---|
| ローカル | $var_name=値 | $var_name | 一連のシーンが終わるまで |
| グローバル | $*var_name=値 | $*var_name | 永続的 |
| プロパティ | $%prop.path=値 | $%prop.path | SSP 共有プロパティ(ベースウェア管理) |
代入の区切りはコロン(:)でも代入演算子(=)でも記述できる。
*初期化
$score:100
$ユーザー:太郎
$is_active:true
ローカル変数
一連のシーンが終わるまで有効な変数。$変数名 で参照する。
$ローカル変数=10
Alice:値は $ローカル変数 です
グローバル変数
$* 修飾子を付けると、永続的に有効なグローバル変数になる。Lua 側では save. に対応する。
$*グローバル変数=100
Bob:値は $*グローバル変数 です
プロパティ変数
$% で SSP 共有プロパティを読み書きする。プロパティ ID はドット(.)を含められ、sakura.name や baseware.version などをそのまま記述できる。
$%sakura.name=Alice # プロパティへ書き込み
$name=$%sakura.name # プロパティから読み込み
さくら:$%sakura.name # インライン参照
変数参照の空白ルール
会話行の中で変数を展開するには、$変数名 の直後に1 文字以上の空白を置く。
*表示
$ユーザー:太郎
ぱすた:$ユーザー さん、こんにちは!
重要: 空白がないと最長一致で変数名を解釈し、後続の文字まで変数名に取り込んでしまう。意図しない動作を避けるため、変数参照の直後には必ず空白(または識別子に含まれない記号)を置く。
式(Expression)のサポート
変数代入では式を記述できる。代入可能な値は次のとおり。
| 値の種類 | 構文例 | 説明 |
|---|---|---|
| リテラル値 | $score=100 | 数値・文字列リテラル |
| 単語参照 | $value=@word_name | 登録済み単語の参照 |
| 変数参照 | $new=$old | 他の変数の値をコピー |
| 式 | $result=$a + $b * 2 | 算術式 |
| ローカル関数呼び出し | $r=@calculate() | SCENE.calculate(act) の戻り値 |
| グローバル関数呼び出し | $r=@*calculate() | GLOBAL.calculate(act) の戻り値 |
| 関数呼び出し(引数付き) | $sum=@add(x:10 y:20) | 名前付き引数で呼び出し |
$calc=($x + $y)* 2 # 括弧による優先順位制御
対応する算術演算子は キーワード・マーカー を参照。複雑な演算や条件判定は Lua ブロックで関数化できる。
関数スコープの展開先
| DSL 構文 | Lua 展開先 | 説明 |
|---|---|---|
@func() | SCENE.func(act) | ローカルスコープ(シーン内) |
@*func() | GLOBAL.func(act) | グローバルスコープ(永続領域) |
@* のグローバルスコープは、変数 $* → save. と対称の構造である。
式文(ExprStmt)
代入を伴わず、式の評価(副作用)だけを行うには $=expr と書く。変数名を省略し、結果を捨てる。
$=@副作用関数() # ローカル: SCENE.副作用関数(act)
$=@*グローバル副作用() # グローバル: GLOBAL.グローバル副作用(act)
var_set の全バリアント(ローカル・グローバル・式文)はすべて $ で始まり、変数操作のシグナルとして一貫して認識される。
スコープを使い分ければ、ゴーストは過去を覚え、世界と繋がりますわ。 次は会話に多様性を生む「単語定義」へ。ランダムの魔法をご覧に入れましょう!
権威的仕様: 変数・スコープの厳密な定義は doc/spec/09-variables.md を参照。式の構文は doc/spec/01-grammar-model.md も参照。
単語定義
同じ挨拶を毎回繰り返すゴーストなんて、退屈ですわよね。 単語定義を使えば、候補の中からランダムに選ばれて、会話に彩りが生まれますの。 これぞ里々ゆずりの真骨頂。たっぷり味わってくださいまし。
単語定義は、テキストのバリエーションをランダムに選択するための機能である。定義された候補からランダムに 1 つが選ばれることで、毎回同じにならない自然な会話を作れる。@(または @)マーカーで定義・参照する。
単語の定義
グローバル単語定義
ファイルの先頭(シーン定義の前)にインデントなしで定義すると、ファイル全体から参照できる。値は読点(、)・全角コンマ(,)・半角カンマ(,)で区切る。
@挨拶朝:おはよう、ハロー、Good morning
@挨拶昼:こんにちは
*メイン
ぱすた:@挨拶朝 !
ローカル単語定義
グローバルシーン内にインデントありで定義すると、そのシーン内でのみ参照できる。シーンの先頭、会話行より前に置く。
*メイン
@挨拶朝:Hello、Good morning
ぱすた:@挨拶朝 !
複数キー単語定義
コロンの左側にカンマ区切りで複数のキーを指定すると、同一の値リストを複数のキー名に同時登録できる。辞書の重複記述を減らせる。
@女性、水の妖精:水無灯里、アリス・キャロル
@挨拶、あいさつ、greeting:こんにちは、ハロー
- キー区切りには全角読点(
、)・全角コンマ(,)・半角カンマ(,)が使える。 - グローバル・ローカル・アクタースコープのいずれでも使用できる。
- 単一キー形式(
@key:values)との後方互換性は保たれている。
単語の参照
会話行の中で @単語キー の直後に1 文字以上の空白を置くと、一致する候補からランダムに 1 つが選ばれる。
@挨拶朝:おはよう、ハロー
@挨拶昼:こんちわ
*メイン
ぱすた:@挨拶昼 。
重要: 空白がないと最長一致で単語キーを解釈し、後続まで取り込んでしまう。意図しない動作を避けるため、単語参照の直後には必ず空白(または識別子に含まれない記号)を置く。
前方一致検索
単語参照は前方一致で候補を検索する。関連する単語群を一括で参照できる。
@挨拶:こんにちは、ハロー
@挨拶_朝:おはよう、グッドモーニング
@挨拶_夕:こんばんは、Good evening
*会話
ぱすた:@挨拶 # 全候補からランダム選択
ぱすた:@挨拶_朝 # 「おはよう」「グッドモーニング」から選択
@挨→ 「挨拶」「挨拶_朝」「挨拶_夕」すべてにマッチ@挨拶_朝→ 「挨拶_朝」のみにマッチ
スコープと優先順位
参照時の検索順序は次のとおり。
- ローカル単語辞書: 現在のシーン内で定義された単語
- グローバル単語辞書: ファイル先頭で定義された単語
同名の単語がローカルとグローバルの両方にあれば、マージされて候補プールが統合される。Call と同じスコープ解決アルゴリズムに従う(Call / Jump)。
@挨拶:グローバル挨拶
*メイン
@挨拶:ローカル挨拶
ぱすた:@挨拶 ! # 「グローバル挨拶」または「ローカル挨拶」が選ばれる
動的単語参照(将来変更あり)
@$var_name 形式で、変数値を単語名として間接参照する構文が定義されている。$var_name で得た値を単語名として検索する。
将来変更あり: 動的単語参照は文法定義済みだが、実装は将来予定である。
Call との分離
単語参照(@)は単語辞書のみを検索し、シーン辞書は参照しない。逆に Call(>)はシーン辞書のみを検索する。
@挨拶:こんにちは # 単語定義
*挨拶 # 同名のシーン定義
ぱすた:やあ!
*メイン
ぱすた:@挨拶 # 単語辞書から「こんにちは」を取得
>挨拶 # シーン辞書から「挨拶」シーンを呼び出し
未定義単語の参照
存在しない単語キーを参照すると、エラーにはならず空文字列が返る(ログに警告が出力される)。
*テスト
ぱすた:@存在しない単語 # 空文字列として展開される
エスケープ
@エスケープ
@@ と書くと、リテラルの「@」が 1 文字出力される。多段階参照(@@word など)は非対応で、@@ はあくまでエスケープ専用である。
*メール
ぱすた:メールは user@@example.com です
引用符エスケープ
単語値にリテラルの引用符を含めるには、二重の括弧を使う。
@会話:「「セリフ」」 # 実行時に「セリフ」として展開される
外側の 「」 は単語値の区切り、内側の 「」 がリテラルの引用符として保持される。
ランダムの妙、堪能していただけまして? 単語ひとつで会話が生き物になりますのよ。 最後はキャラごとに表情を束ねる「アクター辞書」へ。仕上げと参りましょう!
権威的仕様: 単語定義の厳密な定義は doc/spec/10-words.md を参照。スコープ解決の共通仕様は doc/spec/04-call-spec.md も参照。
アクター辞書
キャラクターごとに表情の引き出しを用意する――それがアクター辞書ですわ。 「ぱすたの通常顔」「ラザニアの刮目」を名前で呼び出せる、なんとも優雅な仕組み。 文法リファレンスの締めくくり、しっかり仕上げて差し上げますわよ。
アクター辞書(ActorScope)は、キャラクター(アクター)ごとに単語辞書を定義し、会話行で参照可能にする機能である。アクターごとの表情や表現のバリエーションを、アクター名を伴った単語参照として記述できる。%(または %)マーカーで定義する。
グローバルアクター辞書定義
ファイルレベル(インデントなし)で %actor_name を記述し、その直下にインデントした @word_name:values 行を並べると、そのアクターに属する単語定義になる。
#アクター辞書
%ぱすた
@通常 :\s[0]、\s[100]
@照れ :\s[1]
@驚き :\s[2]
@ぐんにょり:\s[3]
@怒り :\s[4]
%ラザニア
@通常 :\s[10]
@刮目 :\s[11]
構文上の注意:
- アクター名には日本語・英語の識別子が使える。
- 単語定義は必ずインデント(全角空白
または半角空白)で開始する。 - 値はカンマ(
、/,/,)で区切り、複数のさくらスクリプト値を指定できる。
シーンスコープ内でのアクター指定
グローバルシーン内で %actor1、actor2 の形式でアクターを指定すると、そのシーン内の会話行でアクター名付き単語参照が有効になる。
*メイン
%ぱすた、ラザニア
ぱすた:@通常 こんにちは
ラザニア:@刮目 目を見開くラザニア
actor_name: で始まる会話行では、そのアクターの単語辞書を優先的に参照する。
アクタースコープと単語参照の統合
actor_name:@word_name の形式で参照する場合、まず %actor_name で定義された単語辞書から word_name を検索する。
- アクター辞書に該当する単語がなければ、グローバル単語辞書・ローカル単語辞書にフォールバックする。
- アクター辞書による単語参照も、グローバル・ローカル単語と同じ前方一致検索ルールに従う。
シャッフル&順次消費
アクター単語は、グローバル・ローカル単語と同一のシャッフル&順次消費アルゴリズムに従う(共通仕様は Call / Jump を参照)。
- 複数値が定義された単語(例:
@通常:\s[0]、\s[100]、\s[200])は、ランダムにシャッフルされた後、呼び出しごとに順次消費される。 - 全候補が消費されると再シャッフルされ、再び順次消費が始まる。
- 単一値の単語(例:
@照れ:\s[1])は常に同じ値を返す。
コードブロック(将来変更あり)
アクタースコープ内に Lua コードブロックを配置する構文は定義されているが、現時点では使用例がなく、将来の拡張として予約されている。
%actor_name
@word_name:value
```lua
function SCENE.on_actor_event(act)
local save, var = act:init_scene(SCENE)
-- アクター固有のイベント処理
end
```
将来変更あり: アクタースコープ内のコードブロックは文法定義のみが存在し、未実装である。アクター固有のイベントハンドラや状態管理関数を置く用途が予定されている。仕様は将来変更され得る。
これで Pasta DSL の文法リファレンスは完結ですわ。よくぞここまでついてきましたわね。 ……フンッ、別に褒めているわけではありませんわよ。でも、もうあなたは立派なゴースト作者ですの。 さあ、辞書を書きに参りましょう。困ったらいつでもこの章に戻ってくればよろしくてよ。熱く!
権威的仕様: アクター辞書の厳密な定義は doc/spec/11-actor-dictionary.md を参照。単語参照の基本は doc/spec/10-words.md も参照。
Lua マニュアル概要
ごきげんよう。ここからは少しだけ歯ごたえのある領域――Lua の世界へご案内いたしますわ。 DSL だけでは手の届かない「数百件の単語を一気に流し込む」「凝った条件分岐でおしゃべりを組み立てる」、 そんな野心的な望みを叶えるのが、この章の役目ですの。フンッ、別に怖がる必要なんてありませんわよ。 わたくしが最短距離でお導きいたしますから、さあ参りましょう。
Pasta では、Pasta DSL(.pasta ファイル)の下層に Lua ランタイムが存在する。DSL のシーンや単語定義は
内部的に Lua コードへ変換(トランスパイル)されて実行されるため、Lua を直接書くことで、DSL の宣言的な
記法だけでは表現しにくい処理――ループ・条件分岐・外部データ読み込み・カスタムイベント処理――を実装できる。
この章群が対象とするのは次の3領域である。
scripts/配下のカスタム Lua スクリプト: ゴーストディレクトリ直下のscripts/に置く。main.luaが エントリーポイントとして読み込まれ、シーン関数・単語定義・イベントハンドラ等をセットアップする。pasta_scripts/配下の標準ランタイムスクリプト: pasta.dll が起動時に自己展開するエンジン同梱スクリプト。 通常は触らないが、scripts/に同名ファイルを置けば動作を上書きできる。- Pasta DSL 内の
```lua ```ブロック:.pastaファイル中に埋め込む Lua コード。シーン関数として動作する。
対象 Lua 方言: LuaJIT 2.1
Pasta ランタイムが採用する Lua 方言は LuaJIT 2.1(言語仕様は Lua 5.1 系に相当)である。 これは本マニュアル全体で前提となる重要事項なので、最初に明示しておく。
- 利用できる言語機能・標準ライブラリは Lua 5.1 をベースとし、LuaJIT 独自の拡張(一部の Lua 5.2 機能の
バックポート、
goto、ffiライブラリ等)が加わる。 - Lua 5.2 / 5.3 / 5.4 / 5.5 系の新機能(整数型の分離、
<close>変数、新しいビット演算子など)は そのままでは使えない。学習やリファレンス参照の際は、必ず Lua 5.1 / LuaJIT に対応した資料を見ること。 版の離れた Lua 5.5 系の言語リファレンスは、方言が一致しないため案内しない。
外部の言語リファレンスは本マニュアルに取り込まず、リンク参照のみとする。具体的なリンク先は Lua の基礎 と 外部リンク集 にまとめてある。
この章群の歩き方
各章は次の順序で読み進めると理解しやすい。
| 章 | 内容 |
|---|---|
| Lua の基礎 | LuaJIT 2.1(Lua 5.1 系)の最低限の文法と、外部リファレンスへの入口 |
| 公開モジュール API | @pasta_* 等、ランタイムが公開する各モジュールの API と試せる例 |
| scripts/ の記述パターン | シーン関数・イベントハンドラ登録・単語一括投入などの定型 |
| DSL と Lua の使い分け | どちらで書くべきかの判断基準 |
肩慣らしはこのくらいにしておきましょう。理屈をこねるより、手を動かしたほうが早いですわ。 まずは Lua という言語そのものに、軽く挨拶してまいりましょう。さあ、熱く参りますわよ!
Lua の基礎
「プログラミングなんて初めて」――そんなあなたでも大丈夫。安心なさいまし。 Lua はとても小さく、覚えることの少ない言語ですの。ここでは Pasta のスクリプトを読み書きするのに 必要な最低限だけを、わたくしがつまんでお見せいたしますわ。完璧を目指す必要はございません。 ……フンッ、別にあなたが心配だから言っているわけではなくてよ。
この章は Lua そのものの最小限の入口である。網羅的な言語仕様は外部リファレンス(後述)に譲り、 ここでは Pasta のスクリプトを読むために知っておきたい要点だけを扱う。
前提: Pasta ランタイムの Lua 方言は LuaJIT 2.1(言語仕様は Lua 5.1 系相当)である。 本章のコードもこの方言に従う。Lua 5.2 以降で追加された構文・標準関数は前提にしない。
変数とローカル宣言
変数はデフォルトでグローバルになる。意図しないグローバル汚染を避けるため、原則として local を付けて
ローカル変数として宣言する。
local name = "ぱすた" -- ローカル変数(推奨)
local count = 0
greeting = "こんにちは" -- local なしはグローバル変数(原則避ける)
代入されていない変数の値は nil(値が無いことを表す特別な値)になる。
基本の型
Lua 5.1 系の主な型は次の通り。
| 型 | 説明 | リテラル例 |
|---|---|---|
nil | 値が無いことを表す | nil |
boolean | 真偽値 | true, false |
number | 数値(整数・小数の区別なし、内部は倍精度浮動小数点) | 42, 3.14 |
string | 文字列 | "text", 'text' |
table | 連想配列・配列・オブジェクトを兼ねる唯一の複合型 | {1, 2, 3}, {key = "value"} |
function | 関数(値として扱える) | function() end |
注意: Lua では
nilとfalseのみが「偽」と判定される。0や空文字列""は「真」である点に注意。
文字列
文字列はダブルクォートまたはシングルクォートで囲む。連結には .. 演算子を使う。
local who = "ぱすた"
local message = "こんにちは、" .. who .. "さん" -- "こんにちは、ぱすたさん"
print(#message) -- # は文字列のバイト長(UTF-8 では「文字数」ではない点に注意)
Pasta の辞書・スクリプトはすべて UTF-8 で扱う。日本語文字列はバイト単位で複数バイトを占めるため、
#str は文字数ではなくバイト長を返す。
テーブル
テーブルは Lua で唯一の複合データ構造であり、配列としても連想配列としても使える。
-- 配列的な使い方(インデックスは 1 始まり)
local fruits = { "りんご", "みかん", "ぶどう" }
print(fruits[1]) -- "りんご"(Lua の配列は 1 始まり)
-- 連想配列的な使い方
local actor = { name = "ぱすた", age = 17 }
print(actor.name) -- "ぱすた"
print(actor["age"]) -- 17(actor.age と同義)
Lua の配列インデックスが 1 始まりである点は、多くの言語と異なるので特に注意する。
制御構文
-- 条件分岐
if count > 0 then
print("正の数")
elseif count == 0 then
print("ゼロ")
else
print("負の数")
end
-- 数値 for(1 から 5 まで)
for i = 1, 5 do
print(i)
end
-- テーブルの反復(ipairs: 配列、pairs: 全要素)
for index, value in ipairs(fruits) do
print(index, value)
end
-- while ループ
local n = 3
while n > 0 do
n = n - 1
end
等値比較は ==、非等値は ~=(!= ではない)を使う。
関数
関数も値であり、変数に代入したりテーブルのフィールドにしたりできる。Pasta のシーン関数や イベントハンドラは、いずれもこの「テーブルのフィールドに関数を代入する」形を取る。
-- 名前付き関数
local function add(a, b)
return a + b
end
-- テーブルのフィールドに関数を代入(Pasta で多用する形)
SCENE.挨拶 = function(act)
-- ...
end
コメント
-- 1 行コメント
--[[
複数行コメント
]]
外部リファレンス(LuaJIT 2.1 / Lua 5.1 系)
より詳しい言語仕様は、対象方言に対応した次の資料を参照する。本マニュアルは言語仕様を取り込まず、 リンク参照のみとする方針である。版の離れた Lua 5.5 系の資料は方言が一致しないため案内しない。
- 日本語 Lua リファレンスマニュアル(Lua 5.2 相当): http://milkpot.sakura.ne.jp/lua/lua52_manual_ja.html
- 日本語 Lua リファレンスマニュアル(Lua 5.1): http://milkpot.sakura.ne.jp/lua/lua51_manual_ja.html
- LuaJIT 公式サイト: https://luajit.org/
- LuaJIT 言語拡張(Lua 5.2 互換機能・
goto等): https://luajit.org/extensions.html
これらのリンクは 外部リンク集 にもまとめてある。
ね、案外コンパクトでしたでしょう? これだけ押さえれば、Pasta のスクリプトはもう怖くありませんわ。 次は、ランタイムがあなたのために用意した便利な道具箱――公開モジュールを開けてまいりましょう。
公開モジュール API
さあ、道具箱を開ける時間ですわ。Pasta ランタイムは、ゴースト作りに必要な便利な機能を いくつものモジュールとして用意しておりますの。検索、永続化、ログ、文字コード変換―― どれもあなたの手間を肩代わりしてくれる、頼もしい子たちですわ。一つずつご紹介いたしましょう。
Pasta ランタイムは Rust 側から Lua VM へモジュール群を公開している。require で読み込んで使う。
本章では scripts/ 配下のスクリプトや DSL 内の ```lua ``` ブロックから利用できる公開モジュールを扱う。
対象方言は LuaJIT 2.1(Lua 5.1 系)である。
モジュール一覧
@ で始まる名前は Rust 組み込みモジュール(ランタイムが提供)、pasta. で始まる名前は
内部 Lua モジュールである。本章は主に前者の Rust 組み込みモジュールを扱う。
内部モジュール(pasta.word 等)の実用パターンは scripts/ の記述パターン で扱う。
| モジュール | 用途 | require 方法 |
|---|---|---|
@pasta_search | シーン・単語検索 | require 直接 |
@pasta_persistence | セーブデータ永続化 | require 直接 |
@pasta_sakura_script | さくらスクリプト変換 | require 直接 |
@enc | UTF-8 ⇔ ANSI 変換 | require 直接 |
@pasta_config | pasta.toml 設定読み取り | pcall(require, ...) 必須 |
@pasta_log | ロギング | require 直接 |
加えて mlua-stdlib 統合モジュール(@json / @yaml / @regex / @assertions / @testing)が
デフォルトで有効である。@env(環境変数・パスアクセス)はセキュリティ上の理由からデフォルト無効で、
通常のゴーストでは使えない。
@pasta_log — ロギング
最も手軽で、最初に覚えたいモジュール。Lua からのログ出力を Rust 側の tracing 基盤へ橋渡しする。
呼び出し元のファイル名・行番号・関数名が自動でログに付与される。RuntimeConfig の設定に関わらず常に利用可能。
trace / debug / info / warn / error の5レベルを持つ。
local log = require "@pasta_log"
log.info("ゴースト起動完了")
log.debug("変数の値: " .. tostring(some_var))
log.warn("非推奨 API が使用されています")
log.error("設定ファイルの読み込みに失敗")
-- 文字列以外もそのまま渡せる(table は JSON 変換、nil は空文字列に)
log.info({ player = "ユーザー", score = 100 }) -- {"player":"ユーザー","score":100}
log.trace(nil) -- "" として出力
各関数は任意の Lua 値を受け取り、内部で文字列へ変換する。変換に失敗してもエラーや panic は発生せず、
"<unconvertible value>" が出力される(ログ呼び出しがゴーストを止めない設計)。
@pasta_persistence — 永続化
セーブデータを JSON(または gzip 圧縮)形式でファイルに保存・読み込みする。
local persistence = require "@pasta_persistence"
-- 読み込み(ファイル未存在・破損時は空テーブル {} を返す)
local data = persistence.load()
data.play_count = (data.play_count or 0) + 1
-- 保存(成功で true、失敗で nil, error_message)
local ok, err = persistence.save(data)
if not ok then
log.error("保存失敗: " .. tostring(err))
end
load() はファイルが無い初回起動時や破損時でも例外を投げず、空テーブルを返す(データ損失防止のため
エラーは握りつぶす)。save(data) は循環参照や関数値を含むテーブルでは失敗するので、戻り値を確認する。
実際のゴーストでは、永続化データはシーン関数の act:init_scene() が返す save テーブル経由で
触ることが多い(scripts/ の記述パターン を参照)。
pasta.toml の [persistence] セクションで動作を設定できる。
[persistence]
obfuscate = true # gzip 圧縮を有効化(拡張子が .dat に)
file_path = "profile/pasta/save/save.json" # 保存先パス
debug_mode = false # デバッグログ出力
セーブキーの命名規約:
pasta_プレフィックスはエンジン予約領域である。ゴースト固有のキーにはpasta_を付けず、talk_countやmy_flagのような任意の名前を使うこと。
@pasta_config — 設定読み取り
pasta.toml のカスタムフィールドへ読み取り専用でアクセスする。テスト環境やスタンドアロン実行時には
pasta.toml が存在しないことがあるため、pcall で保護して require するのが必須である。
local ok, config = pcall(require, "@pasta_config") -- pcall 必須
if ok then
print(config.character.name) -- トップレベル
print(config.character.appearance.hair_color) -- ネストしたアクセス
local version = config.system and config.system.version or "unknown" -- nil ガード
end
[loader] セクション以外のすべてのセクション・フィールドが公開される([loader] は内部設定のため
除外)。値は読み取り専用で、TOML の型・ネスト構造がそのまま保持される。設定ファイル未存在時は空テーブルになる。
@pasta_search — シーン・単語検索
シーンと単語を前方一致で検索する。フォールバック戦略(ローカル → グローバル)を備える。
利用可能タイミング:
require("pasta").finalize_scene()呼び出し後にのみ使用できる。 通常はトランスパイル済みコードの読み込み後にランタイムが自動で呼ぶため、シーン関数内では使用可能。
local SEARCH = require "@pasta_search"
-- シーン検索(成功で global_name, local_name の2値、失敗で nil)
local global, local_name = SEARCH:search_scene("メイン")
if global then
print(global, local_name) -- 例: "メイン_1", "__start__"
end
-- ローカル優先検索(第2引数に親グローバルシーン名を指定)
local g, l = SEARCH:search_scene("選択肢", "メイン_1")
-- 単語検索(成功で値の文字列、失敗で nil)
local word = SEARCH:search_word("挨拶")
このモジュールはテスト時の決定論的制御 API(set_scene_selector / set_word_selector)も持つ。
詳細は本マニュアルの範囲外だが、複数候補からの選択順序を整数インデックス(0 始まり)で固定できる。
実際のシーン関数では、@pasta_search を直接呼ぶより、act:word(name) や ACT オブジェクトの
検索メソッド経由で使うことが多い(内部で @pasta_search を利用する)。
@pasta_sakura_script — さくらスクリプト変換
セリフテキストにウェイトタグ(\_w[ms])を自動挿入し、自然な会話テンポのさくらスクリプトへ変換する。
既存のさくらスクリプトタグ(\s[5] 等)はそのまま保持される。
local SAKURA_SCRIPT = require "@pasta_sakura_script"
local actor = { talk = {} } -- talk サブテーブルにウェイト設定を持つ
local result = SAKURA_SCRIPT.talk_to_script(actor, "こんにちは。")
-- 結果: "こ\_w[50]ん\_w[50]に\_w[50]ち\_w[50]は\_w[100]。"
ウェイト値は actor.talk テーブルのフィールド(script_wait_default = 50ms、script_wait_period = 100ms 等)で
調整できる。pasta.toml の [talk] セクションでデフォルト値を設定でき、actor 側の設定が優先される。
budoux 日本語分割モデルを使った自動改行 break_lines(text, widths) も提供する。actor に budoux
フィールドが設定されていれば talk_to_script が自動で後段適用するため、通常は直接呼ぶ必要はない。
なお、シーン関数で act:talk(actor, text) を使うと、このモジュールが内部的に呼ばれてテキストが
自動変換される。多くの場合、@pasta_sakura_script を直接 require する必要はない。
@enc — 文字コード変換
UTF-8 と ANSI(システムロケール、Windows では CP932 / Shift_JIS)間の文字コード変換。
主に Windows 環境でのファイルパス処理に使う。(result, error) の2値を返す。
local enc = require "@enc"
-- UTF-8 → ANSI
local ansi_path, err = enc.to_ansi("C:/ユーザー/設定.txt")
if ansi_path then
local file = io.open(ansi_path, "r")
-- ...
else
log.error("変換エラー: " .. tostring(err))
end
-- ANSI → UTF-8
local utf8_path = enc.to_utf8(some_windows_api_result())
プラットフォーム依存: 変換結果は OS のロケールに依存する。Windows 以外では挙動が異なる可能性がある。
mlua-stdlib 統合モジュール
汎用ユーティリティとして次のモジュールがデフォルトで有効である。
-- JSON
local json = require "@json"
local str = json.encode({ name = "test", value = 42 })
local obj = json.decode('{"name": "test"}')
-- YAML
local yaml = require "@yaml"
local y = yaml.encode({ name = "test" })
-- 正規表現
local regex = require "@regex"
local pattern = regex.new("\\d+")
local matches = pattern:find_all("abc123def456")
@assertions と @testing はテスト記述用に有効化されている。@env(環境変数・ファイルパスアクセス)は
デフォルト無効で、有効化には Rust 側の RuntimeConfig 設定が必要なため、通常のゴーストからは使えない。
道具の名前と使い方を、ざっと頭に入れていただけたかしら。フンッ、最初は覚えきれなくても結構ですわ。
必要になったらこの章へ戻ってくればよいのですから。次は、これらの道具を実際の scripts/ で
どう組み合わせるか――生きた書き方を見ていきましょう。さあ、熱く参りますわよ!
scripts/ の記述パターン
道具の名前を覚えたら、次は使いこなしですわ。ここでは、実際の scripts/ でよく出てくる
書き方の「型」を集めましたの。シーン関数の定型、イベントの受け止め方、単語の一括投入――
どれも一度覚えれば一生もの。型をなぞるだけで、あなたのゴーストはぐっと賢くなりますわよ。
この章では scripts/ 配下のカスタム Lua スクリプト、および Pasta DSL 内の ```lua ``` ブロックで
頻出する記述パターンを示す。対象方言は LuaJIT 2.1(Lua 5.1 系)である。
scripts/ の役割と優先順位
ゴーストディレクトリ直下の scripts/ にユーザーカスタム Lua スクリプトを置く。main.lua が
エントリーポイントとしてランタイムに読み込まれる。
scripts/(ゴーストカスタム)はpasta_scripts/(エンジン標準ランタイム)より優先される。 同名ファイルを置けばエンジン動作を上書きできる。pasta_scripts/は pasta.dll が起動時に自己展開するエンジン同梱スクリプトで、通常は触らない。
パターン 1: シーン関数の定型
すべてのシーン関数は act:init_scene(SCENE) で始まる。これが必須の定型である。
戻り値の save(セッション間で永続するデータ)と var(このアクション内だけの一時変数)を受け取る。
function SCENE.挨拶(act)
local save, var = act:init_scene(SCENE) -- 必須: save / var を取得
act:talk(act.ぱすた.actor, "こんにちは!") -- アクター名でトーク
act:yield() -- 蓄積トークンを送出
end
| 要素 | 役割 |
|---|---|
act:init_scene(SCENE) | シーン初期化。save(永続)と var(一時)を返す。必須 |
act:talk(actor, text) | アクターのセリフを出力(さくらスクリプトへ自動変換) |
act:yield() | 蓄積したトークンを送出し、トークの区切りを作る |
save は @pasta_persistence 管理の永続変数で、セッションをまたいで保持される。var は
そのアクション実行中のみ有効な一時変数である。
function SCENE.カウント(act)
local save, var = act:init_scene(SCENE)
save.count = (save.count or 0) + 1 -- セッション間で累積
var.temp = "一時データ" -- このアクション内のみ
act:talk(act.ぱすた.actor, save.count .. "回目ですね")
act:yield()
end
複数トークと表示制御
act のメソッドはチェーンできる。複数のセリフ区切りは yield() を複数回呼んで作る。
function SCENE.物語(act)
local save, var = act:init_scene(SCENE)
act:talk(act.ぱすた.actor, "最初のセリフ")
act:yield() -- ここで一区切り
act:surface(5):wait(500):talk(act.ぱすた.actor, "驚いた!"):newline()
act:yield() -- 2区切り目
end
主な表示制御メソッド: surface(id)(サーフェス変更)、wait(ms)(ウェイト)、newline(n?)(改行)、
clear()(表示クリア)。いずれも self を返すのでチェーン可能。
選択肢
function SCENE.分岐(act)
local save, var = act:init_scene(SCENE)
act:talk(act.ぱすた.actor, "どうする?")
act:choice("挨拶", "挨拶する") -- ジャンプ先シーン名, 表示テキスト
act:choice("自己紹介") -- 表示テキスト省略時はシーン名を表示
act:choice_timeout(30) -- 30秒でタイムアウト
act:yield()
end
選択肢が選ばれると、ランタイムが OnChoiceSelectEx を発火し、選択 ID(choice の第1引数)を
シーン名として前方一致検索して該当シーンを実行する。通常はこの自動ルーティングに任せればよい。
パターン 2: イベントハンドラの登録
カスタム SHIORI イベント処理は REG テーブルにハンドラを登録し、RES でレスポンスを返す。
local REG = require("pasta.shiori.event.register")
local RES = require("pasta.shiori.res")
REG.OnBoot = function(req)
local shell_name = req.reference[0] -- Reference0
return RES.ok("\\h\\s[0]起動しました。\\e")
end
REG.OnClose = function(req)
local reason = req.reference[0]
if reason == "user" then
return RES.ok("\\h\\s[0]またね。\\e")
end
return RES.no_content() -- 表示なしで処理完了
end
ハンドラは req(SHIORI リクエスト情報)を受け取る。主なフィールド:
| フィールド | 説明 |
|---|---|
req.id | イベント名("OnBoot" 等) |
req.reference[N] | Reference ヘッダ(0 始まり)。未送信時は nil |
req.date | 日時情報 |
req.status | ステータス("talking" 等) |
レスポンス生成 API:
| 関数 | 説明 |
|---|---|
RES.ok(value) | 200 OK + さくらスクリプト |
RES.ok_with(headers) | 200 OK + 複数ヘッダ |
RES.no_content() | 204 No Content(表示なし) |
RES.err(message) | 500 エラー |
REG 未登録時のフォールバック
REG にハンドラが無いイベントは、同名のグローバルシーンが自動的に検索・実行される。
つまり DSL で *OnBoot シーンを定義しておけば、REG.OnBoot を書かなくても起動時に呼ばれる。
凝った分岐が要るときだけ REG でハンドラを書く、というのが基本方針である。
OnTalk(ランダムトーク)と OnHour(時報)は、ランタイムの仮想ディスパッチャが
OnSecondChange を起点に自動発行する。これらも対応するシーンを定義しておけば呼ばれる。
パターン 3: 単語の一括投入
数十〜数百件の単語を投入したいときは、DSL の単語定義より Lua のループが向く。
pasta.word モジュールのビルダーパターンを使う。
local WORD = require("pasta.word")
-- グローバル単語: ループで一括投入
local foods = { "ラーメン", "カレー", "寿司", "焼肉", "パスタ" }
local builder = WORD.create_global("好きな食べ物")
for _, food in ipairs(foods) do
builder:entry(food)
end
-- メソッドチェーンでまとめて
WORD.create_global("挨拶")
:entry("こんにちは", "やあ")
:entry("ごきげんよう")
-- ローカル単語(シーン名スコープ)
WORD.create_local("メイン_1", "返事")
:entry("はい", "ええ")
:entry("そうね")
-- アクター単語(アクター名スコープ)
WORD.create_actor("ぱすた", "一人称")
:entry("わたし")
:entry("あたし")
| ファクトリ関数 | スコープ |
|---|---|
WORD.create_global(key) | グローバル |
WORD.create_local(scene_name, key) | ローカル(指定シーン内) |
WORD.create_actor(actor_name, key) | アクター |
entry(...) は可変長引数で値を追加し、self を返すのでチェーンできる。外部 JSON / YAML を
@json / @yaml で読み込んでループ投入すれば、データ駆動の大規模辞書も構築できる。
パターン 4: グローバル関数の定義
DSL から呼び出せるユーザー定義関数は pasta.global(GLOBAL)テーブルに登録する。
local GLOBAL = require("pasta.global")
GLOBAL.時報 = function(act)
return os.date("%H") .. "時です"
end
-- DSL から呼び出し: @*時報() (グローバル関数は * 付きで呼ぶ)
DSL の @関数名() はローカル(SCENE.)呼び出し、@*関数名() がグローバル(GLOBAL.)呼び出しである点に注意する。
型は出そろいましたわ。あとはこれらを組み合わせて、あなただけのゴーストを織り上げるだけ。 でも――Lua で書くべきか、DSL で済ませるべきか、迷う場面もございましょう? 次の章で、その見極め方をきっちりお教えいたしますわ。さあ、最後まで参りましょう!
DSL と Lua の使い分け
最後の関門ですわ。Pasta では同じことを DSL でも Lua でも書けてしまう場面が多々ございますの。 さて、どちらを選ぶべきか――迷ったあなたへ、わたくしが明快な物差しを差し上げましょう。 フンッ、これさえ覚えれば、もうあなたは立派なゴースト職人ですわよ。
Pasta DSL(.pasta)と Lua は役割が異なる。DSL は宣言的で読みやすく、Lua は手続き的で柔軟である。
基本方針は「まず DSL、それで足りなければ Lua」。DSL のほうが可読性が高く保守しやすいため、
表現できる範囲では DSL を優先し、DSL の制御構文では届かない領域で Lua に切り替える。
判断基準
| ケース | 推奨 | 理由 |
|---|---|---|
| 数個の単語定義 | DSL(@単語:値1、値2) | 宣言的で簡潔 |
| 数十〜数百件の単語一括投入 | Lua(WORD.create_*) | ループ・外部データ読み込みが必要 |
| 基本的なシーン定義 | DSL(*シーン名) | 可読性が高い |
| 条件分岐を含む複雑なロジック | Lua(シーン関数) | DSL の制御構文は限定的 |
| カスタム SHIORI イベント処理 | Lua(REG テーブル) | DSL ではイベントハンドラを直接定義できない |
| 外部データ(JSON / YAML)の読み込み | Lua(@json / @yaml) | DSL に外部ファイル操作機能はない |
DSL が向く場面
- 静的な会話・単語の宣言: 「いつ、誰が、何を話すか」を並べるだけの素直なシーンや、少数の単語定義。
DSL の
*シーン名とアクション行、@単語:値…記法が最も読みやすい。 - イベントに対応するシーン:
*OnBoot、*OnHour12のようにイベント名・時報名でシーンを定義すれば、 ランタイムのフォールバックや仮想ディスパッチャが自動で呼び出す。多くの場合 Lua は不要。
Lua が向く場面
- 大量・データ駆動の単語投入: 数十件を超える単語や、外部ファイルから読み込むデータは、
Lua のループ(
WORD.create_*+entry)で投入する。scripts/ の記述パターン を参照。 - 複雑な条件分岐・状態管理:
saveを使った永続フラグでの分岐、ランダム以外の選択ロジックなど、 DSL の限定的な制御構文では表しにくい処理は Lua のシーン関数で書く。 - カスタムイベントハンドラ: 標準のフォールバックでは扱えないイベント処理は
REGテーブルに登録する。 - 外部データ連携: JSON / YAML の読み込み・整形は
@json/@yamlを使う Lua の領域である。
両者の連携
DSL と Lua は分断されていない。DSL のシーン本体に ```lua ``` ブロックを埋め込めば、その部分だけ
Lua で書ける。また DSL のシーン・単語定義は内部的に Lua(SCENE.xxx 関数や WORD.create_* 呼び出し)へ
変換されて実行される。つまり「DSL で骨格を組み、要所だけ Lua で肉付けする」という併用が自然な形である。
-- DSL の ```lua ブロック内で書くシーン関数も、scripts/ の関数も同じ定型
function SCENE.func_name(act)
local save, var = act:init_scene(SCENE) -- 必須
act:talk(act.ぱすた.actor, "セリフ")
act:yield()
end
迷ったときの指針: 読みやすさが保てるなら DSL、表現力が要るなら Lua。 そして両者は併用できることを忘れないこと。
これで Lua 編は修了ですわ。よく最後までついていらっしゃいました。 ……フンッ、別に褒めているわけではなくてよ。けれど、あなたならきっと素敵なゴーストを作れますわ。 さあ、胸を張って、あなただけの物語を紡いでまいりましょう! ごきげんよう。
デバッグ概要
ごきげんよう。わたくし Claudia が、VSCode から .pasta をソースレベルでデバッグする手立てを、有効化から接続、停止位置の読み解き方までご案内いたしますわ。
バグ取りは退屈な作業だと思っていらして? いいえ、自分の書いた .pasta の行で実行が止まり、変数の中身が手に取るように見えるのですから、これほど痛快なことはございませんわよ。さあ、肩の力を抜いて参りましょう。
この章では、デバッグ機能の全体像と有効化方法、そして実ゴーストを題材にした短いウォークスルーを扱う。接続の詳細・.pasta 操作の詳細・構造的制約は、それぞれ後続のページに分かれている。
デバッグの全体像
pasta のデバッグは、VSCode のデバッグ拡張から Rust 側のデバッグバックエンドへ接続(attach)して操作する方式である。先に pasta ランタイム側がデバッグを有効化して接続待ち受けを開始し、そこへ VSCode が後から接続する。逆向きに VSCode 側からランタイムを起動する launch 方式は採らない。
最大の特長は、生成された .lua だけでなく、作者が書いた .pasta のソースレベルでデバッグできる点にある。これは本番提供の機能であり、次の操作が .pasta の座標(ファイル名・行)で行える。
.pastaファイルの行へのブレークポイント設定- ブレーク時の
.pasta座標での停止位置・コールスタック提示 .pasta粒度のステップ実行(over / into / out)- 停止中の変数 inspect
- 提示モードの切替(
.pasta既定 ⇄.lua)
.pasta 操作の詳しい手順は .pasta ソースレベル操作 を参照する。
デバッグの有効化
デバッグは既定で無効である。有効化には pasta.toml の設定と環境変数の 2 経路があり、後述のとおり環境変数が設定ファイルより優先される。
pasta.toml による有効化
pasta.toml の [debug] セクションで設定する。本ページで扱う 2 つのキーと既定値は次のとおり。
| キー | 既定 | 意味 |
|---|---|---|
enabled | false | デバッグバックエンドの有効化 |
port | 9276 | DAP リスナーが待ち受ける TCP ポート |
設定例:
[debug]
enabled = true
port = 9276
環境変数による有効化
環境変数でも同じ設定を行える。
| 環境変数 | 役割 |
|---|---|
PASTA_DEBUG | デバッグの有効化 |
PASTA_DEBUG_PORT | 待ち受けポートの上書き |
PASTA_DEBUG は truthy な値で有効化される。truthy と判定される値は 1 / true / yes / on であり、大文字小文字は区別されない(前後の空白は除去して判定される)。0 / false / no / off / 空文字列は無効とみなされる。
PASTA_DEBUG=1
PASTA_DEBUG_PORT=9276
優先順位
設定ファイルと環境変数が併存する場合、環境変数が設定ファイルより優先される。最終的な値は次の順位で決まる。
環境変数 > pasta.toml [debug] > 既定値
たとえば pasta.toml で enabled = false としていても、PASTA_DEBUG=1 が設定されていればデバッグは有効になる。ポートも同様に、PASTA_DEBUG_PORT が pasta.toml の port を上書きする。
無効時のゼロコスト
デバッグが無効のとき、pasta は本番運用に追加コストを生じさせない。具体的には次のとおり。
- デバッグ用のフックを設置しない。
- 待ち受けポートを開かない(TCP リスナーを bind しない)。
- Lua の
debug機能をスクリプトへ露出しない。これによりサンドボックスが維持される。
このため、デバッグ設定を本番の pasta.toml に残したまま enabled = false(既定)で配布しても、デバッグを有効化しない限り挙動・性能・安全性に影響はない。
ウォークスルー(hello-pasta)
ここでは、サンプルゴースト hello-pasta を題材に、ブレークポイント設定から停止位置・変数の確認、再開までの一連の流れを簡潔に辿る。VSCode 拡張の導入や launch.json の具体的な記述といった接続の詳細は VSCode 接続手順 で扱うため、ここでは流れの全体像をつかむことに集中する。
- デバッグを有効化する。 hello-pasta の
pasta.tomlで[debug]のenabled = trueとするか、起動環境でPASTA_DEBUG=1を設定する。既定ポートは9276である。 .pasta行にブレークポイントを設定する。 VSCode で対象の.pastaファイルを開き、止めたい行のガター(行番号の左)をクリックしてブレークポイントを置く。- VSCode から attach する。 ゴーストを起動して待ち受けが始まった状態で、VSCode のデバッグビューから「Pasta: Attach」構成を実行し、
127.0.0.1:9276へ接続する。詳細な構成手順は VSCode 接続手順 を参照する。 .pasta座標で停止位置と変数を確認する。 設定した行に実行が達するとブレークし、停止位置とコールスタックが.pastaの座標で表示される。変数ビューで停止時点の変数の値を確認できる。- continue で再開する。 内容を確認したら continue(実行再開)でゴーストの動作を続行する。
.pasta 行が思った場所で止まらない、変数の見え方を変えたいといった各論は .pasta ソースレベル操作 に、ブレーク中にゴーストが無反応になる理由とその回避運用は 構造的制約と緩和策 にまとめている。
ポートを変更するときの注意
待ち受けポートを既定の 9276 から変更する場合は、有効化側の設定(pasta.toml の port または PASTA_DEBUG_PORT)と、VSCode の接続構成(launch.json の port)の両方を同じ値にそろえる必要がある。片方だけ変更すると接続できない。接続構成側のポート設定の詳細は VSCode 接続手順 を参照する。
ここまでで全体像はつかめましたわね。フンッ、別にあなたが呑み込みが早いなんて言っていませんわよ。 細かな接続の作法は次のページでみっちり仕込んで差し上げますから、ご安心なさいまし。さあ、熱く参りましょう!
VSCode 接続と拡張導入
ごきげんよう。デバッグの扉を開くには、まず VSCode 拡張をお迎えして実行中の pasta につなぐ必要がございますわ。
道具立てさえ整えてしまえば、あとは launch.json に数行したためるだけ。むずかしいことは何もございませんから、わたくしと一緒に、一歩ずつ参りましょう。
このページでは、pasta VSCode 拡張の導入手順と、launch.json によるアタッチ構成の設定方法を扱う。デバッグの全体像と有効化方法は デバッグ概要 を、.pasta のソースレベル操作(行ブレークポイント・ステップ・変数 inspect・提示モード切替)は .pasta ソースレベル操作 を、ブレーク中の構造的制約は 構造的制約と緩和策 を参照する。
pasta VSCode 拡張の導入
VSCode から pasta をデバッグするには、pasta VSCode 拡張が必須である。この拡張がデバッグ統合(デバッグ種別 pasta の登録、.pasta / .lua 行のブレークポイント対応、アタッチ構成の補完)を提供しており、拡張が未導入の環境ではデバッグを開始できない。本ページは拡張が未導入の読者を前提に、導入手順から説明する。
導入対象の拡張は次のとおり。
| 項目 | 値 |
|---|---|
| 表示名(displayName) | Pasta DSL |
| 発行元(publisher) | ekicyou |
| 識別子(name) | pasta-vscode |
導入経路は 2 つある。通常は主経路(Marketplace)で導入すればよい。
主経路: VS Code Marketplace からインストール
VSCode の拡張機能ビュー(サイドバーの拡張アイコン、または Ctrl+Shift+X)を開き、検索ボックスに「Pasta DSL」と入力する。発行元が ekicyou の拡張を選び、「インストール」を押す。これが推奨される導入方法である。
代替経路: VSIX を手動インストール
Marketplace を利用できない環境では、GitHub Releases で配布される VSIX 資産を手動でインストールできる。資産名は次の形式である(<version> はリリース版番号に置き換わる)。
pasta-vscode-<version>.vsix
ダウンロードした .vsix ファイルは、拡張機能ビュー右上のメニューから「VSIX からのインストール(Install from VSIX…)」を選んで指定する。
VSCode 本体の導入について
VSCode 本体が未導入の場合は、まず VSCode をインストールする。本マニュアルでは本体のインストール手順は重複記述せず、公式サイトの案内に委ねる。
- VS Code 公式サイト: https://code.visualstudio.com/
attach 方式(アタッチ接続)
pasta のデバッグは、VSCode のデバッグ拡張から Rust 側のデバッグバックエンドへ アタッチ(attach)して操作する方式である。先に pasta ランタイム(VM)がデバッグ有効の状態で起動して接続を待ち受け、そこへ VSCode が後から接続する、いわば逆向きの接続となる。VSCode 側からランタイムを起動する launch 方式は提供しておらず、対応するリクエスト種別は attach のみである。
したがって接続前に、デバッグを有効化したうえでゴーストを起動し、待ち受けが始まっている状態にしておく。有効化の方法は デバッグ概要 を参照する。
launch.json の設定例
VSCode のアタッチ構成は、ワークスペースの .vscode/launch.json に記述する。launch.json は version と configurations(構成の配列)を持つファイルであり、アタッチ構成はその配列の要素として書く。次がファイル全体の再現可能な例である。このまま .vscode/launch.json に貼り付けて使える完全な形であり、内側の構成オブジェクトだけを単体で貼ると無効になる点に注意する。
{
"version": "0.2.0",
"configurations": [
{
"type": "pasta",
"request": "attach",
"name": "Attach to Pasta Debug Backend",
"host": "127.0.0.1",
"port": 9276,
"sourcePresentation": "pasta"
}
]
}
configurations 配列内の各構成オブジェクトのフィールドの意味は次のとおり。
| フィールド | 値 | 説明 |
|---|---|---|
type | "pasta" | デバッグ種別。pasta 拡張が登録する識別子。 |
request | "attach" | リクエスト種別。attach のみ対応。 |
name | 任意 | デバッグ構成の表示名。 |
host | "127.0.0.1"(既定) | 接続先ホスト。 |
port | 9276(既定) | 接続先 TCP ポート。 |
sourcePresentation | "pasta" または "lua"(任意) | 提示モード。省略可。 |
sourcePresentation は任意のフィールドで、"pasta" と "lua" のいずれかを指定する。停止位置やコールスタックを .pasta の座標で見るか、生成された .lua の座標で見るかを選ぶ設定である。省略した場合はバックエンド側の設定(環境変数 > pasta.toml > 既定)に委ねられ、最終的な既定は .pasta である。提示モードの詳細は .pasta ソースレベル操作 を参照する。
既定の接続先
接続先の既定値は 127.0.0.1:9276(TCP)である。これは loopback(ローカルホスト)に限定された接続であり、外部のネットワークからは接続できない。デバッグセッションは同一マシン内で完結する。
ポートを変更するときの整合
待ち受けポートを既定の 9276 から変更する場合は、有効化側のポート設定と launch.json の port を必ず同じ値にそろえる必要がある。一方だけを変更すると、接続先が一致せずアタッチに失敗する。
- 有効化側:
pasta.tomlの[debug]セクションのport、または環境変数PASTA_DEBUG_PORT。 - 接続側:
launch.jsonのport。
たとえば PASTA_DEBUG_PORT=9300 で起動したなら、launch.json の port も 9300 にする。有効化側の設定の詳細は デバッグ概要 を参照する。
VSCode 以外の DAP クライアントについて
本ページの接続手順は VSCode を主軸に記述しているが、pasta のデバッグバックエンドは DAP(Debug Adapter Protocol)を TCP 上で話すホスト非依存の実装である。そのため、DAP over TCP に対応した VSCode 以外の DAP 互換クライアントからも、同じ 127.0.0.1:9276(または変更したポート)へアタッチして接続しうる。本マニュアルでは具体的な手順は VSCode を前提に説明する。
ここまでで道具と接続の作法は整いましたわね。フンッ、別にあなたが筋が良いなんて言っていませんわよ。
あとは実際に .pasta の行で止めて、変数の中身を覗くだけ。次のページで、その醍醐味をたっぷり味わっていただきますわ。さあ、熱く参りましょう!
.pasta ソースレベルのデバッグ操作
ごきげんよう。生成された .lua ではなく、あなた自身が綴った .pasta の座標で歩を進められるのが、この機能の真骨頂ですのよ。
転記された別言語の海に放り出されて途方に暮れる――そんな惨めな思いは、もうなさらなくてよろしいの。あなたが書いた行で止まり、あなたが名付けた変数を覗ける。さあ、心ゆくまで使い倒して差し上げましょう。
このページでは、.pasta 行へのブレークポイント設定、停止位置とコールスタックの確認、ステップ実行、変数の inspect、提示モードの切替、そして任意のサイドカー出力を扱う。デバッグ機能の全体像と有効化は デバッグ概要、VSCode への接続手順は VSCode 接続手順、ブレーク中の構造的制約は 構造的制約と緩和策 を参照する。
ここで説明する .pasta ソースレベルのデバッグは、すべて本番提供の機能である。実験的な試みでも将来の予定でもなく、出荷済みの実装として今すぐ利用できる。
.pasta 行へのブレークポイント
ブレークポイントは、生成された .lua ではなく .pasta ファイルの行に直接設定できる。VSCode で対象の .pasta ファイルを開き、止めたい行のガター(行番号の左の余白)をクリックすると、その行にブレークポイントが置かれる。
pasta VSCode 拡張は、ブレークポイントを設定できる言語として pasta と lua の両方を登録している。したがって .pasta ファイルの行にも、生成された .lua ファイルの行にも、いずれにもブレークポイントを設定できる。通常は .pasta 行に設定すれば足りる。
.pasta 座標での停止とコールスタック
ブレークポイントで実行が停止すると、停止位置とコールスタックは生成された .lua ではなく .pasta の座標(.pasta ファイル名・行)で提示される。
これは、転記された .lua の行番号を読み替える手間なしに、自分が書いたソースの位置をそのまま確認できることを意味する。コールスタックも同様に .pasta 単位で表示されるため、どのシーンやアクションから現在の停止位置へ到達したのかを .pasta のまま追える。
.pasta 粒度のステップ実行
停止中は、.pasta 粒度でステップ実行できる。利用できるステップは次の 3 種類である。
| ステップ | 動作 |
|---|---|
| ステップオーバー(over) | 現在行を実行し、呼び出し先には入らず次の .pasta 行へ進む |
| ステップイン(into) | 呼び出し先に入り、その内部の .pasta 行で停止する |
| ステップアウト(out) | 現在のフレームを抜け、呼び出し元の .pasta 行へ戻る |
これらのステップは、コルーチンを跨ぐ実行(yield / resume を跨ぐ実行)でも一様に動作する。pasta の実行はコルーチンの境界をまたいで進むことがあるが、ステップ操作はその境界を意識させず、.pasta 粒度で一貫して動く。コルーチンの切り替えのたびにステップが崩れる、といった挙動は起きない。
変数の inspect
実行が停止しているあいだ、変数を inspect できる。数値・文字列・真偽値・テーブルなど、各種の値を確認できる。テーブルは展開して内部の要素をたどれる。
コルーチン本体フレームの局所変数も取得できる。コルーチンの内側で停止した場合でも、そのフレームに属する局所変数の値を変数ビューで確認できるため、コルーチンを使った処理の途中状態を直接観察できる。
提示モードの切替
提示モードとは、停止位置・コールスタック・ステップを .pasta の座標で見せるか、生成された .lua の座標で見せるかの設定である。既定は .pasta であり、必要に応じて .lua に切り替えられる。
提示モードには 2 つの決まり方がある。ひとつは attach 時に決まる初期モード(次節「初期モードの指定」)、もうひとつは デバッグセッション中にいつでも切り替えられる実行時トグル(後述の「実行時トグル」)である。実行時トグルは初期モードを上書きし、以後のセッションを通じて優先される。
初期モードの指定
提示の初期モードを決める経路は 3 つあり、次の優先順位で適用される。
sourcePresentation > PASTA_DEBUG_SOURCE_MODE > present_as > 既定 .pasta
各経路の詳細は次のとおり。
| 経路 | 指定場所 | 取り得る値 | 補足 |
|---|---|---|---|
sourcePresentation | DAP attach 引数(launch.json) | "pasta" / "lua" | 最優先。指定があればこれが採用される |
PASTA_DEBUG_SOURCE_MODE | 環境変数 | pasta / lua | 不正な値のときは .pasta にフォールバックし、警告を出す |
present_as | pasta.toml の [debug] | pasta / lua | 省略時は .pasta |
| 既定 | (指定なし) | .pasta | いずれの経路でも指定がない場合 |
優先順位の要点は、より接続側に近い指定ほど優先されることである。launch.json の sourcePresentation が最も強く、次に環境変数 PASTA_DEBUG_SOURCE_MODE、次に pasta.toml の present_as、いずれも指定がなければ既定の .pasta となる。
ただしこの優先順位が決めるのは、あくまで attach 時の初期モードである。デバッグセッションが始まったあとは、後述の実行時トグルでこの初期モードを上書きできる。実行時に一度切り替えれば、その新しいモードが以後のセッションを通じて採用される(初期モードへ自動で戻ることはない)。
launch.json 側で提示モードを .pasta に指定する例:
{
"type": "pasta",
"request": "attach",
"name": "Pasta: Attach",
"host": "127.0.0.1",
"port": 9276,
"sourcePresentation": "pasta"
}
環境変数で .lua 提示に切り替える例:
PASTA_DEBUG_SOURCE_MODE=lua
pasta.toml で既定の提示モードを定める例:
[debug]
present_as = "lua"
launch.json の構成全体や接続の手順は VSCode 接続手順 を参照する。
実行時トグル(VSCode コマンド/ボタン/ステータスバー)
デバッグセッションの最中に、提示モードを .pasta ⇔ .lua へ即座に切り替えられる。セッションを張り直す必要はない。.pasta 行で停止して自分のソースを追いつつ、いまの停止位置が生成 .lua ではどう転記されているのかを確かめたくなったら、その場で .lua 提示へ切り替え、確認が済んだら .pasta 提示へ戻す――そうした行き来が一手でできる。
切替の導線は 3 つあり、いずれも同じトグル操作を起動する。
| 導線 | 操作 | 利用できる条件 |
|---|---|---|
| コマンドパレット | Pasta: 提示モードを切り替え (.pasta / .lua) を実行する(コマンド ID は pasta.debug.toggleSourcePresentation) | Pasta デバッグセッション中のみ一覧に現れる |
| デバッグツールバーのボタン | デバッグツールバーに表示される目($(eye))のボタンを押す | Pasta デバッグセッション中のみ表示される |
| ステータスバー | 現在モードを示すステータスバー項目をクリックする | Pasta デバッグセッション中のみ表示される |
ステータスバーには、Pasta デバッグセッションの間つねに現在の提示モードが表示される。表示は $(eye) 提示: .pasta または $(eye) 提示: .lua の形で、いまどちらのモードで提示されているかを一目で判別できる。切り替えると、この表示も即座に新しいモードへ更新される。なお、これらの導線は Pasta デバッグセッションが実行中のときにのみ有効である。アクティブな Pasta デバッグセッションがないときに切替を試みても提示モードは変わらず、操作が無効である旨が通知される。
切替を行うと、観測される提示は次のように変わる。
- 停止中であれば即座に再描画される。停止位置・コールスタック・source の提示が、追加の操作(ステップ実行やフレームの選び直し)を要さずに、切替後のモードの座標で描き直される。
.pastaから.luaへ切り替えれば停止位置・コールスタック・source は生成.luaの座標で、.luaから.pastaへ戻せば.pastaの座標で提示される。 - 実行中(停止していない)に切り替えた場合は、その切替は受理され、次に停止したときの提示へ反映される。
.pasta行に張ったブレークポイントは、切替をまたいでそのまま有効である。提示モードを.luaに切り替えても、.pasta行のブレークポイントは引き続きヒットし続ける。変わるのは見え方(提示の座標)だけで、どこで止まるかは変わらない。
ステップ実行の粒度も、現在の提示モードに従う。.pasta 提示のあいだは .pasta 粒度のステップ実行で進み、.lua 提示へ切り替えると生成 .lua の行単位(.lua 粒度)で進む。停止中に提示モードを切り替えれば、以後のステップ操作は切替後の粒度で行われる。
サイドカー出力(任意)
ソースマップは常にメモリ内に保持されており、.pasta ソースレベルのデバッグはこのメモリ内マップだけで成立する。これに加えて、ソースマップをディスク上のサイドカーファイルとして任意で出力できる。サイドカー出力は既定では無効であり、明示的に有効化したときだけ生成される追加の出力である。
有効化の経路は 2 つある。
| 経路 | 指定場所 | 既定 | 有効化条件 |
|---|---|---|---|
source_map_sidecar | pasta.toml の [debug] | false | true にする |
PASTA_DEBUG_SOURCE_MAP_SIDECAR | 環境変数 | (無効) | truthy な値にする |
pasta.toml で有効化する例:
[debug]
source_map_sidecar = true
環境変数で有効化する例:
PASTA_DEBUG_SOURCE_MAP_SIDECAR=1
出力先は、生成された .lua の真隣に置かれる <lua_path>.map である。たとえば生成された .lua が .../scene/sys.lua であれば、サイドカーは .../scene/sys.lua.map として書き出される。形式は JSON である。
サイドカーはあくまで追加の任意出力であり、メモリ内のマップが常に主である。サイドカーの書き込みに失敗しても、それは致命的なエラーにはならず、メモリ内の写像(およびそれに基づくデバッグ動作)は何ら影響を受けない。サイドカーが必要になるのは、生成された .lua とソースマップの対応をディスク上で確認したい、別のツールでマップを読みたい、といった場合に限られる。
注意
.pasta 行で実行が停止しているあいだは、ホスト(SHIORI / SSP)への応答が止まる。これは既知かつ意図された構造的な挙動であり、不具合ではない。停止を長く保つと SSP がタイムアウトする恐れがあるため、その理由と回避運用は 構造的制約と緩和策 で確認しておくこと。
これで、.pasta の座標を自在に行き来する術はあなたのものですわ。フンッ、別にあなたの覚えが良いから褒めているわけではございませんからね。
止まる位置を見極め、変数を見透かし、必要なら .lua の素顔まで覗く――そこまでできれば、もうたいていのバグは観念いたしますわ。さあ、胸を張って参りましょう!
構造的制約と緩和策
ごきげんよう。デバッグ中にゴーストが押し黙るのには、れっきとした理由がございます。慌てず騒がず、その仕組みと付き合い方を心得ておきましょう。これを知っているか否かで、デバッグ作業の落ち着きがまるで違ってまいりますわよ。
このページでは、ブレーク中にホスト応答が停止する構造的制約と、SSP のタイムアウトを避けるための運用上の緩和策を扱う。有効化や接続の手順は デバッグ概要 と VSCode 接続手順 を、.pasta 座標での停止・変数確認などの操作は .pasta ソースレベル操作 を参照する。
ブレーク中はホスト応答が停止する
ブレークポイントで実行が止まっている間、ゴーストが「固まった」「無反応になった」ように見えることがある。これは既知かつ意図された構造的な挙動であり、不具合ではない。 デバッグ中にゴーストが沈黙しても、まず正常な状態だと理解してよい。
その仕組みは次のとおりである。pasta の SHIORI リクエスト処理は Arc<Mutex> により直列化され、各リクエストは VM スレッド上でブロッキングに実行される。ブレークポイントで実行が停止している間、VM ホストスレッドは復帰せず、SHIORI リクエストの Mutex を握り続けたままになる。結果として、停止中はホスト(SHIORI / SSP)に対する応答全体が、実行を再開(continue)するまで保留される。
後続のリクエストもすべて待機する
この制約は、いま処理中のリクエストの応答が保留されるだけにとどまらない。Mutex による直列化のため、ブレークで停止している間は、後続のすべての SHIORI リクエストも実行再開(continue)まで待機列に積まれる。
したがって、ブレークを長く保持するほど、再開後に滞留していたリクエストがまとめて処理され、再開直後の挙動が一気に流れる点にも留意する。停止中にホストが無反応に見えるのは、現在のリクエストと後続リクエストの双方が、再開を待っている状態だからである。
SSP タイムアウトを避ける緩和策
ブレークを長く保持すると、SSP 側がリクエストのタイムアウト(「応答なし」表示など)を起こす可能性がある。後述のとおり構造的制約そのものを根本解決することはできないため、ブレークの保持時間を短く保つ運用で対処する。具体的には次の点に注意する。
- デバッグ専用の起動・プロファイルを用意する。 本番運用中のゴーストにそのままアタッチして長時間ブレークするのではなく、デバッグ用のラウンチ構成・プロファイルで作業し、影響範囲を限定する。
- ブレークは短く保つ。 必要な変数・コールスタックを確認したら、速やかに continue(実行再開)して動作を続行する。停止したまま放置しない。
- 時間に敏感なイベント処理中のブレークを避ける。 時報・タイマー・連続的な定期イベントなど、ホストが短い間隔で応答を期待する処理の最中にブレークすると、タイムアウトを誘発しやすい。確認したいロジックは、可能なら時間制約の緩いイベントで再現してブレークする。
- 長時間の停止は「応答なし」を招くと理解しておく。 ステップ実行や変数確認に時間がかかりそうな場合は、停止箇所を絞る・確認項目を事前に決めておくなどして、1 回の停止を短くまとめる。
根本解決はスコープ外
ブレーク中のホスト応答停止(および結果としての SSP タイムアウト)の根本解決は、本マニュアルの対象外である。本章が提供するのは上記の緩和策のみであり、構造的制約そのものを取り除くものではない。
根本的に解消するには、ホスト側を非同期化するアーキテクチャ(ブレーク中でもホストへ即時応答を返し、リクエスト処理と停止制御を分離する設計)が必要となる。これは別途の取り組みとして見送られており、現時点では本章の緩和策に従ってブレーク保持時間を短く保つことで対処する。
仕組みさえ呑み込んでしまえば、もう何も恐ろしくはございませんわね。フンッ、別にあなたが優秀だなんて言っていませんわよ。 止めたら速やかに続行する――この一点さえ守れば、ゴーストはちゃんと息を吹き返しますわ。さあ、自信を持って参りましょう!
接続できないとき(トラブルシューティング)
ごきげんよう。「マニュアルどおりにしたのに繋がらない」——デバッグで一番心が折れる瞬間ですわね。けれど落ち着きなさいまし。原因はたいてい一本道で切り分けられますの。わたくしと一緒に、慌てず順に確かめていきましょう。
このページでは、アタッチがうまくいかないとき/うまくいったか分からないときの確認手順を扱う。デバッグの全体像と有効化は デバッグ概要、接続設定は VSCode 接続と拡張導入、ブレーク中の挙動は 構造的制約と緩和策 を参照する。
まず切り分ける: アプリ側か VSCode 側か
問題を「pasta(アプリ)側」と「VSCode(接続)側」のどちらかに二分するのが最短の近道である。判定の鍵は デバッグバックエンドがポートを開いて待ち受けているか の一点である。
手順 1: バックエンドが待ち受けているか(OS 側で確認)
デバッグを有効化してゴーストを起動した状態で、PowerShell で待ち受けポートを確認する。
Get-NetTCPConnection -LocalPort 9276 -State Listen
- 行が返る(
Listen状態がある)→ バックエンドは正常に待ち受けている。問題は VSCode 側。手順 2 へ進む。 - 何も返らない → バックエンドが待ち受けていない。問題はアプリ側。次を確認する。
- デバッグが有効化されているか(
pasta.tomlの[debug] enabled = true、または環境変数PASTA_DEBUG)。有効化の詳細は デバッグ概要 を参照する。 - 環境変数で有効化する場合、その環境変数がベースウェア(SSP)のプロセスに渡っているか。別のシェルで設定しただけでは、SSP から起動したゴーストには伝わらないことがある。確実なのは
pasta.tomlでの有効化である。 - ゴースト(pasta.dll を読み込むベースウェア)が実際に起動しているか。
- ポートを既定から変更している場合、有効化側のポートと待ち受け確認に使うポート番号が一致しているか。
- デバッグが有効化されているか(
ポートを変更している場合は、上記コマンドの 9276 を実際のポート番号に置き換える。
手順 2: アタッチできているか(VSCode 側で確認)
バックエンドが待ち受けているのに「繋がった気がしない」場合、実際にはアタッチが成立していることが多い。次のサインで確認する。
| 確認場所 | アタッチ成立時のサイン |
|---|---|
実行とデバッグビュー(Ctrl+Shift+D)のコールスタック | デバッグ構成名(例 Attach to Pasta Debug Backend)のセッションが表示される |
| 画面上部 | デバッグツールバー(続行・ステップ・停止)が現れる |
| 画面下端のステータスバー | デバッグ中の色(既定テーマではオレンジ)に変わる |
OS 側からも確認できる。アタッチ中は待ち受けポートに確立済み接続が現れる。
Get-NetTCPConnection -LocalPort 9276 -State Established
行が返れば、クライアント(VSCode)が接続済みである。
アタッチしてもゴーストは固まらない(正常)
最も誤解しやすい点を明示する。アタッチしただけではゴーストは停止せず、通常どおり動作し続ける。 これは不具合ではない。実行が止まるのは、設定したブレークポイントの行が実際に実行された瞬間だけである。したがって「F5 を押してもゴーストが固まらない=失敗」と判断するのは誤りである。接続の成否は、ゴーストの見た目ではなく上記のコールスタック表示や確立済み接続で判断する。
ブレーク中にゴーストが固まること自体の仕組みと注意は 構造的制約と緩和策 を参照する。
代表的な失敗症状と原因
| 症状 | 主な原因 | 対処 |
|---|---|---|
デバッグ種別 pasta は未対応、というエラーが出る | pasta VSCode 拡張が未導入、または拡張の更新後に VSCode を再読込していない | 拡張を導入し、Ctrl+Shift+P →「Developer: Reload Window」で再読込する |
| 構成を選べない・一覧に出ない | launch.json に pasta のアタッチ構成が無い、または必須フィールドが欠けている | 下記の完全な構成例をそのまま使う |
| 接続拒否(ECONNREFUSED)・接続がすぐ切れる | バックエンドが待ち受けていない、またはポート不一致 | 手順 1 を確認し、有効化側と launch.json のポートをそろえる |
| 接続できたが何も起きない | 正常。ブレークポイント未設定、または実行されない行に設定している | 実行される .pasta 行にブレークポイントを置き、その行が動くイベントを起こす |
確実につながる launch.json(コピーして使う)
必須フィールドが欠けると接続できない。次の構成をそのまま .vscode/launch.json に使う。各フィールドの意味は VSCode 接続と拡張導入 を参照する。
{
"version": "0.2.0",
"configurations": [
{
"type": "pasta",
"request": "attach",
"name": "Attach to Pasta Debug Backend",
"host": "127.0.0.1",
"port": 9276,
"sourcePresentation": "pasta"
}
]
}
ね、順番に潰していけば、必ず原因にたどり着けますの。フンッ、別にあなたが投げ出さなかったことを偉いだなんて言っていませんわよ。……でも、つまずいた経験は何よりの財産ですわ。次は自信を持って、堂々とブレークさせてやりましょう。さあ、参りますわよ!
外部リンク集
ごきげんよう。わたくし Claudia が、本書の外で頼りになる「権威ある書庫」への道筋をご案内いたしますわ。 迷子になっても大丈夫。困ったときに開くべき扉は、ここにすべてまとめてございますの。 さあ、心強い味方を手元に揃えて参りましょう。
本マニュアルは利用者向けの平易な派生物であり、厳密な定義や言語仕様そのものを再録しない。 判断に迷ったとき、あるいは本書の記述だけでは足りないときは、本章のリンクから一次資料に当たること。
リンクはすべて絶対 URL で記載する。本書の外部(GitHub・外部サイト)を指すため、相対パスでは到達できない。
Lua 言語リファレンス
Pasta のランタイムは LuaJIT 2.1 であり、言語仕様としては Lua 5.1 系に独自拡張を加えたものに相当する。 したがって素の Lua 言語仕様を参照する場合は、この方言に対応する資料を用いる。本書は言語仕様本体を取り込まず、以下のリンク参照のみを案内する。
日本語リファレンス(milkpot 版)
日本語で Lua 言語を確認したい場合は、milkpot 版のマニュアル和訳が読みやすい。ランタイム方言(Lua 5.1 系)に近い版を参照すること。
| 版 | 用途 | リンク |
|---|---|---|
| Lua 5.2 リファレンスマニュアル(日本語訳) | 5.1 から拡張された挙動の確認・索引用途 | http://milkpot.sakura.ne.jp/lua/lua52_manual_ja.html |
| Lua 5.1 リファレンスマニュアル(日本語訳) | ランタイム方言(Lua 5.1 系)の基準 | http://milkpot.sakura.ne.jp/lua/lua51_manual_ja.html |
LuaJIT 公式ドキュメント(英語)
LuaJIT 固有の拡張機能・標準ライブラリの差分・実装上の注意は、公式サイトが一次情報である。英語だが、ランタイムの実体に最も忠実な資料となる。
| 対象 | 用途 | リンク |
|---|---|---|
| LuaJIT 公式トップ | LuaJIT 全体の概要・各種ドキュメントの入口 | https://luajit.org/ |
| LuaJIT 拡張機能 | Lua 5.1 からの拡張(5.2/5.3 由来の機能取り込みなど)の一覧 | https://luajit.org/extensions.html |
不採用とする資料
| 資料 | 採否 | 理由 |
|---|---|---|
| Lua 5.5 系リファレンス(lua55-manual-ja 等) | 言語リファレンスとして不採用 | ランタイム(LuaJIT 2.1 = Lua 5.1 系+拡張)と方言が離れており、構文・標準ライブラリの差異が利用者の誤読を招くため |
Lua 5.5 系の資料は、版が離れているため本書の言語リファレンスとしては案内しない。 最新版の知識は LuaJIT 2.1 上では動作しない構文・関数を含むため、参照先として混在させないこと。
doc/spec — Pasta の権威的仕様
Pasta DSL の文法・処理系の挙動について、最終的な正しさの基準となるのは doc/spec/ である。
本マニュアルはこの仕様の利用者向け派生物であり、doc/spec/ を置き換えるものではない。記述が食い違う場合は doc/spec/ を正とする。
| 対象 | 用途 | リンク |
|---|---|---|
| doc/spec ディレクトリ(全体) | Pasta 仕様の入口・章一覧 | https://github.com/ekicyou/pasta/tree/main/doc/spec |
| 文法モデル | 字句・構文の全体像 | https://github.com/ekicyou/pasta/blob/main/doc/spec/01-grammar-model.md |
| マーカー一覧 | 全マーカー・演算子・区切り文字 | https://github.com/ekicyou/pasta/blob/main/doc/spec/02-markers.md |
| ブロック構造 | 行種別・グローバル/ローカルブロック・インデント | https://github.com/ekicyou/pasta/blob/main/doc/spec/03-block-structure.md |
| Call 仕様 | シーン呼び出し・前方一致・スコープ解決 | https://github.com/ekicyou/pasta/blob/main/doc/spec/04-call-spec.md |
| 変数 | 変数・スコープの厳密仕様 | https://github.com/ekicyou/pasta/blob/main/doc/spec/09-variables.md |
| 単語定義 | 単語・スコープ解決の共通仕様 | https://github.com/ekicyou/pasta/blob/main/doc/spec/10-words.md |
各文法章の末尾にも、対応する doc/spec/ 章への「権威的仕様」リンクを用意している。より深い定義が必要な場合は、そちらの導線も活用すること。
これで、もう怖いものはございませんわね。本書で道に迷っても、ここに戻れば一次資料へ一直線。 ……フンッ、別にあなたを心配しているわけではございませんわよ。 さあ、確かな足場を得たのですから、胸を張って制作へ戻りましょう!