Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

実行時の失敗の出力

クローディア(考え中)

値の無い変数、どこにも無い単語、登録していないアクター……書き間違えたまま動かすと、ゴーストは空のまま話を続けてしまいますの。どこで間違えたのか、見ているだけでは分かりにくかったのですわ。

アンソニー(素)

ログを開かなければ気づけない、というのは心細いものでございます。

クローディア(したり顔)

ですから pasta は、間違えたその場所に、バルーンの中で 【…】 の印を出しますの。この頁で、印の読み方をすべてお教えいたしますわ。


このページは、シーンの実行中に起きる辞書の書き間違い(実行時の失敗)について、バルーンに出る失敗表記とログの警告、表記が出る位置、重ねて出さない規則、表記を出すかどうかの設定、Lua から失敗を報告する方法を示す。Lua のエラーでシーンや要求そのものが止まる場合(止まるエラー)の表記も、止まるエラーの表記で示す。

実行時の失敗では、シーンは止まらずに続く。失敗した後の続き方(値の無い変数や見つからない単語・関数は空として続ける、見つからない Call は飛ばして次の行へ進む、数や文字列にできない被演算子は 0・空文字列として計算する)は、失敗表記を出すかどうかにかかわらず同じである。失敗表記の文字列は、変数への代入・関数の引数・Call の引数・算術・連結の値には混ざらない。

止まるエラーでは、シーンや要求はそこで止まり、続かない。失敗表記の形と切り替えは実行時の失敗と同じものを使い、止まった理由を表記でバルーンに出す(止まるエラーの表記)。起動・読み込み時の失敗はバルーンに出ない。その読み方は 起動シーケンスとモジュール解決 を参照する。

失敗表記の形

失敗表記は、どの種類も同じ形で出る。

【何が起きたか:対象】
【何が起きたか:対象(説明)】
  • 何が起きたか は失敗の種類の名前である(次節の表)。
  • 対象 は、失敗した参照を辞書の書き方で示す。変数はローカル変数が $名前、グローバル変数が $*名前、シーン引数が $0・$1…、関数は @名前()・@*名前()・@$名前()、シーンは >名前 になる。関数の引数は表記に含まれない(@計算(1) は @計算() と出る)。
  • 説明 は値の状態であり、要るときだけ対象の後に (…) で付く。
  • 対象が分からないときは、説明が対象の位置に出る(例: 【数にできない値:"xy"】)。
  • 実行時の失敗の表記に辞書の行番号は含まれない。対象の名前と、バルーンの中で表記が出た位置(前後の台詞)が手がかりになる(止まるエラーの表記は、説明のエラー文に止まったファイルと行が入る。止まった場所とエラー文)。

名前や値に、さくらスクリプトのタグとして働く文字が含まれていても、タグとしては実行されず、書いた文字のとおりに表示される。表記の中身では、\ が \\、% が \% に置き換わり、改行(CR・LF)は空白になる。

種類ごとの表記とログの警告

1 件の失敗について、バルーンに失敗表記が 1 つ、ログに警告(warn レベル)が 1 行出る。次の表の辞書の例は、どれもシーンの中の行である。

