「もし、ぐにゅうがやわらかかったら」…えーと、どっちだっけ。 …はぁ? あれ? 「かたかったら」の間違いかな?
本章では、華和梨の独自簡易言語「KIS(Kawari Inline Script/華和梨インラインスクリプト)」の使い方を解説します。
インラインスクリプトとは、華和梨のデータ中に各種のコマンド(辞書の追加や条件分岐、データ取得など)を埋め込み実行させ、その結果をデータに反映させる仕組みです。インラインスクリプトを使うと、発想と応用次第でいろーんなことができます。
インラインスクリプトは、データ内に"$("〜")"で囲われたスクリプトとして書き込みます。
エントリ名(sentenceなども含む) : ……$(インラインスクリプト)……
一つのデータの中に複数のインラインスクリプトを書くこともできます。
エントリ名(sentenceなども含む) : ……$(インラインスクリプト1)$(インラインスクリプト2)……$(インラインスクリプト3)……
ひとつのインラインスクリプトは、コマンドとゼロ個以上の引数から成ります。コマンドと引数はスペースで区切ります。
コマンド名 引数1 引数2 引数3 …
スペース文字で区切りますので、引数の中でスペース文字を使いたい場合は、その引数をダブルクォーテーション""で囲うなどするようにしてください。また、引数部分でインラインスクリプトやエントリを使用することは当然可能です。
インラインスクリプトを実行した後に、実行結果が出力データに残るコマンドがあります。(エントリをデータ中で使用すると、エントリに登録されている単語が呼び出されて実際の出力データに使用されるのと同じです)。この、出力データに残されるデータを「戻り値」といいます。
${エントリ} → 解釈 → エントリに登録されている単語1つ
$(インラインスクリプト) → 解釈 →インラインスクリプトの戻り値
また、引数にエントリやインラインスクリプト・ダブルクォーテーションなどが含まれていた場合は、コマンド実行前にまずそれらを一回だけ解釈してから、その結果をコマンド実行に使用します。
(実はコマンド名の入るべき部分(インラインスクリプトの先頭単語)にもエントリやインラインスクリプトなどのデータを使用できます。ただし、"if"、"while"、"silent"は特別な構文コマンドのコマンド名なので、例外としてできません。)
複数のインラインスクリプトをセミコロン";"で仕切ることにより、ひとつの"$("〜")"内にまとめて書き込むすることができます(マルチステートメント機能)。(Phase5.1以降)
エントリ名(sentenceなども含む) : ……$(インラインスクリプト1 ; インラインスクリプト2 ; インラインスクリプト3)……
マルチステートメントの戻り値は、内部に書かれた複数のインラインスクリプトそれぞれの戻り値を並べたものになります。
(この機能は、あとで説明されるsilentコマンドなどで特に意味を持ちます。)
一度使用したエントリを${0},${1}…書式でもう一度呼び出す履歴参照(3章)も利用できます。ただし、インラインスクリプト内で使ったエントリをインラインスクリプトの外で履歴参照することはできません(インラインスクリプト外で使ったエントリをインラインスクリプト内で使うことはできます)。例を見てみましょう。
${エントリ1}${エントリ2}${0}${1}$(コマンド1 ${エントリ3}${0}${1}${2})${エントリ4}${2}
エントリ1とエントリ2の内容は、${0}・${1}という書式を使い、インラインスクリプトの外でも中でも参照することができます。インラインスクリプト中のエントリ3は、${2}としてインラインスクリプトの中に限り参照することができますが、インラインスクリプトの外では参照することができません。エントリ4の内容が、インラインスクリプトの外で${2}とした場合に参照されます。
インラインスクリプトで使用できるコマンドは次のとおりです(Phase6.1)。
コマンド | 書式 | 効果 | 戻り値 |
---|---|---|---|
辞書操作コマンド▼ | |||
set | set エントリ1 単語1 | エントリ1の既登録単語をすべて削除してから、エントリ1に単語1を登録 | なし |
adddict | adddict エントリ1 単語1 | エントリ1に単語1を追加登録 | |
clear | clear エントリ1 単語1 | エントリ1の内容をすべて削除する | |
enumerate | enumerate エントリ1 | エントリ1に登録されている全単語を一覧表示 | エントリ1の全単語(空白区切り) |
searchghost | searchghost エントリ1 | 稼働中の他のゴーストの一覧をエントリ1にセットする | なし |
演算コマンド▼ | |||
inc | inc エントリ1 単語1 単語2 | エントリ1に単語1(数値)を加算する(単語1を省略した場合は1)。 加算の結果が単語2(数値)を越えた場合、エントリ1を単語2にそろえる(単語2を省略した場合は上限なし)。 |
なし |
dec | dec エントリ1 単語1 単語2 | エントリ1から単語1(数値)を引く(単語2を省略した場合は1)。 減算の結果が単語2(数値)を下回った場合、エントリ1を単語2にそろえる(単語2を省略した場合は下限なし)。 |
|
条件式 | [ 単語1 演算子 単語2 ] | 単語1と単語2を演算子で比較する。評価結果が真なら"1"を、偽なら""を返す。 演算子には、文字列比較演算子( == , = , != , > , < , >= , <= )と数値比較演算子( -eq , -ne , -gt , -lt , -ge , -le )がある。 |
true / false |
test | test 単語1 演算子 単語2 | ||
expr | expr 評価式文字列 | 評価式文字列を元に、様々な演算を行う。 | さまざま |
rand | rand 単語1 | ゼロ以上、単語1(数値)未満の乱数を返す(単語1が負の値の場合、ゼロ以下で単語1より大の乱数を返す) | 数値 |
構文コマンド▼ | |||
if | if 条件単語 単語1 単語2 | 条件単語が真("0"、"false"、""のどれでもない)の場合には単語1を、"0"や"false"や""の場合には単語2を返す(単語2は省略可能)。 | 単語1 / 単語2 |
while | while 条件単語 単語1 | 条件単語が真("0"、"false"、""のどれでもない)の間、ずっと単語1を繰り返す。 | 単語1×繰り返した回数 |
until | until 条件単語 単語1 | 条件単語が偽("0"、"false"、""のどれか)の間、ずっと単語1を繰り返す。 | 単語1×繰り返した回数 |
foreach | foreach エントリ1 エントリ2 単語1 | エントリ2の単語1つをエントリ1にセットし、単語1を実行する。それを、エントリ2に登録されている全単語について繰り返す。 | 単語1×エントリ2の総単語数 |
silent | silent | マルチステートメントの中で使用する。silentコマンドがあるのと同じ"$("〜")"中で、silentコマンドを使用する前に実行されたすべてのコマンドの戻り値を破棄する。 | なし |
NULL | NULL | 空文字列を返す。 | なし(空文字列) |
ファイルコマンド▼ | |||
load | load ファイル名 | 華和梨辞書形式ファイルを読み込む(暗号化辞書も可) | なし |
save | save ファイル名 エントリ名1 エントリ名2 ... | 指定したエントリの内容を、指定ファイルに華和梨辞書形式でセーブする(指定ファイルの内容は一旦クリアされてからセーブされる) | |
savecrypt | savecrypt ファイル名 エントリ名1 エントリ名2 ... | 指定したエントリの内容を、指定ファイルに暗号化華和梨辞書形式でセーブする(指定ファイルの内容は一旦クリアされてからセーブされる) | |
データ操作コマンド▼ | |||
date | date 単語1 | 単語1に指定された書式で、現在日時を返す。単語1の書式文字列では、日時データが代入されるタグ(%d, %e, %H, %j, %k, %m, %M, %S, %w, %y, %Y)を使用できる。 | 日時の文字列 |
pirocall | pirocall 単語1 | kawari.iniで読み込んだ「何か。(仮)」のローカルスクリプトファイル形式辞書から、指定したエントリ(単語1)の本文を返す | ローカルスクリプトファイル中のスクリプトエントリ「単語1」の本文 |
escape | escape 単語1 | 単語1中のさくらスクリプトタグをすべてエスケープする | 単語1を処理した結果 |
echo | echo 単語1 | 単語1を返す。 | 単語1 |
eval | eval 単語1 | 単語1に華和梨の各種記号(エントリ、ダブルクォーテーションによるエスケープ、インラインスクリプトなど)が入っていた場合に、それを解釈して返す。 | 単語1を再解釈した結果 |
(コマンドは追加・変更される可能性がありますので、華和梨の最新Readmeに必ず目を通してください。)
辞書を扱うコマンドには、指定エントリに登録されている単語を破棄してから単語を登録する"set"、辞書にエントリ・単語を追加する"adddict"があります。順に説明していきましょう。
setは、指定したエントリに、たった1つだけ単語を設定するコマンドです。指定したエントリがまだ存在しない場合、自動的に新しいエントリが作られ、単語が設定されます。既存のエントリを指定した場合、既に登録されている単語をすべて破棄して、新しい単語を1つだけ設定します。
# 人名エントリの内容をすべて破棄して新たに「田中真紀子」を設定
sentence : $(set 人名 田中真紀子)\0「田中真紀子」を「人名」に登録しました。\e
# nameエントリの内容をすべて破棄して新たに人名エントリ中の単語のうち1つだけを設定
sentence : $(set name ${人名})\0「name」に「人名」のうちの単語「${name}」を登録しました。\e
# Actエントリの内容をすべて破棄して文字列「${normal}」を設定
# (${Act}を呼び出すと${normal}が呼ばれ、「normal」エントリに登録されている単語が1つ呼び出される)
sentence : $(set Act "${normal}")\0「Act」に「"${normal}"」を登録しました。\e
便宜上、sentence(ランダムトーク文)の形をとりましたが、インラインスクリプトはデータのどこに埋め込んでもかまいません。
setコマンドは戻り値を持ちませんから、実行されたあとには何の文字列も残りません。だから、上の例を実行すると、「何か。(仮)」の吹き出しにはインラインスクリプトの前後に記述されているさくらスクリプト部分が表示されます。
adddictは、辞書に単語を足すコマンドです。既にあるエントリに単語を追加することもできますし、指定されたエントリがない場合は自動的に新しくエントリが生成され、単語が追加されます。setとの違いは、既存のエントリに追加するときに、既に登録されている単語を破棄しない点です。
# 人名エントリに「田中真紀子」を追加
sentence : $(adddict 人名 田中真紀子)\0「田中真紀子」を「人名」に登録しました。\e
# nameエントリに人名エントリ中の単語1つを追加
sentence : $(adddict name ${人名})\0「人名」のうちのデータの一つを「name」に登録しました。\e
# Actエントリに「${normal}」を追加
sentence : $(adddict Act "${normal}")\0「"${normal}"」を「Act」に登録しました。\e
$(adddict ... )コマンドも戻り値を持ちません。上の例を実行すると、「何か。(仮)」の吹き出しにはインラインスクリプトの前後に記述されているさくらスクリプト部分が表示されます。
さて、ここで注意しておかなければならないことがあります。2番目の例では、"人名"エントリに登録されている単語のうちどれか一つが"name"に登録されました。しかし、これではいったいどの単語が登録されたかがユーザーからはわかりません。これを表示させたいと考えたときは、
# nameエントリに人名エントリ中の単語を追加
sentence : $(set 仮エントリ ${人名} ; adddict name ${仮エントリ})\0「${仮エントリ}」を「name」に登録しました。\e
のようにします。
なお、このとき"仮エントリ"にローカルエントリ("@〜")を使いたくなると思いますが、Phase5.1〜5.4ではローカル変数そのものが使えなくなっています。(将来的には、再び使えるようになります。また、インラインスクリプト中でも使えるようになるでしょう。)
clearコマンドは、指定したエントリに既に登録されている単語をすべて削除します。
# 人名エントリの内容をすべて破棄
sentence : $(clear 人名)\0「人名」をきれいに消しました。\e
enumerateコマンドは、エントリに登録されている全単語一覧が欲しい場合に使うコマンドです。指定エントリに含まれている全ての単語を、空白文字区切りの形で返します。
season : 春 , 夏 , 秋 , 冬
sentence : \0$(enumerate season)\e
とすれば、sentenceが呼び出されると、"season"に含まれるエントリが「春 夏 秋 冬」(順番は不定)のようにまとめて出力されます。(どっちかというとデバッグ用コマンドなので、使い勝手は悪いかもしれません。)
searchghostは、「何か。(仮)」を複数同時起動している場合に、自分以外に起動しているゴーストの名前を取得します。取得した結果を、指定したエントリの単語として登録します。複数人のゴーストが存在する場合は、ゴースト1人1人の名前を別々の単語として、エントリに登録します。
sentence : \0$(searchghost ゴースト出席簿)出席チェーック!\1$(enumerate ゴースト出席簿)…みんなおるな。\e
このようにすると、エントリ「ゴースト出席簿」に、同時起動しているゴースト名が単語として追加されます。
このコマンドは、過去互換性のために残されているコマンドです。システム変数${system.OtherGhost}を使うようにしてください。
演算コマンドには、エントリに値を加算する"inc"、減算する"dec"、条件判断に使用する条件式コマンドがあります。
指定したエントリに格納されている数値(半角数字と正負記号)に、指定した増分値(単語1、半角英数)を加え、その結果をエントリにセット(格納されていた値を破棄して新しい値を格納)します。このとき、加えた結果が上限値(単語2、半角英数)を越える場合は、加算結果ではなく上限値をエントリにセットします。
増分値が指定されていない場合は増分値"1"として動きます。上限値が指定されていない場合は、上限判定を行いません。
# インクリメントコマンドincのサンプル
数字1 : 0
数字2 : 15
数字3 : 100
sentence : \0えーっとね、${数字1}に1を足すとね、$(inc 数字1)${数字1}になるんだよ。\e
sentence : \0えーっとね、${数字1}に${数字2}を足すとね、$(inc 数字1 ${数字2})${数字1}になるんだよ。\e
sentence : \0えーっとね、${数字1}に${数字2}を足しますが、${数字3}を越えてはいけません。答えはいくつ?\1$(inc 数字1 ${数字2} ${数字3})${数字1}。\e
こんな感じで使います。なお、上限判定だけ利用して使うこともできます。
# インクリメントコマンドを利用したサンプル
数字1 : 150
数字2 : 100
sentence : \0${数字1}が${数字2}より大きかったら、${1}にそろえるね。\n「$(inc 数字1 0 ${数字2})${数字1}。」\nどう?\e
decは、incの反対の効果を持ちます。指定したエントリに格納されている数値(半角数字と正負記号)から、指定した減分値(単語1、半角数字)を引き、その結果をエントリにセット(格納されていた値を破棄して新しい値を格納)します。このとき、引いた結果が下限値(単語2、半角数字と正負記号)を下回る場合は、減算結果ではなく下限値をエントリにセットします。
減分値が指定されていない場合は減分値"1"として動きます。下限値が指定されていない場合は、下限判定を行いません。
# デクリメントコマンドdecのサンプル
数字1 : 0
数字2 : 15
数字3 : -100
sentence : \0えーっとね、${数字1}から1を引くとね、$(dec 数字1)${数字1}になるんだよ。\e
sentence : \0えーっとね、${数字1}から${数字2}を引くとね、$(dec 数字1 ${数字2})${数字1}になるんだよ。\e
sentence : \0えーっとね、${数字1}から${数字2}を引くんだけど、${数字3}を下回ってはいけません。答えはいくつ?\1$(dec 数字1 ${数字2} ${数字3})${数字1}。\e
こんな感じで使います。なお、下限判定だけ利用して使うこともできます。
# デクリメントコマンドを利用したサンプル
数字1 : -150
数字2 : -100
sentence : \0${数字1}が${数字2}より小さかったら、${1}にそろえるね。\n「$(dec 数字1 0 ${数字2})${数字1}。」\nどう?\e
条件式コマンドは、2つの単語を比較して結果を返すコマンドです。コマンドですが、特定のコマンド文字列はなく、2つの単語と比較演算子からなる条件式文字列を大括弧と空白"[ "〜" ]"で囲んで表現します(まったく同じ結果を返すtestというコマンドも実装されていますが、説明は省きます)。条件判断の結果が真の場合、戻り値が"true"に、偽の場合は戻り値が"false"になります。
比較演算子には次のようなものがあります。
演算子 書式 効果 文字列比較演算子 == 単語1 == 単語2 単語1と単語2が等しかったら真、違ったら偽。 = 単語1 = 単語2 != 単語1 != 単語2 単語1と単語2が違ったら真、等しかったら偽。 > 単語1 > 単語2 単語1のほうが単語2よりも文字コード順で後ろになる場合は真、そうでなければ偽。 < 単語1 < 単語2 単語1のほうが単語2よりも文字コード順で前になる場合は真、そうでなければ偽。 >= 単語1 >= 単語2 単語1のほうが単語2よりも文字コード順で後ろになるか同じになる場合は真、そうでなければ偽。 <= 単語1 <= 単語2 単語1のほうが単語2よりも文字コード順で前になるか同じになる場合は真、そうでなければ偽。 数値比較演算子 -eq 数値1 -eq 数値2 数値1と数値2が等しければ真、そうでなければ偽。 -ne 数値1 -ne 数値2 数値1と数値2が違うならば真、そうでなければ偽。 -gt 数値1 -gt 数値2 数値1のほうが数値2より大きければ真、そうでなければ偽。 -lt 数値1 -lt 数値2 数値1のほうが数値2より小さければ真、そうでなければ偽。 -ge 数値1 -ge 数値2 数値1が数値2以上ならば真、そうでなければ偽。 -le 数値1 -le 数値2 数値1が数値2以下ならば真、そうでなければ偽。
条件式は、単体で使用することはほとんどなく、主にif文やwhile文などの条件分岐を行う部分で使用します。
exprコマンドは、さまざまな比較演算、数値計算、文字列処理を行う関数です。戻り値も用途によって違います。実際には、主に数値計算や文字列処理に使用することが多いでしょう。
exprの内部式書式 効果とサンプル 戻り値 単語検索 単語1 | 単語2 単語1。ただし、単語1が偽(文字無し、または"0"、または"false")なら単語2。 単語1または単語2。 例) 人名-specialに内容があれば、それ。無ければ人名の展開結果が返る。
$(expr ${人名-special} | ${人名})単語1 & 単語2 単語1。ただし、単語1と単語2のどちらも偽(文字無し、または"0"、または"false")なら""。 単語1または""。 例) system.Se時点nceとsystem.Sender双方に内容があれば、system.Senderが返る。
$(expr ${system.Sender} & ${system.Se時点nce})数値・文字列比較 単語1 < 単語2 単語1が単語2より小さければ真。そうでないなら偽。 真なら"1"、偽なら""。 単語1 <= 単語2 単語1が単語2と等しいか、より小さければ真。そうでなければ偽。 単語1 = 単語2 単語1と単語2が等しければ真。等しくなければ偽。 単語1 == 単語2 単語1 != 単語2 単語1と単語2が等しくなければ真。等しければ偽。 単語1 >= 単語2 単語1が単語2と等しいか、より大きければ真。そうでなければ偽。 単語1 > 単語2 単語1が単語2より大きければ真。そうでなければ偽。 数値計算 単語1 + 単語2 加算。 計算結果。
単語1と単語2が双方とも数値でなければ、""。単語1 - 単語2 減算。 単語1 * 単語2 乗算。 単語1 / 単語2 除算。 単語1 % 単語2 剰余。単語1を単語2で割った余り。 文字列処理 substr 単語 位置 長さ 指定単語中の、指定位置の文字から指定長さだけ部分文字列を切り出す。
位置は、指定単語の先頭から何番目の文字から切り出すかを数字で指定する。
開始文字の位置は、1から数える。全角文字も1文字として数える。切り出した部分文字列。 例1) 単語「1234567」の先頭から、3文字分の部分文字列を切り出す。
$(expr 1234567 1 3)
出力1) 123
例2) 長めの名詞エントリの文章を読み込むが、途中で止める。
\1$(expr substr ${長めの名詞} 1 6)\w8\w8…\w8\w8…\w8\w8\w8\w8\0\s[7]どうして途中で止めるんだよっ!\w8\eindex 単語 文字列 単語中の、指定文字列に含まれる文字のうちのどれかが最初に現れる場所を返す。
位置は1から数える。全角文字も1文字として数える。文字の位置。 例) $(expr index 華和梨の代わり わ)
出力) 6length 単語 単語の長さを数える。 指定単語の長さ。 find 単語1 単語2 単語1の中に単語2があれば単語2を返す。なければ""を返す。 単語2、または""。 特殊 quote 文字列 文字列にsubstrやindexなどの内部式用の文字列が混じっていても、それを内部式用のコマンドキーワードとして解釈せず、単なるひとまとまりの文字データとして取り扱う。 文字列。 ( 内部式 ) 内部式をexprで展開実行して、その結果をさらにexprで処理する。入れ子構造を作るために用いる。
インラインスクリプトを入れ子にするわけではないので、内部式ではいきなりインラインスクリプトコマンドを使用することはできない。すなわち「$(expr 遅いねぇ & ( ( date %H ")" > 22 ")")」などとすることはできない。
インラインスクリプトの"$("〜")"内で使用する場合、入れ子の末尾の")"をクォートしないと、インラインスクリプト開始"$("の対応カッコだと思われるので注意。例を参照のこと。内部式の評価結果。 例1) $(expr 20 * ( 3 + 2 ")")
出力1) 100
例2) $(expr ${system.Sender} & ( ${system.Sender} != まゆら ")")
system.Senderが空 → (出力無し)
system.Senderが"まゆら" → (出力無し)
system.Senderが"まゆら"以外 → system.Senderの値
randコマンドは、乱数を発生させるときに使うコマンドです。ゼロから指定数値(正負どちらでも可)までの乱数を発生し、返します。ただし、指定数値そのものは発生する乱数の範囲に含まれません。(指定数値が正の場合はゼロから指定数値未満、指定数値が負の場合はゼロ以下かつ指定数値より大きい数値)
# 0から51までの乱数を返す
sentence : \0ルーレットを回したら、$(rand 52)番だった。\1ずいぶんマスの多いルーレットだな。\e
# 0から-9までの乱数を返す
sentence : \0暗いから命中率$(rand -10) ね。\1そんな修正があるの、聞いてないぞ!\e
if文は、Phase0.5系列から劇的に変化しました。ややこしいthen文、else文、endif文が廃止され、すっきりとしたコマンドになっています。
if文は、指定した条件単語が真("0"でも"false"でも""でもない)の場合、単語1を返し、偽("false"か"0"か"")の場合、単語2を返します。単語2は省略可能です。省略したとき、条件単語が偽の場合は、ifコマンドは戻り値を持ちません。
条件式による条件判断を行うときは、条件単語の部分にインラインスクリプトの条件式コマンドを埋め込んで使います。
data : 朝 , 昼 , 夜
sentence : \0\s0条件判断サンプル。$(if $([${data} == 朝]) 朝だよ。 朝じゃないよ。)\e
この場合では、条件式コマンド$([ ${data} == 朝 ])が条件単語として埋め込まれています。${data}の値は「朝」「昼」「夜」の3つを取りうるようになっていますが、判断するときに${data}の中身が3つの単語のうちからランダムに決定され、それが「朝」であるかどうかが条件式で判断されます。判断結果は"true"または"false"の文字列としてifコマンドに渡されます。もし"true"の場合は「朝だよ。」が、"false"の場合は「朝じゃないよ。」が戻り値として選ばれます。
以下は、条件式を使わずtrue、falseを直接与えた場合のサンプルです。
sentence : \0\s0条件判断のサンプル。$(if true 真だよ…こっちしか読まないよ。 偽だよ…こっちはオフレコ。)\e
sentence : \0\s0条件判断のサンプル。$(if false 真だよ…こっちはオフレコ。 偽だよ…こっちしか読まないよ。)\e
if文を直接入れ子にすることも、簡単にできます。
sentence : $(if $([ ${A} == "1" ]) $(if $([ ${B} == "2" ]) Aは1でBは2だよ。 Aは1だけどBは2じゃないよ。) $(if $([ ${B} == "2" ]) Bは2だけどAは1じゃないよ。 Aは1じゃないしBも2じゃないよ。))
わかりにくくなるときは、次のように書いてもよいでしょう。
sentence : $(if $([ ${A} == "1" ]) ${sample7b分岐1} ${sample7b分岐2})
sample7b分岐1 : $(if $([ ${B} == "2" ]) Aは1でBは2だよ。 Aは1だけどBは2じゃないよ。)
sample7b分岐2 : $(if $([ ${B} == "2" ]) Bは2だけどAは1じゃないよ。 Aは1じゃないしBも2じゃないよ。)
whileコマンドは、ループを実現するコマンドです。条件単語が真("0"でも"false"でも""でもない場合)の間中、単語1を展開しつづけます。単語1の中で条件単語を変化させるか、あるいはループ回数の上限に達するまで繰り返します。
条件単語の部分には、ifコマンドと同じく、インラインスクリプトの条件式コマンドなどを利用することができます。
例として、10回ループを回して止めるスクリプトを紹介します。
sentence : \0ループはじめます。$(set ループ回数 1)$(while $([ ${ループ回数} <= 10 ]) ${ループ回数}回目。$(inc ループ回数))ループ終了。\e
この例では、最初にエントリ「ループ回数」の値を1に初期化しています。その後、「ループ回数」の値が10以下なら、ループを回しつづけるように条件式コマンドを使って設定しています。そして、ループさせる単語の内部で、1回ループするごとに「ループ回数」の値を1増やすよう、incコマンドを使って設定しています。こうすることで、1回ループするたびに「ループ回数」の値が1ずつ増加し、ついに10を超えた時点でループ終了となるように設定することができます。
untilコマンドは、whileコマンドと逆で、条件単語が偽("0"か"false"か""の場合)の間中、単語1を展開しつづけます。単語1の中で条件単語を変化させるか、あるいはループ回数の上限に達するまで繰り返します。
条件単語の部分には、ifコマンドと同じく、インラインスクリプトの条件式コマンドなどを利用することができます。
foreachコマンドは、あるエントリの中の全単語を取り出し、その単語ひとつひとつについて処理を実行するためのコマンドです。
具体的には、エントリ2の単語1つをエントリ1にセットするごとに、指定された単語1を実行します。それを、エントリ2に登録されている全単語について繰り返します。
例を挙げましょう。
知人 : ぐにゅう , まゆら , 理夢
sentence : \0友達って、$(set 人数 0 ; foreach 名前 知人 $(echo ${名前}さん、 ; inc 人数) ; echo の${人数}人)しかいないかも。\n\w8……寂しい。\1……俺もなの?\e
これを実行すると、友達としてエントリ「知人」に含まれている全名前をどんどん挙げながら、その人数を数えて、最後に全人数を言い、オチにつなぎます。
silentコマンドは、マルチステートメントの中で使用する特殊なコマンドです。
silentコマンドがあるのと同じ"$("〜")"中で、silentコマンドを使用する前に実行されたすべてのコマンドの戻り値を破棄します。いくつかのインラインスクリプトコマンドを連続実行していくときに、一部のコマンドの戻り値を消したい(トークの中に文字として残したくない)場合に使います。
たとえば、
$(コマンド1 ; コマンド2 ; silent)
となっていた場合は、コマンド1とコマンド2の戻り値が破棄され、この"$("〜")"の戻り値は""(文字無し)になります。また、
$(コマンド1 ; コマンド2 ; silent ; コマンド3 ; コマンド4)
となっていた場合は、コマンド1とコマンド2の戻り値が破棄されますが、その後コマンド3と4が実行されますので、この"$("〜")"の戻り値は、コマンド3とコマンド4の戻り値を連結した文字列になります。
NULLコマンドは、空白文字列を返すコマンドです。主に、条件式などの中で利用します。
たとえば、
sentence : $(if $([ ${test1} == $(NULL) ]) $(echo "test1はカラ") )
とすれば、もしエントリ"test1"の中身がないとき、またはエントリ"test1"が空白を返したときに、「test1はカラ」と表示されます。
ファイルコマンドは、辞書ファイルを取り扱うコマンドです。データをロードしたり、セーブしたり、暗号化してセーブしたりできます。
ゴーストに単語を学習させたり、パラメータを用意して変化結果を記憶させたり、ゲームを用意してユーザーの途中経過を保存したり……と、さまざまな場面で使うことができます。
loadコマンドは、辞書ファイルをロードするコマンドです。kawari.ini中で「dict : ファイル名」と書くことで読み込むことのできる辞書形式なら、すべて読み込むことができます。
# dict-flag.txtを読み込む
sentence : $(load dict-flag.txt)\0辞書dict-flag.txtを読み込んだよ。\e
辞書ファイルが華和梨本体とは違うフォルダにある場合、場所の指定には相対パスと絶対パスのどちらも利用できます。
saveコマンドは、辞書ファイルにデータをセーブするコマンドです。このコマンドを実行すると、指定ファイルの中身がいったんクリアされ(ここに注意!)、指定ファイルに指定したエントリのデータが書き込まれます。
# flag1、flag2、flag3エントリをdict-flag.txtに保存
event.OnClose : $(save dict-flag.txt flag1 flag2 flag3)\0パラメータ保存した!安心。終了〜。\-
上の例では、終了時に呼ばれるイベント「event.OnClose」中で、dict-flag.txtに3つのエントリflag1、flag2、flag3を保存しています。
savecryptコマンドは、saveコマンドと同じく、辞書ファイルにデータをセーブするコマンドですが、savecryptでは暗号化してセーブを行います。saveコマンドと同じく、このコマンドを実行すると指定ファイルの中身がいったんクリアされ、指定ファイルに指定したエントリのデータが暗号化されて書き込まれます。
# flag1、flag2、flag3エントリをdict-flag.txtに暗号化して保存
event.OnClose : $(savecrypt dict-flag.txt flag1 flag2 flag3)\0パラメータ、暗号で保存した!安心。終了〜。\-
上の例では、終了時に呼ばれるイベント「event.OnClose」中で、dict-flag.txtに3つのエントリflag1、flag2、flag3を暗号化してから保存しています。
データを操作するコマンドには、dateコマンド、pirocallコマンド、escapeコマンド、echoコマンド、evalコマンドがあります。
dateは、現在の年月日や時刻を取得して文字列として取り扱うコマンドです。取得したデータは戻り値に入るので、システムエントリっぽく使うことが出来ます。
sentence : \1今、なんどき?\0えっとね、$(date "%y/%m/%d/ %H:%M:%S")です。\e
書式指定文字列には以下の書式文字が使えます。
書式文字 書式 例(2001年5月20日16時35分30秒・日曜日の場合) %y 年 (4桁) 2001 %Y %m 月 (01..12) 05 %n 月 (1..12) 5 %d 日 (01..31) 20 %e 日 (1..31) 20 %H 時 (00..23) 16 %k 時 (0..23) 16 %M 分 (00..59) 35 %N 分 (0..59) 35 %S 秒 (00..59) 30 %r 秒 (0..59) 30 %w 曜日(0..6) 日曜日が0 0 %j 元日からの通算日(001..366) 140 %J 元日からの通算日(1..366) 140
書式指定文字列中にはエントリも使えます。たとえば、先の例文と以下の例文は同じ結果になります。
date_format : "%y/%m/%d %H:%M:%S"
sentence : \1今、なんどき?\0えっとね、$(date ${date_format})です。\e
date_formatエントリに複数の書式指定をしておけば、書式をランダムに変更するなんてこともできますね。反応のさくらスクリプトそのものも一緒に埋め込んでしまってもいいですね。
時間ネタ : "\0%y/%m/%d %H:%M:%Sです。\1腹減ったな。\e","\0だから、%H:%M:%Sだってば。\1タスクトレイの時計と張り合うなよ。\e"
sentence : $(date ${時間ネタ})
pirocallコマンドは、「華和梨からscript.txtの中身のエントリを使いたい!」場合にのみ使用するコマンドです。
pirocallは、script.txt中の指定されたスクリプトエントリの中身を呼び出して返すことができます。(中身に華和梨の単語エントリなどが記述されていた場合は、それも展開されます)
script.txtとは、かつてゴースト作成に利用されていた、イベント反応用さくらスクリプトを記述するためのデータファイルです。「何か。(仮)」の「スクリプト」の項に記載されています。(このscript.txtを用いてゴーストのしゃべりを作ることを「スクリプトオーバーライド」と呼びます)
現在では、華和梨を使う限りscript.txtはゴースト作成にはまったく必要ないのですが、以前からゴースト作成に携わっている皆様の中には、かなり作り込んだscript.txtをお持ちの方も多いでしょう。「せっかく作ったscript.txtを全部華和梨に移植するのは大変だ」という場合に、このpirocallコマンドを使います。
たとえば、次のようなscript.txtがあったとします。
[SAKURA]
///////////////
// mainmenu
///////////////
#mainmenu
\1\s0\0\s0ん?\n\n\q0[#mainmenu ai][何か話して]\q1[#mainmenu com][コミニュケート]\q2[#mainmenu hoge][メニューは不要だと思う]\q3[#cancel][キャンセル]\z\e
#mainmenu ai
${sentence}\e
#mainmenu com
\0\s0?\__c\e
#mainmenu hoge
\0\s7メニューが無いとshioriのデバッグができないじゃないのか。\e
#terminate etc
\-
このscript.txtを、kawari.iniで他の辞書と同じように読み込みます。
dict : dict-base.txt
dict : ghost-kawari.txt
dict : dict-standard.txt
dict : dict-長めの名詞.txt
dict : script.txt
(なお、スクリプトオーバーライド用ファイルかどうかの判別は、ファイル頭の"[SAKURA]"ヘッダで行っているので、ファイル名はscript.txtでなくてもかまいません。)
ghost-standart.txtなどの華和梨辞書ファイル中でこのscript.txt内の反応を呼び出すときには、次のようにします。
event.OnMouseDoubleClick : $(pirocall "#mainmenu")
この例では、マウスのダブルクリック時の反応に、script.txt内の"#mainmenu"というエントリを使用するように設定しています。(script.txt内のエントリ名は、コメントアウトのための文字"#"が入っているので、華和梨からコールするためにはダブルクォーテーションで囲う必要があります。)
escapeコマンドは、指定された単語中のさくらスクリプトタグをすべてエスケープする(「何か。(仮)」が解釈せずにそのまま表示する形にする)コマンドです。具体的には、「\」を「\\」に、「%」を「\%」に置き換えます。
たとえば、次のように使います。
# ドラッグ&ドロップされたファイル名を表示する
event.OnFileDropped : $(escape ${system.Reference0})がドロップされました。 \e
イベントOnFileDroppedの戻り値Reference0には、「何か。(仮)」の本体にドラッグ&ドロップされたファイルのファイル名がパス付きで渡されます。パス文字列中には「\」がたくさん含まれていますから、これをそのまま表示しようと思っても、「何か。(仮)」がタグ開始文字の「\」と勘違いしてうまく表示できません。
そこで、対象はさくらスクリプトではないですが、その中のさくらスクリプトタグ開始文字をすべて置換してしまうためにescapeコマンドを使います。
echoコマンドは、引数で渡した単語をそのまま表示するだけのコマンドです。主にデバッグなどのために使用します。
evalとは、echoとは違い、引数で渡した単語を「再解釈して」その結果を表示するコマンドです。
たとえば、華和梨のインラインスクリプトコマンドやエントリが埋め込まれている文字列があったとします。華和梨にとってはあくまで文字列ですから、そのままでは内部のコマンドやエントリは実行・変換されません。こいつを、ちゃんとインラインスクリプトコマンドを実行し、エントリも単語に置き換えて表示するよう、華和梨に処理させるコマンド、それがevalです。
例を見てみましょう。
data1 : データ実体
data2 : "${data1}"
sentence : \0データは「${data2}」だよ。\e
この例では、「データは${data1}だよ。」と出力されます。(このように出力させるために、data2に単語を登録するときにわざわざダブルクォーテーションで囲ってあるのがわかるでしょうか。)
このダブルクォーテーションの内側にある${data1}を、展開させて表示したいと思ったとしましょう。真っ先に${${data2}}のような書き方を思いついてしまうかもしれませんが、これは華和梨の文法上、間違いですので動きません。
そこでevalコマンドの出番です。
data1 : データ実体
data2 : "${data1}"
sentence : \0データは「$(eval ${data2})」だよ。\e
このようにevalコマンドで${data2}を開いて出てきた文字列を、再度華和梨の文法に従って解釈しなおすよう、指示します。すると、「データはデータ実体だよ。」と出力されます。
どんなときに使うのか?というと、ですね。
エントリの中に登録されている単語文字列&それを利用して作った文字列を、エントリ名として使いたいときです。ためしに、時間別対応のスクリプトを書いてみましょう。
["今何時?"] : \0$(eval "${"time-$(date %H)"}")だよ。\e;
time-00 : 午前零時
time-01 : 午前一時
time-02 : 午前二時
time-03 : 午前三時
time-04 : 午前四時
time-05 : 午前五時
time-06 : 午前六時
time-07 : 午前七時
time-08 : 午前八時
time-09 : 午前九時
time-10 : 午前十時
time-11 : 午前十一時
time-12 : 午後零時
time-13 : 午後一時
time-14 : 午後二時
time-15 : 午後三時
time-16 : 午後四時
time-17 : 午後五時
time-18 : 午後六時
time-19 : 午後七時
time-20 : 午後八時
time-21 : 午後九時
time-22 : 午後十時
time-23 : 午後十一時
time-24 : 午後十二時
この例では、$(date %H)で取得した2文字表記の「時間」の前後に文字列を追加して、華和梨文法のエントリ呼び出し用文字列「${time-??}」を構成し(この文字列は華和梨にとってはただのデータ文字列扱い)、それをevalで展開しています。(わかるかな。)
(Phase0.5β2付属のghost-kawari.txt中のOnBootサンプルより、このほうが記述はシンプルです。)
このページは、偽Meisterが作成しています。