# プログラマブル準AIモジュール "華和梨" 幸水マニュアル

2008/01/26
Phase 8.2.5

華和梨開発チーム :
NAKAUE.T (Meister), 偽Meister (夢乃), さとー, 酔狂, さくらのにえ

## Index

← back

> 1. 用途
> 2. 起動方法
> 2.1. コマンドラインオプション
> 2.2. インタープリタモード
> 2.3. デバッガモード
> 2.4. quietモード
> 3. 操作方法
> 3.1. echoモード
> 3.2. commandモード
> 4. 終了方法
> 5. 幸水を使ったデバッグの指針
> 5.1. 文法エラーのデバッグ
> 5.2. 実行時エラーのデバッグ
> 5.3. アルゴリズムのデバッグ

## 1. 用途

"幸水(こうすい)"はコンソールで動作する華和梨インタープリタです。 稼動中のゴーストの華和梨と接続し、デバッグ作業を行うことも出来ます。

## 2. 起動方法

"kosui.exe"をダブルクリックするか、 "kosui.exe"の存在するフォルダで コンソールを開き、"kosui.exe"と入力すると起動します。

この時、稼動中のゴーストがあるかどうかによって、 幸水の動作は大きく「インタープリタモード」と、 「デバッガモード」の二つに分かれます。

### 2.1. コマンドラインオプション

幸水は以下のコマンドラインオプションを受け付けます。 DOS窓からの起動時などに指定してください。

kosui [--event|-e イベント名] [--norc] [--quiet] [<rcfile>]

--event / -e イベント名 | デバッガモードで、ゴーストへ通知する際のイベントを変更する。
--norc | kawarirc.kisを読み込まない。
--quiet | quietモードで、初期化ファイルを読み込んだ後に直ちに終了する。
<rcfile> | ファイル名を指定することにより、 kawarirc.kisの代わりにそのファイルを初期化ファイルとして読み込む。

### 2.2. インタープリタモード

稼動中のゴーストが見つからない場合、幸水はインタープリタモードで起動します。 インタープリタモードでは、幸水に内蔵された華和梨モジュールを使用します。 また、稼動中のゴーストがある場合でも、 起動後に接続するゴーストとして0番を選ぶとインタープリタモードで起動します。

インタープリタモードで起動すると、 幸水は"kosui.exe"が存在するフォルダの kawarirc.kisを通常通りに読み込んだ後、 キー入力を受け付ける状態になります。

### 2.3. デバッガモード

稼動中のゴーストがある場合、幸水はデバッガモードで起動します。 デバッガモードでは、 幸水はゴーストに対してDirect SSTPを使ったイベント通知によって文章を送ります。 デフォルトで通知できるイベントは「ShioriEcho」ですが、 起動時のオプションに「kosui -e イベント名」、 もしくは「kosui --event イベント名」とすることにより、 任意のイベント名で通知することができます。

稼動中のゴーストが華和梨ゴーストで、 「debugger on」が実行されている場合、幸水は上記のイベントを利用して、 そのゴーストの華和梨モジュールと接続します。 接続すると、幸水は稼動中のゴーストを操作することが出来ます。

デフォルトの「ShioriEcho」が使われている場合、 ShioriEchoイベントに対応するエントリを記述する必要はありません。 その他のイベント名を用いている場合は、対応するエントリを記述してください。

### 2.4. quietモード

このモードは特殊なモードです。 コマンドラインオプションで、 「kosui --quiet」と指定することでこのモードに入ります。

quietモードは、インタプリタモードと同じように 幸水に内蔵された華和梨モジュールを使用して起動します。 初期化ファイル(標準ではkawarirc.kis)を読み込んだ後、 直ちに終了します。華和梨をSAORI Basicとして使用したい場合や、 CGIとして使用したい場合に使います。

## 3. 操作方法

インタープリタモード、デバッガモードのどちらでも、幸水の操作方法は共通です。 キーボードから文章を入力し、最後にENTERキーを押すと入力した文章を評価します。 入力を評価した結果は、インタープリタモードの場合はコンソールに、 デバッガモードの場合はゴーストのバルーンに表示します。

「.[ENTER]」と入力すると、幸水は入力モードを切り替えます。入力モードは2種類あり、 「echoモード」と「commandモード」があります。コンソールの一番下の表示行が

echo-mode >

の場合がechoモード、

command-mode >

の場合がcommandモードです。起動直後のモードはechoモードです。

### echoモード

echoモードでは、幸水は入力された文章をそのまま評価します。 文章中に${npw}等のエントリ呼び出し、 $(set A "TEST")等のインラインスクリプトがある場合、これも解釈します。

echo-mode > \0${挨拶}、私は${myname}です。\1よろしくな。\e$(set Boot 1)

### commandモード

commandモードはインラインスクリプトの評価に特化したモードです。 幸水は入力された文章の前後に、"$("と")"を付加してから評価します。例えば、

command-mode > adddict npw "まゆら"
と入力すると、「$(adddict npw "まゆら")」という文を評価します。

## 4. 終了方法

echoモード、commandモードのどちらでも、 「exit[ENTER]」と入力すると幸水を終了することが出来ます。