何が起きたか辞書の例失敗表記ログの警告
値の無い変数さくら:今日は$話題。の話($話題 に値が無い)【値の無い変数:$話題】act:talk - undefined variable: 'var.話題'
単語が見つからないさくら:前@挨拶。後【単語が見つからない:@挨拶】proxy:word - handler not found: key='挨拶', mode='word', via=proxy(さくら)
単語が見つからない$w=@挨拶【単語が見つからない:@挨拶】act:word - handler not found: key='挨拶', mode='word', via=act
関数が見つからないさくら:前@計算(1)後【関数が見つからない:@計算()】proxy:expr_fn - handler not found: key='計算', mode='expr', via=proxy(さくら)
関数が見つからない$f=@計算(1)【関数が見つからない:@計算()】act:expr_fn - handler not found: key='計算', mode='expr', via=act
関数が見つからないさくら:前@*計算(1)後【関数が見つからない:@*計算()】act:global_fn - function not found: key='計算'
名前にならない値さくら:前@$未代入。後($未代入 に値が無い)【名前にならない値:$未代入(値なし)】proxy:word - undefined variable: 'var.未代入'
名前にならない値さくら:@$空()($空 が空文字列)【名前にならない値:$空(空文字列)】proxy:expr_fn - empty variable: 'var.空'
名前にならない値$d=@$真($真 が真偽値)【名前にならない値:$真(真偽値)】act:word - unsupported value type: 'var.真' (boolean)
名前にならない値>$行き先($行き先 に値が無い)【名前にならない値:$行き先(値なし)】act:call - undefined variable: 'var.行き先'
名前にならない値>@真偽()(関数が真偽値を返す)【名前にならない値:@真偽()(真偽値)】act:call - key is not a string or number: operand='@真偽()', value=true (boolean)
未登録アクターぱすたー:こんにちは(ぱすたー が登録されていない)【未登録アクター:ぱすたー】act:actor_proxy - unregistered actor: name='ぱすたー'
数にできない値$y=$x+1($x が 「abc」)【数にできない値:$x("abc")】act:arith - operand is not a number: op='+', operand='var.x', value='abc' (string)
数にできない値$z=「xy」*2【数にできない値:"xy"】act:arith - operand is not a number: op='*', value='xy' (string)
文字列にできない値$c=$t&「後」($t が真偽値の真)【文字列にできない値:$t(true)】act:concat - operand is not a string or number: op='&', operand='var.t', value=true (boolean)
文字列にできない値$c2=「前」&@真偽()【文字列にできない値:@真偽()(true)】act:concat - operand is not a string or number: op='&', operand='@真偽()', value=true (boolean)
シーンが見つからない>会話2【シーンが見つからない:>会話2】act:call - handler not found: key='会話2', mode='scene', via=act
スクリプトのエラー*会話 の ・雨の日 から呼んだ Lua のブロックの return data.雨(dic\天気.pasta の 5 行目。data が nil。止まった場所とエラー文の例)【スクリプトのエラー:*会話・雨の日(dic\天気.pasta:5: attempt to index local 'data' (a nil value))】スクリプトのエラー:*会話・雨の日(dic\天気.pasta:5: attempt to index local 'data' (a nil value))
時間切れ$幅=$%baseware.version(結果が 5 秒以内に届かない)【時間切れ:callback timeout: get_property】OnPastaCallBack1: callback timeout: get_property

各種類の対象になる状況は次のとおりである。

  • 値の無い変数: アクション行で、値の無い変数(未代入の変数・値なしを代入した変数・渡されていないシーン引数)を展開した。グローバル変数は $*名前(警告では save.名前)、シーン引数は $0(警告では args[1])と出る。
  • 単語が見つからない: 単語参照 @単語 の単語が見つからない。警告の接頭辞は、アクター付きの行では proxy:word、それ以外では act:word になる。
  • 関数が見つからない: 関数呼び出し @名前(…) の関数、またはグローバル関数の呼び出し @*名前(…) の関数が見つからない。
  • 名前にならない値: 動的参照(@$名前・@$名前(…))または動的コール(>$名前・>@名前(…))の値が、検索に使う名前にならない(値が無い・空文字列・文字列と数値以外)。説明は、値が無ければ 値なし、空文字列なら 空文字列、真偽値なら 真偽値、表なら 表、関数なら 関数、それ以外は Lua の型の名前になる。警告の接頭辞は参照の場所により act:word・proxy:word・act:expr_fn・proxy:expr_fn・act:call のいずれかになる。
  • 未登録アクター: 登録されていないアクター名のアクション行を実行した。目印は話者がその名前に切り替わるたびに 1 回出る(「重ねて出さない規則」)。
  • 数にできない値・文字列にできない値: 算術の被演算子が数にできない値、または連結の被演算子が文字列にできない値だった(値が無い場合を除く)。説明は、文字列なら "abc" のように引用符付き、真偽値なら true・false、それ以外は「名前にならない値」と同じ型の説明になる。
  • シーンが見つからない: Call・Jump のターゲットのシーンが見つからない。シーン名の別名(シーン名の別名)で置き換えた名前が見つからないときも、表記には書いた名前が出る。このときログには、上の警告に加えて、書いた名前と置き換え後の名前を含む行(Scene not found (alias applied))が出る。
  • スクリプトのエラー・時間切れ: Lua のエラー、または待ち合わせの時間切れで、シーンや要求が止まった(止まるエラー)。表記の中身と出る応答は 止まるエラーの表記 で示す。

