例外処理

プログラムの実行過程で、ユーザーからの予想外の値の入力など、いくつかのあらかじめ予測不能な原因が競合した結果、そのまま処理を続行することができなくなる局面がある。そのような場合に備えて、処理を適切に中断させるためのワードがある。一般的にそのような処理は例外処理(exception)とよばれている。本来ならばその後に継続するはずの複数の処理を無しで済ますものであるため、かなり強引な処理となる。したがって、例外処理は、まさに例外的に予測不能な事態に対して用いるべきもので、通常の条件外のときまで例外処理をすると、ちょっとカッコ悪い。

ABORT

ABORTは、以降の処理を全て中止して整理し、入力待機状態に戻す。実行時にはビープ音が出る。通常は、IF構造で囲い、エラーコードフラグを判定して、エラーがある場合にのみABORTが呼ばれるようにする。ABORT自体は、エラーコード-1となっている。

ABORT"

ABORTの最後に二重引用符 “ をつけたこのワードは、エラーがあってABORTによって処理を中断する際に表示するメッセージ文字列を追加することができる。
たとえば、
ABORT" Error1 happend!"
とすれば、動作はABORTと同じだが、コンソールに
Error1 happend!
と印字される。

これは、ただのABORTとは異なり、スタックから一つ入力値をとる。この値が0であったときには、ABORT"~"は実行されない。非0の値をエラーとして認識する。

DIE

DIEはMopsで伝統的な処理中断用のジェネリックなワードで、入力値を一つ取り、その値がゼロでない場合には、その入力値をエラーコードとして処理を中断する。入力値が0である場合は、そのまま処理を続行する。
DIE ( error-code -- )

以上の例外処理ワードでは、実行されると、処理を中断した後、通常のイベント待ち状態に戻ってしまう。どこかのプロセスに飛んだりすることはできない。したがって、何か処理が必要であるときは、エラーを判別した後に、必要な処理を行うコードを付け加え、最後に上記例外ワードを呼び出すようにしなければならない。

THROW - CATCH

ThrowとCatchのペアは、例外が生じたときに指定された位置にジャンプする機構を提供する。例外が生じた場合、Throwを実行すると、プロセスは、対応するCatchのところにジャンプする。Catchの後は、Throwされたのか、エラーなく実行されたのかを、エラーコードフラグによって判定し、適切にエラー処理を行うべきことになる。

THROW ( k×x n -- k×x | i×x n )

形の上では、Catchは、どこかにThrowを含み得るワードのxtを実行する形式になる。
CATCH  ( i×x xt -- j×x 0 | k×x err )
例えば、TestWordというワードが定義されて、それが呼び出すどこかのワードでThrowが実行され得るという場合、
0 value flag1
0 value flag2
0 value flag3
: th1 flag1 THROW ;
: th2 flag2 THROW th1  ;
: TestWord flag3 IF th2 ELSE ." Nothing Done!" THEN ;
: word1 (... -- err )  .... ['] TestWord Catch ;
という形になる。このようにすれば、TestWordの処理の中のどこでThrowが実行されても、プロセスはこのCatchに戻ってくる。この場合、Catchは、xtで与えられたワード、この場合、TestWordを実行する。TestWordの実行終了後、実行はCatchの終わりの部分に戻ってくる。エラーがない場合には、プロセスはThrowを素通りする。“ノーエラー”コードはThrowが消費する。最終的に例外なしでCatchの終端に達した場合は、そこで0(ノーエラー)がスタックに積まれる。エラーが生じた場合のCatch後の動作は、Throwのセマンティクスによるとされるが、結局は、エラーコードに応じた例外処理(Mopsの場合はDIEに相当)である。この処理は各自が実装することができる。総合的に見れば、Catchの後には必ず、エラーコードか0がスタックのトップにあるので、そこから行うべき処理を判定すれば良い。

上のコードは無意味な上に複雑だが、word1内の....部分はないものとして概説すると、value変数であるflag1~3の値によってTHROW-CATCHの動作が確認できる。
  1. flag3の値が0の場合、TestWord内のIF-分岐で、後半の"Nothing Done!"が印字されるにとどまり、THROWはされないので、Catchに戻ってきたときは、0 (ノーエラー)がトップスタックに残される。
  2. flag3が非0の場合、TestWord内のIF-分岐でth2が実行される。
    1. flag1,2とも0の場合、th2内で、flag2は0であるのでTHROWはその値を受けてノーエラーと判断して、THROWはせず、次に実行を続ける。したがってth1が呼び出される。th1内では、flag1が0であるので、やはりTHROWはされず、そのままTestWord内の全実行は終了して、Catchに戻ってくる。THROWはどこでもなされていないから、Catchの後には0(ノーエラー)がスタックに残る。
    2. flag2の値が1である場合、th2内で、flag2の値1はエラーコードと判断されて、THROWが実行されるため、th1は呼び出されることなく、実行はCatchに戻る。CatchはTHROWがなされたエラーコードとして1をスタックに残す。この場合、flag1の値は実行に影響しない。
    3. flag2が0で、flag1の値が2である場合、th2内でflag2の値0はノーエラーと判断されてTHROWはなされず、th1が呼び出される。th1内ではflag1の値2はエラーコードと判断されて、ここでTHROWが実行される。実行はCATCHに戻り、THROWがなされたエラーコードとして2をスタックに残す。

Catchで実行されるワードが、Catchで実行される部分を入れ子状に含んでいてもよく、Throwは、例外時には、その部分を実行している一番上位の(つまり、そのThrowを含む一番内側の)Catchに実行を戻すことになる。

以上の例外処理ワードは、実行の流れを飛躍させる結果になる。特に、Throw-Catchペアは、その後の実行はプログラムに任されているので、本当の例外処理の場面に限って用いるべきで、そうでないと実行の流れが混乱して、わかりにくいコードになってしまう。Catchがエラーコードを返したなら、通常は、一連の処理を中止し、必要な後処理をした後は、直ちにイベント待ち状態に戻るべきことになる。

次は、?
最終更新:2019年04月21日 14:36