## 5. 幸水を使ったデバッグの指針

幸水を用いたデバッグは、大きく3段階に分かれます。 必ずしもこの手順に従う必要はありませんが、合理的手順だと考えます。 参考にしてください

1. 文法エラーデバッグの段階
2. 実行時エラーデバッグの段階
3. アルゴリズムデバッグの段階

それでは、各段階でのデバッグ手順を紹介します。

### 5.1. 文法エラーのデバッグ

まず、文法的エラーの検出を行います。 例として、次のようなファイルのデバッグを行います。 仮に、ファイル名は「test.kis」として話を進めます。

# test.kis
日本語挨拶 : こんにちは
英語挨拶 : Hello.
npw : 黒衣鯖人,閑馬永空,川上新夜
# 存在しないコマンドを誤って使った例
test1 : $(copu npw npw.backup)
# 辞書モードが違う例
get npw;
# 括弧の対応がおかしい例
test2 : 英語では、「${日本語挨拶}」は「${英語挨拶)」です。
=kis
# 括弧の対応がずれて、おかしな所でエラーが出る例
function test3 $(
if $[ ${today} != ${birthday} ] $(
if $[ ${bootcount} >= 10 ] $(
echo \0\s[5]非誕生日、おめでとうございます！;
#)
) else $(
echo \0\s[4]…誕生日かい、たまにはついてない日もあるよ。\e;
)
);
=end

このファイルを幸水で読み込みます。 この際、ログレベルを「error」と「warning」に設定します。 OSがWin9x系でログが流れて困る場合は、適当なログファイルに出力します。

コマンドラインでの入力:
command-mode > loglevel error warning
command-mode > logfile debug.log
command-mode > load test.kis

すると、ログファイルdebug.logに、 次のような内容が出力されていると思います。

.\test.kis 9: error: ':' not found.
.\test.kis 9: error: ':' not found.
.\test.kis 9: error: could not find EntryId at start of entry definition.
.\test.kis 9: error: no entry names.
.\test.kis 12: error: '}' not found. runaway entry call?
.\test.kis 12: error: could not find statement list separator ','.
.\test.kis 25: error: ')' not found. runaway script?

エラーの内容を見てみましょう。 エラーメッセージ一覧を参照しながら見て下さい。 まず、9行目で「":"が無い」、「エントリ名が無い」というエラーが出ています。 9行目を見ると、本来ならスクリプト記述ゾーンで書くべきコマンドを、 辞書記述ゾーンで書いています。このため、コマンドをエントリ名と誤解して、 「":"が無い」というエラーを出しています。ですから、9行目を

=kis
get npw;
=end

このように書き換えると、9行目のエラーは解消します。

次に、12行目のエラーメッセージを見ます。 「"}"が無い」が主なエラーです。よく見ると、 12行目は「${英語挨拶}」とあるべき部分が、 「${英語挨拶)」と、間違った括弧が書いてあります。 これを修正すれば12行目のエラーは解決です。

最後に、25行目のエラーを見ます。 「")"を見失った」というエラーです。しかし、25行目を見ると括弧はあります。 しかしよく見ると、21行目でわざと括弧をコメントアウトしています。 このため、括弧の対応がとれず、一番最後の括弧でエラーが出ていた訳です。

このように、括弧の対応エラーは、記述間違いのあった所とは違う部分で エラーが出ることがあります。 括弧の対応を、エラーの出た箇所より前に遡って確かめてください。 こうした作業は、メモ帳ではなくエディタを使うことをお薦めします。 大抵、プログラムを書くモードがあり、括弧の対応をエディタが確かめてくれます。

以上までで文法デバッグの終わったスクリプトを掲載します。 しかし、このスクリプトは、まだ実行時エラーが残っています。 実行時エラーのデバッグは、次の節で解説します。

# test.kis(文法エラー修正後)
日本語挨拶 : こんにちは
英語挨拶 : Hello.
npw : 黒衣鯖人,閑馬永空,川上新夜
# 存在しないコマンドを誤って使った例
test1 : $(copu npw npw.backup)
# 辞書モードが違う例
=kis
get npw;
=end
# 括弧の対応がおかしい例
test2 : 英語では、「${日本語挨拶}」は「${英語挨拶}」です。
=kis
# 括弧の対応がずれて、おかしな所でエラーが出る例
function test3 $(
if $[ ${today} != ${birthday} ] $(
if $[ ${bootcount} >= 10 ] $(
echo \0\s[5]非誕生日、おめでとうございます！;
)
) else $(
echo \0\s[4]…誕生日かい、たまにはついてない日もあるよ。\e;
)
);
=end

文法エラーデバッグの節の終わりに、一つアドバイスがあります。 エラー箇所を一つ修正したら、その都度ファイルに保存して幸水でチェックして下さい。 一つエラー箇所を直すと、そのあとのエラー表示が劇的に消えることがよくあります。

これは、華和梨がファイルを読み込む際、ファイルの先頭から読み込むため、 一箇所文章の解釈でつまづくと、そのあとの解釈はすべてその誤りを引きずる為です。 これは、英語の翻訳で代名詞が指す人物を間違えると、 そのあとの文章が滅茶苦茶な解釈になるのと同じことです。

また、疑わしい部分全体を一度コメントアウトし、 徐々にコメントアウトの範囲を変えて、 どこでエラーが発生するかを絞り込む方法も有効です。

華和梨は人間と比べて非常に愚直で、「文章を良きに計らう」ことが出来ません。 根気よくエラーを直してください。

### 5.2. 実行時エラーのデバッグ

続いて、実行時エラーのデバッグに移ります。 エコーモードで、次のように入力して下さい。

echo-mode > ${test1}

これは、test1エントリを呼び出しています。 すると、次のようなエラーメッセージが出る筈です。

undefined function "copu" called.

「"copu"という定義していない関数が呼ばれた」と言っています。 確かにtest1エントリを見ると、 copuというコマンドを呼んでいます。 自分で定義しない限り、このコマンドは存在しません。

このように、コマンド名のタイプミス等は、 実際に動かしてみないと、エラーを発見できない場合があります。 文法デバッグが完了したら、主要なエントリ・ユーザ定義コマンドを 実際に呼んでみて、実行時エラーが起きないか確かめます。

さらに、エントリ名のタイプミスで、 実際には存在しないエントリを呼んでいる場合もよくあります。 これを検知する為には、

loglevel error warning decl

このように、 ログレベル設定に「decl」を追加します。 この状態で、定義されていないエントリ「hoge」をエントリ呼び出しすると、

read access to empty entry 'hoge'

このように、「空のエントリを呼び出した」と通知してきます。 なお、entryコマンドで空のエントリを呼んだ場合、 通知しないので注意してください。

以上の作業を繰り返してエラー表示が出なくなれば、 実行時エラーを追放できたことになります。

### 5.3. アルゴリズムのデバッグ

実は、これからが本当の意味でのデバッグです。 普通の言葉でも、文法的誤りは無いのに、筋の通らない文章は幾らでもあります。 また、内容は正しくても、実際に起こらない状態を前提としているため、 実質的に意味をなさない文章も良くあります。 スクリプトにもまったく同じことが言えます。

プログラムの世界では、プログラムの流れの骨格を「アルゴリズム」と呼びます。 ゴーストのデバッグでも、最後はこうしたアルゴリズムのデバッグが必要です。 例えば、 Referenceが必ず3個あることを想定したコマンドは、 4個目のReferenceがあった場合、 情報を取りこぼすかもしれません。 また、 数字の大小比較で「<」と「>」を書き間違えても、 文法的にはまったく正しいスクリプトです。

こうしたアルゴリズムのデバッグで有効な方法を、2つ紹介します。 どちらも普通のプログラム言語のデバッグで、一般的な方法です。

#### printfデバッグ

この方法は、 スクリプトの随所にlogprintを挿入し、 スクリプトの流れのキーとなるエントリの内容を、ログに記録する方法です。 C言語でprintf関数を用いてよく行うことから、 この名前があります。

例えば、

reply.OnMinuteChange : $(
if $[ $(date %M) == ${timer} ] $(
entry sentence.timer;
);
)

このようなスクリプトを考えます。 このスクリプトは指定時刻になったら発話することを意図していますが、 ある時刻を指定した場合、発話しなかったとします。 これをprintfデバッグするため、 次のように書き換えます。 値を「#」で挟んでいますが、 これはログ中で強調するためで、特に記号に意味はありません。

reply.OnMinuteChange : $(
logprint #$(date %M)#;
logprint #${timer}#;
logprint #$[ $(date %M) == ${timer} ]#;
if $[ $(date %M) == ${timer} ] $(
#entry sentence.timer;
setstr @temp $(entry sentence.timer);
logprint #${@temp}#;
echo ${@temp};
);
)

このスクリプトの場合、

- 条件式がおかしい
- 発話する内容がおかしい

の2通りの原因が考えられます。 そこで、条件式とエントリの両方をログに出力しています。 また、 sentence.timerエントリを複数回呼んで状態を変えないよう、 一度@tempエントリにsetstrで代入し、 それからログに出力、戻り値として出力を行っています。

printfデバッグは比較的煩雑ですが、 エントリの内部情報を確実に得られます。 しかし、内部状態を得ることで状態を変えてしまわないよう、 注意する必要があります。 また、デバッグコードが残っていると、ファイルサイズが増加します。 ゴーストのリリースの際、 ネットワーク更新を素早くする為、確実にデバッグコードを消すよう、 注意を払う必要があります。

#### デバッガによるデバッグ

このデバッグ方法は、幸水をデバッガモードで起動して、 実際にゴーストが動いている状態で行います。 先ほどのスクリプトをデバッグすることを考えると、 echoモードで

echo-mode > $[ $(date %M) == ${timer} ]

など、 先ほどのprintfデバッグでログに出力した内容を、 バルーンに出力させる方法です。

この方法は、スクリプトを書き換える必要が無い点が大きなメリットです。 反面、時間に依存する情報のデバッグでは、 ちょうどいい瞬間にエントリの内容を調べることが、やや難しいのが難点です。