次の場合は失敗として扱わず、表記も警告も出さない。

  • 算術・連結の被演算子に値が無い(0・空文字列として計算する)。
  • 静的な単語参照の名前が空である。

出る位置

失敗表記は、失敗が起きた時点の出力の位置に出る。

  • 発言の途中で失敗したときは、その発言の中の失敗した位置(前の文字の後、後の文字の前)に出る。さくら:前@挨拶。後 は 前【単語が見つからない:@挨拶】。後 になる。
  • アクション行以外の行(変数の代入行・Call 行の引数・式の中・Lua ブロックから呼んだ act のメソッド)で失敗したときも、その時点までに出力した内容の後に出る。
  • 失敗表記は、直前に話したアクターのバルーンに続けて出る。失敗表記のためにアクターは切り替わらない。そのため、別のアクターの行の先頭で起きた失敗は、直前の話者のバルーンに出る。
  • 出力の先頭(最初の発言より前。チェイントークで再開した直後を含む)で失敗したときは、さくらスクリプトのスコープを切り替えずに出る。
  • チェイントーク(チェイントーク)で中断した後、再開してから起きた失敗は、再開した後の出力に出る。
  • 未登録アクターの目印だけは例外で、そのアクターの発言の先頭(スコープの切り替えの直後)に出る。

次のシーンを、さくら の立ち位置が 0、うにゅう の立ち位置が 1 で実行した例を示す。@無い単語 は見つからず、$未定義 には値が無い。

*例
 さくら:@無い単語、です
 うにゅう:$未定義、だね
【単語が見つからない:@無い単語】\p[0]、\_w[450]です【値の無い変数:$未定義】\p[1]、\_w[450]だね\e

1 つ目の表記は出力の先頭なのでスコープを切り替える前に出る。2 つ目の表記は うにゅう の行の先頭で起きたが、直前の話者 さくら のバルーンに続けて出る。

登録されていない ぱすたー の行(立ち位置は 0 になる)の先頭で単語が見つからない場合は、目印の後に表記が出る。

*例
 ぱすたー:@無い単語、だよ
\p[0]【未登録アクター:ぱすたー】【単語が見つからない:@無い単語】、\_w[450]だよ\e

失敗表記には、台詞と違って句読点のウェイトも自動の改行も入らない。失敗表記を挟んでも、前後の台詞のウェイト・自動の改行・段落区切りの改行・アクターの切り替えは、失敗が無い場合と同じに出る。ただし、別のアクターからスコープを戻った直後の発言が失敗表記で始まる場合は、段落区切りの改行が失敗の無い場合と変わることがある。

重ねて出さない規則

1 つの原因に対して、失敗表記は 1 つ、ログの警告は 1 行だけ出る。内側の参照が失敗を報告したとき、その結果の値の無さを受け取った外側は、失敗を重ねて報告しない。

内側(報告する)外側(報告しない)例
単語・関数・グローバル関数が見つからないアクション行の展開さくら:@挨拶 は 【単語が見つからない:@挨拶】 だけ
動的参照の値が名前にならない単語・関数の検索さくら:@$未代入 は 【名前にならない値:$未代入(値なし)】 だけ
動的コールの変数の値が名前にならないCall の検索>$行き先 は 【名前にならない値:$行き先(値なし)】 だけ
動的コールの関数が見つからない動的コールの値の判定>@無い関数() は 【関数が見つからない:@無い関数()】 だけ
被演算子の関数が見つからない算術・連結$a=@無い関数()+1 は 【関数が見つからない:@無い関数()】 だけ
未登録アクター立ち位置の警告ログに actor_spots fallback の行は出ない
  • 未登録アクターの行(継続行を含む)が続くときは、目印と警告は話者がその名前に切り替わったときに 1 回だけ出る。間に別のアクターの行を挟んで戻ると、もう一度出る。
  • 同じ書き間違いが別の行・別の回で起きたときは、それぞれの回で報告する。一度報告した名前を以後は黙る、という抑止はしない。
  • 値なしを代入した変数を後で展開した場合は、代入の行と展開の行で別々に報告する。$x=@挨拶 の後の さくら:$x は、【単語が見つからない:@挨拶】 と 【値の無い変数:$x】 の 2 つになる。

表記だけの出力

シーンの出力が失敗表記だけになったときも、空の出力としては扱わず、失敗表記を含む出力を返す。たとえば登録されていない ぱすたー の ぱすたー:$未定義 だけのシーンは、【未登録アクター:ぱすたー】【値の無い変数:$未定義】 を含む出力になる。

失敗表記を出さない設定(次節)では、表記だけだった出力は何も出さないシーンと同じ扱い(SHIORI の 204 応答)になる。

表記の切り替え

pasta.toml の [logging] の balloon_warning(真偽値)で、失敗表記をバルーンに出すかどうかを切り替える。キーの一覧は [logging](ログ出力) を参照する。

[logging]
balloon_warning = false
  • 書かなければ true(失敗表記を出す)である。
  • false のときは、未登録アクターの目印とシーンが見つからない場合の表記を含め、すべての失敗表記を出さない。バルーンの出力は、true のときの出力から失敗表記を除いたものと同じになる。
  • 止まるエラーの表記(止まるエラーの表記)も、この設定で切り替わる。false のときの止まるエラーの応答は、これまでと同じ(500 と X-Error-Reason。出る応答)になる。
  • ログの警告は、この設定にかかわらず出る。ログに記録するかどうかは、これまでどおり level・filter が決める。level = "error" で警告がログに残らない設定でも、失敗表記は balloon_warning だけで決まる。
  • 値が真偽値でないときは、書かなかった場合と同じ true になる。[logging] の他のキー(file_path・level・filter)はこの値の影響を受けない。
  • pasta_check(release を含む)の動作は、この設定によって変わらない。

既定の true のままでは、配ったゴーストでも、利用者のバルーンに失敗表記と止まるエラーの表記が出る。ゴーストを配る前に balloon_warning = false を書く。既定値は変わらず、pasta_check release もこの行を書き足さない。

止まるエラーの表記

GET の要求の処理中に Lua のエラーが起きて、その要求の処理が続けられなくなったとき(止まるエラー)も、その要求の応答で、止まった理由を失敗表記としてバルーンに出す。次の場所で起きたエラーが対象で、どこで起きても表記の形は同じである。

  • イベントのハンドラ(REG に登録した関数・既定のハンドラ)
  • イベントで始めたシーン、チェイントークで再開したシーン
  • プロパティの待ち合わせ(act:get_property)から再開したシーン
  • 待ち合わせの時間切れ(時間切れ)

止まったシーンは再開されず、チェイントークの続きと待ち合わせは捨てられる。表記を出した後の要求は、エラーが無かったときと同じに処理される。

2 つの種類

何が起きたか失敗表記出るとき
スクリプトのエラー【スクリプトのエラー:止まった場所(エラー文)】Lua のエラーで止まった
スクリプトのエラー【スクリプトのエラー】エラーの値が文字列でない(error({}) など)
時間切れ【時間切れ:理由】act:get_property の待ち合わせが時間切れになった。理由は timeout_message(既定は callback timeout: get_property)

形と、さくらスクリプトのタグとして働く文字の扱いは、実行時の失敗と同じである(失敗表記の形)。エラー文の中の \(Windows のパスの区切り)や % も、書いた文字のとおりに表示される。1 件の止まるエラーにつき、表記は 1 つ出る。

止まった場所とエラー文

止まった場所(表記の対象)は次のように示す。

  • シーンの中で止まったときは、そのシーンを辞書に書いた名前で示す。グローバルシーンは *名前、ローカルシーンの中なら *グローバルシーン名・ローカルシーン名 になる。シーンから呼んだ Lua のブロックの関数や scripts/ の関数の中で止まったときも、呼んだシーンの名前になる。
  • 別名(シーン名の別名)で登録したシーンも、書いた名前で出る。*会話 は OnTalk ではなく *会話 と出る。
  • 同じ名前のシーンが複数あるときは、書いた名前だけを示し、どの 1 つかは区別しない。区別したいときは、シーンに別の名前を付ける(ランダムトークのまま分けるなら、*OnTalk_新しいおもちゃ のように書く)。
  • シーンの外(REG に登録したハンドラ・既定のハンドラ)で止まったときは、要求のイベントの ID(OnBoot など)で示す。

エラー文(表記の説明)は、Lua のエラー文の最初の行である。2 行目以降とスタックトレースは出さない。エラー文の先頭の場所は、次のように直して出す。ファイルは、ゴーストのフォルダーから見た形になる。

止まった所表記に出る場所
辞書の中(アクション行・Lua のブロック).pasta のファイルと行(dic\天気.pasta:5:)
ゴーストのフォルダーの中の、作者が置いた .lua(scripts/ など)そのファイルと行(scripts\天気.lua:6:)
辞書から変換した Lua・pasta の標準ランタイム(pasta_scripts/)・pasta の内部でエラーを投げ直した場所出さない

辞書の中で止まっても、.pasta の行に直せないとき(別のグローバルシーンや別のファイルの Lua のブロックの関数を呼んで、その中で止まったときなど)は、場所を出さずにエラー文だけを出す。error("…", 0) のように場所の付かないエラー文は、そのまま出る。

次の辞書 dic/天気.pasta を、ランダムトーク(*会話)として実行した例を示す。

*会話
```lua
function SCENE.雨を調べる(act)
    local data = nil
    return data.雨
end
```
 >雨の日

 ・雨の日
  さくら:雨の日は……
  さくら:@雨を調べる()

5 行目の return data.雨 で止まり、バルーンには途中までの台詞の後に次の表記が出る。

【スクリプトのエラー:*会話・雨の日(dic\天気.pasta:5: attempt to index local 'data' (a nil value))】

Lua のブロックの代わりに、scripts/天気.lua の関数を呼んで、その 6 行目で止まったときは、scripts\天気.lua:6: が出る。

【スクリプトのエラー:*会話・雨の日(scripts\天気.lua:6: attempt to index local 'forecast' (a nil value))】

scripts/main.lua で REG.OnBoot に登録したハンドラが、7 行目の error("設定が読めない") で止まったときは、イベントの ID が出る。

【スクリプトのエラー:OnBoot(scripts\main.lua:7: 設定が読めない)】

途中の台詞

シーンやハンドラが、エラーが起きるまでに積んだ台詞は捨てずに出し、その後に表記を出す。表記の前に出た台詞が、辞書のどこまで進んで止まったかの手がかりになる。表記を出さない設定では、途中の台詞もこれまでどおり出ない。表記は、実行時の失敗と同じく、直前に話したアクターのバルーンに続けて出る。

出る応答

設定ステータス応答の値(Value)応答の理由の行(X-Error-Reason)
表記を出す(balloon_warning = true・書かない)200 OK途中の台詞と表記表記を出さないときと同じ
表記を出さない(balloon_warning = false)500 Internal Server Error無しこれまでと同じ(エラー文の最初の行)
  • 表記を出すときのステータスが 200 なのは、SSP が 500 の応答の値をバルーンに出さないためである(SSP 2.9.18 で確かめた)。失敗の応答であることは、X-Error-Reason の行で示す。
  • 応答の理由の行の中身は、表記を出すかどうかで変わらない。表記と違い、場所は直さずに Lua のエラー文の最初の行のまま入る。

次の場合は、設定にかかわらず表記を出さず、これまでと同じ応答になる。

  • ID が On で始まらない GET の要求(version・name・menu.… などのリソースの要求)で止まった。応答の値が台詞として表示されないため、500 と X-Error-Reason を返す。
  • 通知(NOTIFY)の要求で止まった。何も表示しない。
  • 作者が RES.err(理由) で作った応答を返した。Lua のエラーではないので止まるエラーにならず、その応答がそのまま返る(RES)。
  • ゴーストの読み込みに失敗しているなど、要求が Lua の処理に届く前に 500 になった(ゴーストが起動しない・喋らないとき)。

時間切れ

act:get_property の待ち合わせが時間切れになると、待っていたシーンは timeout_message を理由とするエラーで止まる(OnPastaCallBack)。

  • 表記を出す設定では、その回の OnSecondChange の応答で 【時間切れ:callback timeout: get_property】(timeout_message を渡したときはその文字列)を出す。X-Error-Reason は timeout_message の文字列そのものである。
  • timeout_message に文字列でない値(false など。省いたときは既定の文字列になる)を渡した待ち合わせは、時間切れでもエラーにならず、そのままシーンを再開する。再開したシーンがエラーで止まったときは、その回の OnSecondChange の応答で 【スクリプトのエラー:…】 を出す。表記を出さない設定では、これまでどおり応答を作らない。
  • 時間切れが応答を生んだ回は、その回の OnTalk・OnHour は発行されず、次の回以降に回る。
  • 1 回の OnSecondChange で複数の待ち合わせが時間切れになったときは、これまでどおり最初の応答だけを使う。表記を持つ応答も同じに扱う。

ログの警告

止まるエラー 1 件につき、ログに警告(warn レベル)が 1 行出る。表記を出す設定かどうか、ID が On で始まるかどうか、GET か NOTIFY かにかかわらず出る。中身は表記と同じもの(種類・止まった場所・直したエラー文)から、【 】 の枠を除いたものである。タグとして働く文字の置き換えはせず、辞書から変換した Lua の場所も出ない。

スクリプトのエラー:*会話・雨の日(dic\天気.pasta:5: attempt to index local 'data' (a nil value))
  • timeout_message を持つ時間切れでは、再開の前に出る警告(OnPastaCallBack1: callback timeout: get_property のように、待ち合わせのイベント名と理由)がその 1 行になる。
  • timeout_message の無い時間切れで再開したシーンが止まったときは、待ち合わせのイベント名を前に付けて出る(OnPastaCallBack1: スクリプトのエラー:…)。

繰り返すエラー

同じ書き間違いによるエラーが、別の要求で繰り返し起きたときは、それぞれの回で表記を出す。一度出したエラーを以後は黙る、という抑止はしない。たとえば OnSecondChange のハンドラで止まり続けると、毎秒の応答に表記が出る。直せば消え、balloon_warning = false でも消せる。

Lua から失敗を報告する

act:failure

Lua のコード(シーン内の Lua ブロック・scripts/ のスクリプト)からは、実行中の act の failure(text, warning) で、辞書の失敗と同じ出口から失敗を報告できる(failure(text, warning))。

function SCENE.直接失敗(act)
    act:failure("Lua失敗:テスト", "test - direct failure")
end
  • text は失敗表記の中身である。【 】 はこの関数が付けるので、上の例は 【Lua失敗:テスト】 になる。
  • text も他の失敗表記と同じく置き換えられ、タグとして働く文字は文字のとおりに表示される。
  • warning を渡すと、その文字列がログに警告(warn レベル)として 1 行出る。nil ならログには出さない。
  • 表記は、呼んだ時点の出力の位置に出て、[logging] balloon_warning の設定に従う。

実行中の act の制限

PASTA.num・PASTA.str(PASTA.num(op, v, desc))は act を引数に取らないため、失敗表記を実行中の act に積む。実行中の act は、同じコルーチン(スレッド)で最後にシーンを始めた(init_scene を呼んだ)act である。辞書から生成したコードでは、チェイントークで再開した後を含めて、これは常にそのシーンの act になる。

Lua から直接呼ぶ場合は、次の制限がある。

  • 同じコルーチンの中で、Lua のコードが別の act でシーン関数を呼ぶと、その関数から戻った後も、実行中の act は内側の act のままになる。その後に PASTA.num・PASTA.str で起きた失敗の表記は、内側の act に積まれる。
  • シーンを始めていないコルーチンの中で呼ぶと、ログの警告だけが出て、失敗表記は出ない。
  • コルーチンの外(メインスレッド)で呼ぶと、失敗表記は、そこで最後に init_scene を呼んだ act に積まれる。この対応は、次に init_scene を呼ぶと上書きされる。
  • Lua から act:call を値の無いキーで呼ぶと、【名前にならない値:値なし】 が出て、ログに act:call - nil key (undefined variable?), skipping scene search が出る。
  • WORD.dynamic_key を act を渡さずに呼んだときは、ログの警告だけが出て、失敗表記は出ない(WORD.dynamic_key(value, var_path, via, act))。

クローディア(にっこり)

【…】 が出たら、慌てずにこの頁の表と見比べなさいまし。何をどこで間違えたのか、すぐに見当がつきますわ。

アンソニー(素)

配る前には balloon_warning のこともお忘れなく。利用者の方の画面に、開発の跡を残さずに済みます。