MikoScript 言語仕様

 例外処理

 プログラムの実行中にエラー等の「例外」が発生した場合、本来の処理を継続できなく
なります。この時、制御は「例外処理」に移行します。「例外」とは、本来の処理を継続
できなくなる事態のことです。また、それに対応する処理が「例外処理」です。エラーは、
例外の代表的なものですが、例外は、必ずしもエラーだけではありません。しかし、例外
とエラーを同義で使う場合もあります。

 本言語システムが管理するエラーに対応する例外処理は、以下の順で行なわれます。
    (1)エラー情報の設定
    (2)エラー代替
    (3)エラーワープ
つまり、プログラム実行中にエラーが発生すると、まず、そのエラー情報が設定されます。
次に、そのエラーの代替手段が確認されます。それが設定されていれば、そのエラーは、
その手段で代替されて、制御は、本来の処理に戻ります。代替手段が設定されていないと、
最後に、エラーワープが確認されます。それが設定されていれば、そのワープ先に制御が
移行します。設定されていないと、そのスレッドの実行は異常終了します。エラー代替と
エラーワープは、最初は未設定なので、適時設定する必要があります。

 ユーザー独自の例外処理は、例えば、ワープによって行なうことができます。その際、
スレッドローカルスコープ内等に、その例外情報を設定して渡すことができます。

●エラー情報
 プログラム実行中にエラーが発生した時に、そのエラー情報が、現実行中のスレッドの
スレッドローカルスコープ内に自動的に設定されます。各エラー情報が格納される変数名
とその内容を、次に示します。
    err_code    エラー識別番号(整数値)
    err_info    エラー付加情報(文字列)
    err_state   エラーワープ状態フラグ(整数値)
この各エラー情報の変数にアクセスするには、各変数名の前にスレッドローカルスコープ
を規定する演算子 $ を付加する必要があります。

 エラー識別番号とエラー付加情報に対応するエラーメッセージは、::ErrorMessage と
いうシステム組み込み関数を、次の形式でコールすることによって、取得できます。
    ::ErrorMessage( エラー識別番号, エラー付加情報 )
例えば、上記の $err_code, $err_info に対応するエラーメッセージをプリントするには、
次のようにします。
    print ::ErrorMessage( $err_code, $err_info );
 エラーワープ状態フラグ $err_state については、「エラーワープ」の節で説明します。

●エラー代替
 本言語では、プログラム実行中に発生したエラーを代替する手段が提供されています。
例えば、存在しない変数の値を読み出そうとした場合、通常はエラーになりますが、その
「代替値」が設定されていれば、その値が対象の変数の値の代わりに使われて、その処理
が継続されます。また、未定義の関数をコールしようとした場合も、エラーになりますが、
その「代替関数」が設定されていれば、その関数が対象の関数の代わりにコールされて、
その処理が継続されます。

(1)代替値
 不在/不正の箱の中身を読み出そうとした時には、例外が発生します。それに対処する
ために「代替値」が設定できるようになっています。代替値を設定しておけば、この例外
が発生した時、その不在/不正の箱の値の代わりに、代替値が使用されて、プログラムの
実行が継続します。代替値は、DefValue という名前の箱に設定しておきます。例えば、
    DefValue = 0;
    print A;
の実行では、プリント対象の変数 A が不在なので、この値は、DefValue の値で代替され
て、0 がプリントされます。この例では、DefValue は、現実行関数のローカルスコープ
内に設定しましたが、他のスコープに設定しておくこともできます。また、DefValue に
は、整数以外のデータ型も設定できます。特に、null を設定する場合はよくあります。

 DefValue 箱は、以下の順に各スコープで検索されて、最初に見つかった箱が採用され
ます。
    (1) 関数ローカルスコープ
    (2) スレッドローカルスコープ
    (3) モジュールローカルスコープ
    (4) グローバルスコープ

例えば、次の例では、関数 f1 内での変数 X の代替値は、その関数ローカルスコープ
にはないので、スレッドローカルスコープ内の $DefValue になり、関数 f2 内での変数 
Y の代替値は、その関数ローカルスコープ内に設定されている DefValue になります。
    function f1()  {  print X;  }
    function f2()  {  DefValue = "代替値0";  print Y;  }

    $DefValue  = "代替値1";
    ^DefValue  = "代替値2";
    ::DefValue = "代替値3";
    f1();
    f2();
これを実行すると、次の通り、プリントされます。
    代替値1
    代替値0

 代替値による例外処理が不要になった際には、DefValue の箱を削除することによって、
その代替値の例外処理を無効できます。例えば、
    DefValue = 0;       // これ以降、代替値による例外処理が有効
    print A;
    delete DefValue;    // これ以降、代替値による例外処理は無効
    print A;
では、最初の print 文の実行で 0 がプリントされますが、2回目の print 文の実行で
発生するエラーの代替は無効なので、そのスレッドは異常終了します。

(2)代替代入先
 代入文で、代入先が不正な場合、大抵はコンパイル時にエラーになります。プログラム
の実行時に、代入先が不在の場合、通常は新規作成されます。そのため、代入先が不正/
不在で、例外が発生することは、あまりありません。しかし、例えば、
    A::B = 0;
の実行で、スコープ規定演算子 :: の左辺の箱 A が、不在、又は、スコープを持たない
箱の場合には、例外が発生します。
 このような代入先が不在/不正時に発生する例外に対処するために、「代替代入先」が
設定できるようになっています。代替代入先を設定しておけば、この例外発生時に、その
代入先の代わりに、代替代入先に設定されている代入先へ、その代入文の右辺の評価値が
代入されて、プログラムの実行が継続します。
 代替代入先は、DefDest という名前の箱に設定しておきます。この DefDest 箱の格納
スコープとその検索順は、前述の DefValue 箱の場合と同様です。
 DefDest 箱の中身が参照の場合、その参照先が、実際の代替代入先になります。また、
その中身が参照でない場合は、DefDest 箱自身が、代替代入先になります。
 次に、代替代入先の例を示します。
    A = 0;
    DefDest := A;       // 実際の代替代入先は、箱 A になる
    X::Y = 123;         // 代入先が不在/不正のため、例外が発生する
    print "DefDest=" : DefDest, "A=" : A;

    DefDest := null;    // 実際の代替代入先は、DefDest 自身になる
    X::Y = 456;         // 代入先が不在/不正のため、例外が発生する
    print "DefDest=" : DefDest, "A=" : A;
これを実行すると、次の通り、プリントされます。
    DefDest=123, A=123
    DefDest=456, A=123

 なお、代替代入先による例外処理を無効にするには、各スコープ内の DefDest 箱を、
削除します。これは、前述の DefValue 箱等の場合と同様です。

(3)代替関数
 次のどれかの実行で対象となる箱が不在/不正の時には、例外が発生します。
   ・関数コール(標準型、リレー型、代入型、コマンド型)
   ・クラスインスタンス生成
   ・メンバー関数コール
   ・純粋配列要素の読み出し
   ・DLL関数コール
この例外に対処するために、「代替関数」が設定できるようになっています。代替関数を
設定しておけば、このような例外発生時に、その不在/不正の箱に代わって、代替関数が
コールされて、プログラムの実行が継続します。
 代替関数の返値は、その不在/不正の箱に対する操作結果の代わりに使われます。この
返値のデータ型は任意です。代替関数は、DefFunc という名前の箱に設定しておきます。
この DefFunc 箱の格納スコープとその検索順は、前述の DefValue 箱の場合と同様です。

 次に、未定義関数を標準型とリレー型でコールした場合の代替関数の例を示します。
    DefFunc = function( x )  {  return ##代替関数: 引数=${x}##;  };
    print fff( 123 );
    print 456'ggg;
この実行では、最初の print 文内で関数 fff を標準型でコールしていますが、この関数
は存在しないので、DefFunc に設定されている関数が代わりにコールされます。そのため、
ここでは、「代替関数: 引数=123」とプリントされます。また、2番目の print 文内で、
関数 ggg をリレー型でコールしていますが、この関数もないので、代替関数が呼ばれて、
ここでは、「代替関数: 引数=456」とプリントされます。

 今度は、未定義関数のメンバー関数をコールした場合の代替関数の例を示します。
    ^DefFunc = function( v )  {  print "代替メンバー関数: " : .X, .Y, v;  };
    class C {  .X = 1;  .Y = 2;  }
    C.f( 3 );
この実行では、クラス C のメンバー関数 f がコールされていますが、この関数は未定義
なので、代替関数 DefFunc の関数が代わりにコールされます。この時、対象の関数 f の
引数( この場合 3 )とメンバースコープ( この場合、クラス C 内 )は、代替関数に
そのまま引き継がれます。そのため、この代替関数によってプリントされるのは、
    代替メンバー関数: 1, 2, 3
となります。

 本言語では、クラスインスタンス生成、メンバー関数コール、純粋配列要素の読み出し、
DLL関数コールは、標準型の関数コールと、表記上同じ書式になります。そのために、
これらを区別するには、その対象を特定する必要がありますが、その対象が不在/不正の
場合には、それができないので、そのエラー代替はどれも、代替関数のコールになります。

 代替関数による例外処理を無効にするには、各スコープ内の DefFunc 箱を削除します。
これは、DefValue 箱の場合と同様です。また、代替関数の場合には、DefFunc の中身が、
関数でなければ、代替関数による例外処理は行なわれません。そのため、例えば、
    DefFunc = null;
としても、代替関数による例外処理は無効になります。

(4)代替演算
 演算子を使う演算で、不正な演算が行なわれた場合に、例外が発生します。この例外に
対処するために、「代替演算関数」と「代替演算値」が設定できるようになっています。
代替演算関数を設定しておけば、不正演算の例外発生時、その関数が呼ばれて、その返値
が、その演算結果の代わりに使われます。また、代替演算値を設定しておけば、不正演算
の例外発生時、その演算結果の代わりにその代替演算値が使われます。いずれの場合でも、
その代替によって、プログラムの実行が継続します。代替演算関数と代替演算値の両方が
設定されている場合、代替演算関数の方が優先されます。

 不正演算に対するこのような代替演算が行なわれるのは、次の演算子に限られます。
  二項演算子  +  -  *  /  %  >  <  >=  <=  ==  !=  &&  ||  &  |  ^  <<  >>
         +=  -=  *=  /=  %=  &=  |=  ^=  <<=  >>=
  前置演算子  +  -  ~  ++  --
  後置演算子  ++  --
なお、この中に、真偽判定に関連した演算子がないのは、真偽判定演算では、どのような
オペランドでも不正としては扱われないからです。

 代替演算関数は、上記のどの演算子にも共通に適用される関数と、各演算子ごとに個別
に適用される関数があります。共通の代替演算関数は、DefOpFunc という名前の箱に設定
しておきます。一方、個別の代替演算関数は、DefOpF["op"] という名前の箱に設定して
おきます。但し、op は、各演算子の表記そのものになります。例えば、+ 演算子専用の
代替演算関数は、DefOpF["+"] となり、>> 演算子専用の代替演算関数は、DefOpF[">>"] 
となります。ある演算子に対して、共通と個別の両方の代替演算関数が設定されていると、
個別の代替演算関数の方が優先されます。代替演算関数の返値のデータ型は任意です。

 次に、代替演算関数の例を示します。
    DefOpF["+"] = function( a, b )
        {  return ##専用の代替演算関数: ${a} + ${b}##; };
    DefOpF["-"] = function( a, b )
        {  return ##専用の代替演算関数: ${a} - ${b}##; };
    DefOpFunc = function( a, b )
        {  return ##共通の代替演算関数: ${a} ${$err_info} ${b}##; };

    A = 1;   B.x = -1;   C = 0;
    print  A + "文字列";    // 不正演算: 数値 + 文字列
    print  A - null;        // 不正演算: 数値 - null
    print  A * B;           // 不正演算: 数値 * 複合箱
    print  A / C;           // 不正演算: 0除算
これを実行すると、次の通り、プリントされます。
    専用の代替演算関数: 1 + 文字列
    専用の代替演算関数: 1 - <null>
    共通の代替演算関数: 1 * <container>
    共通の代替演算関数: 1 / 0

 代替演算値には、上記のどの演算子にも共通に適用される値と、各演算子ごとに個別に
適用される値があります。共通の代替演算値は、DefOpValue という名前の箱に、個別の
代替演算値は、DefOpV["op"] という名前の箱に、設定しておきます。op は、各演算子の
表記そのものになります。例えば、+ 演算子専用の代替演算関数は、DefOpV["+"] となり、
>> 演算子専用の代替演算関数は、DefOpV[">>"] となります。ある演算子に対して、共通
と個別の両方の代替演算値が設定されていると、個別の代替演算値の方が優先されます。
代替演算値のデータ型は任意です。

 次に、代替演算値の例を示します。
    DefOpV["+"] = "+ 演算子専用の代替値";
    DefOpV["-"] = "- 演算子専用の代替値";
    DefOpValue  = "共通の代替演算値";

    A = 1;   B.x = -1;   C = 0;
    print  A + "文字列";    // 不正演算: 数値 + 文字列
    print  A - null;        // 不正演算: 数値 - null
    print  A * B;           // 不正演算: 数値 * 複合箱
    print  A / C;           // 不正演算: 0除算
これを実行すると、次の通り、プリントされます。
    + 演算子専用の代替値
    - 演算子専用の代替値
    共通の代替演算値
    共通の代替演算値

 なお、上記の代替演算関数や代替演算値の各箱を格納するスコープと、その検索順は、
前述の DefValue 箱の場合と同様です。また、代替演算関数や代替演算値の箱を削除する
ことによって、その箱に対応する各代替演算の例外処理を無効にできます。

(5)代替ラベル
 goto 文の実行時に、その行き先の可変ラベルが見つからない場合、例外が発生します。
また、call 文の実行では、その行き先の固定/可変ラベルが見つからない場合、例外が
発生します。それに対処するために、「代替ラベル」が設定できるようになっています。
代替ラベルを設定しておけば、このような例外が発生した時、その不在ラベルの代わりに
代替ラベルが使用されて、プログラムの実行が継続します。

 代替ラベルは、goto 文の場合、DefLabel という名前を、call 文の場合、DefSubr と
いう名前を使います。これらは、固定ラベルとして使用することもできますし、ラベル名
を格納するラベル変数としても使えます。ラベル変数の場合、その中身の文字列が、実際
の代替対象のラベル名になります。固定ラベルの代替ラベルと、ラベル変数の代替ラベル
の両方がある場合、ラベル変数の方が優先されます。

 goto 文の実行で例外が発生した時は、まず、DefLabel がラベル変数として設定されて
いるかどうか確認されます。つまり、DefLabel という名前の箱が、前述の DefValue 箱
の場合と同様の各スコープで検索されます。もし、その箱が見つかり、その中身が文字列
であり、その文字列のラベル名が、現実行中の関数内にあれば、制御はそのラベルへ移行
します。そのような箱が、各スコープのどこにも見つからなければ、今度は、DefLabel 
が固定ラベルとして設定されているかどうかが確認されます。つまり、DefLabel という
名前のラベルが、現実行中の関数内にあれば、制御はそのラベルへ移行します。

 call 文の実行で例外が発生した時も、goto 文の場合とほぼ同じですが、DefLabel で
はなく、DefSubr が対象になります。また、call 文の場合、サブルーチンのラベル名が、
現実行中の関数内で見つからない場合には、現実行中の関数が属するモジュール内の暗黙
のメイン関数内も検索されます。

 固定ラベルの DefLabel と DefSubr は、プログラムの実行中には変更できませんが、
ラベル変数の DefLabel と DefSubr は、プログラムの実行中に、その中身を書き換える
ことができます。また、それを削除する、あるいは、ラベル名に該当しないデータを設定
することによって、ラベル変数によるラベル代替を、無効にすることもできます。

 次に、DefLabel を使った例を示します。
    i = 0;
  LOOP:   goto Entry[ ++i ];

  Entry[1]:     print "春";   goto LOOP;
  Entry["秋"]:  print "秋";   delete DefLabel;   goto LOOP;
  END:          return;

  DefLabel:
    switch( i )
    {
      case 2:   print "夏";   DefLabel = :Entry["秋"];   break;
      case 4:   print "冬";   DefLabel = :END;           break;
    }
    goto LOOP;
これを実行すると、次の通り、プリントされます。
    春
    夏
    秋
    冬

ここで、「春」をプリントする所へは、例外の発生なしにジャンプします。「夏」と「冬」
をプリントする所へは、固定ラベルの DefLabel が代替ラベルとなってジャンプします。
「秋」をプリントする所と END ラベルへは、ラベル変数の DefLabel の中身の文字列が
代替ラベルとなってジャンプします。

 次に、DefSubr を使った例を示します。
    for( i = 1 ; i <= 4 ; i++ )
        call Entry[ i ];
    return;

  Entry[1]:     print "春";   back;
  Entry["秋"]:  print "秋";   delete DefSubr;   back;

  DefSubr:
    switch( i )
    {
      case 2:   print "夏";   DefSubr = :Entry["秋"];   break;
      case 4:   print "冬";   DefSubr = :END;           break;
    }
    back;
これを実行すると、前と同じ内容が、プリントされます。ここで、「春」をプリントする
サブルーチンへは、例外の発生なしに来ます。「夏」「冬」をプリントするサブルーチン
へは、固定ラベルの DefSubr が代替ラベルとなって来ます。「秋」をプリントするサブ
ルーチンへは、ラベル変数の DefSubr の中身の文字列が代替ラベルとなって来ます。

●エラーワープ
 既に述べたように、プログラム実行時にエラーが発生した時には、まず、エラー情報が
設定されて、次に、エラー代替が確認され、それが設定されていれば、その処理に移行し
ますが、エラー代替が設定されていなければ、「エラーワープ」が確認されます。その際、
に、エラーワープが設定されていれば、そのワープ先に制御が移行します。エラーワープ
が設定されていなければ、そのスレッドの実行は異常終了します。

 エラーワープ先の設定には、「固定ラベル」と「ラベル変数」のどちらか、又は、その
両方が使えます。これには、次のように、例外処理の各場合に応じて、各々特別な名前が
割り当てられています。同じ例外処理の場合には、この両者の名前は同じです。
 OnError        全エラー共通(但し、以下の個別エラーワープの設定の方が優先)
 OnValueError   不正読出値の例外処理で、代替値 DefValue がない場合
 OnDestError    不正代入先の例外処理で、代替代入先 DefDest がない場合
 OnFuncError    不正関数コールの例外処理で、代替関数 DefFunc がない場合
 OnOpError      不正演算の例外処理で、その代替演算がない場合(全演算共通)
 OnOpErr["op"]  不正演算の例外処理で、その代替演算がない場合(各演算個別)
                 ( 左記の op は、各演算子の表記そのものになります。)
 OnLabelError   不正行先ラベルの例外処理で、代替ラベル DefLabel がない場合
 OnSubrError    不正サブルーチンの例外処理で、代替ラベル DefSubr がない場合
 OnFileError    ファイル(バッファ)関連のエラーが発生した場合
 OnFileEnd      ファイル(バッファ)の読み出し等でその終端を検知した場合
 エラーワープは、システムが例外処理時に自動的に行なうワープですが、この動作は、
ユーザーが「warp 文」で行なうワープと、基本的に同様です。例えば、上記の OnError 
のエラーワープでは、システムが行なうのと、ユーザーが、
    warp OnError;
を実行するのと、基本的に同様の動作になります。このワープの動作の詳細に関しては、
「制御の流れ(ジャンプ系)」の章の「warp 文」の節で述べています。そのため、ここ
では、繰り返しになる部分の説明を省略しています。

 エラーワープ状態フラグ $err_state は、エラーワープが起動された時に、整数値 1 
に設定されます。それ以外の時は、この変数に対して何も行なわれないので、この変数は、
存在していないか、または、前の値のままです。この変数が存在してその値が 0 以外の
整数値の時には、エラーワープは決して起動されません。これによって、エラーワープの
二重起動を防止しています。エラーワープが起動されてから、再度起動されてもよい状態
になった時には、この変数の値を整数値 0 に設定するか、または、この変数自身を削除
する必要があります。

 次の文のブロック内で、エラーワープが起動されると、そのブロックを抜けたことに
なります。
  ・scope 文
  ・構造体設定文
  ・クラス設定文
また、これらのブロック内に、エラーワープ先のラベルを設定することはできません。

(例1)全エラー共通対応の固定ラベル OnError へのエラーワープ
    A = B;  // ここで例外発生 → 固定ラベルの OnError へエラーワープ
    print "ここには来ない!";
    return;

  OnError:      // エラーワープのエントリー
    print "固定ラベルへエラーワープしました!";
    return;
これを実行すると、「固定ラベルへエラーワープしました!」とプリントされます。なお、
「ここには来ない!」はプリントされません。

(例2)全エラー共通対応のラベル変数 OnError へのエラーワープ
    OnError = :ErrorHandler;    // エラーワープ先のラベルをラベル変数に設定
    A = B;  // ここで例外発生 → ラベル変数の中身のラベルへエラーワープ
    print "ここには来ない!";
    return;

  ErrorHandler:     // エラーワープのエントリー
    print "ラベル変数の中身のラベルへエラーワープしました!";
    return;

  OnError:         // エラーワープのエントリー
    print "固定ラベルへエラーワープしました!";
    return;
これを実行すると、「ラベル変数の中身のラベルへエラーワープしました!」とプリント
されます。なお、「ここには来ない!」はプリントされません。また、ラベル変数の方が
固定ラベルよりも優先されるので、「固定ラベルへエラーワープしました!」はプリント
されません。

(例3)コール先の関数内で例外発生 → 固定ラベルへのエラーワープ
    F1();   // このコール先の関数内でコールした関数内で例外が発生する
    print "ここには来ない!";
    return;

  OnError:
    print "固定ラベルへのエラーワープ!";
    return;

    function F1()
    {
        F2();   // このコール先の関数内で例外が発生
        print "F1: ここには来ない!";
    }

    function F2()  {  A = B;  }   // ここで例外発生!
これを実行すると、「固定ラベルへのエラーワープ!」だけがプリントされます。その他
がプリントされることはありません。

(例4)コール先の関数内で例外発生 → 変数ラベルによるエラーワープ
    OnError = :MainErrorHandler;    // エラーワープ先のラベルをラベル変数に設定
    F1();       // このコール先の関数内とそのコール先の関数内で例外が発生する
    return;     // ここに来ることはない

  MainErrorHandler:     // ラベル変数によるエラーワープのエントリー
    print "Main: ラベル変数によるエラーワープ!";
    return;

  OnError:      //(ラベル変数の方が優先のためここには来ない)
    print "Main; 固定ラベルによるエラーワープ!";
    return;

    function F1()
    {
        OnError = :F1_ErrorHandler;     // エラーワープ先をラベル変数に設定
        F2();     // このコール先の関数内で例外が発生する
        return;   // ここに来ることはない

      F1_ErrorHandler:   // ラベル変数によるエラーワープのエントリー
        print "F1: ラベル変数によるエラーワープ!";
        delete OnError;   // ラベル変数によるエラーワープ禁止
        $err_state = 0;   // エラーワープを再起動可に
        F2();     // このコール先の関数内で例外が発生する
    }

    function F2()  {  A = B;  }   // ここで例外発生!
これを実行すると、次の通り、プリントされます。
    F1: ラベル変数によるエラーワープ!
    Main: ラベル変数によるエラーワープ!

(例5)各個別エラー対応の固定ラベルへのエラーワープ
    for( i = 1 ; i <= 7 ; i++ )
    {
        switch( i )
        {
          case 1:  print A;          // 読み出し対象の箱がない → 例外発生
          case 2:  X::Y = 1;         // 代入先が不在/不正 → 例外発生
          case 3:  print f(0);       // コール先の関数がない → 例外発生
          case 4:  print  i + "abc"; // 不正演算 → 例外発生
          case 5:  print  i - null;  // 不正演算 → 例外発生
          case 6:  goto Entry[i];    // 行先のラベルがない → 例外発生
          case 7:  call Entry[i];    // コール先のサブルーチンがない → 例外発生
        }
      NEXT:
        $err_state = 0;   // エラーワープを再起動可に
    }
    print "プログラム終了";
    return;

    OnValueError:   print i: ": OnValueError ...";    goto NEXT;
    OnDestError:    print i: ": OnDestError  ...";    goto NEXT;
    OnFuncError:    print i: ": OnFuncError  ...";    goto NEXT;
    OnOpError:      print i: ": OnOpError    ...";    goto NEXT;
    OnOpErr["-"]:   print i: ": OnOpErr[\"-\"] ...";  goto NEXT;
    OnLabelError:   print i: ": OnLabelError ...";    goto NEXT;
    OnSubrError:    print i: ": OnSubrError  ...";    goto NEXT;
これを実行すると、次の通り、プリントされます。
    1: OnValueError ...
    2: OnDestError  ...
    3: OnFuncError  ...
    4: OnOpError    ...
    5: OnOpErr["-"] ...
    6: OnLabelError ...
    7: OnSubrError  ...
    プログラム終了

(例6)各個別エラー対応のラベル変数へのエラーワープ
    OnValueError = :L1;
    OnDestError  = :L2;
    OnFuncError  = :L3;
    OnOpError    = :L4;
    OnOpErr["-"] = :L5;
    OnLabelError = :L6;
    OnSubrError  = :L7;

    for( i = 1 ; i <= 7 ; i++ )
    {
        switch( i )
        {
          case 1:  print A;          // 読み出し対象の箱がない → 例外発生
          case 2:  X::Y = 1;         // 代入先が不在/不正 → 例外発生
          case 3:  print f(0);       // コール先の関数がない → 例外発生
          case 4:  print  i + "abc"; // 不正演算 → 例外発生
          case 5:  print  i - null;  // 不正演算 → 例外発生
          case 6:  goto Entry[i];    // 行先のラベルがない → 例外発生
          case 7:  call Entry[i];    // コール先のサブルーチンがない → 例外発生
        }
      NEXT:
        $err_state = 0;   // エラーワープを再起動可に
    }
    print "プログラム終了";
    return;

    L1:  print i: ": OnValueError ...";    goto NEXT;
    L2:  print i: ": OnDestError  ...";    goto NEXT;
    L3:  print i: ": OnFuncError  ...";    goto NEXT;
    L4:  print i: ": OnOpError    ...";    goto NEXT;
    L5:  print i: ": OnOpErr[\"-\"] ...";  goto NEXT;
    L6:  print i: ": OnLabelError ...";    goto NEXT;
    L7:  print i: ": OnSubrError  ...";    goto NEXT;
これを実行すると、(例5)と同じ内容が、プリントされます。

(例7)エラーワープによるクラスインスタンスの自動破棄
    class ^C
    {
        function Construct( v ) {  print "構築: V=" : .V = v;  }
        function Destruct()     {  print "破棄: V=" : .V; }
    }

    F1();   //  このコール先の関数内でコールした関数内で例外が発生する
    print "ここには来ない!";
    return;

  OnError:
    print "Main: 固定ラベルによるエラーワープ!";
    return;

    function F1()
    {
        C1 = ^C( 1 );
        C2 = ^C( 2 );
        print "F1: -----------";
        F2();   // このコール先の関数内で例外が発生する
                // この時のエラーワープで、インスタンス C1, C2 は破棄される
        print "F1: ここには来ない!";
    }

    function F2()
    {
        A = B;  // ここで例外発生 → メイン関数の OnError へエラーワープ
    }
これを実行すると、次の通り、プリントされます。
    構築: V=1
    構築: V=2
    F1: -----------
    破棄: V=2
    破棄: V=1
    Main: 固定ラベルによるエラーワープ!

(例8)クラス設定ブロック内での例外発生 → エラーワープ
    class ^C
    {
        .X = 1;
        .Y = 2;
        .Z = A;  // ここで例外発生 → 固定ラベルの OnError へエラーワープ
    }
    print "ここには来ない!";
    return;

  OnError:
    // ここにエラーワープで来た時は、クラス設定ブロックを抜けた状態になっている
    do ^C'each with v {  print v'name : "=" : v;  };
    return;
これを実行すると、次の通り、プリントされます。
    X=1
    Y=2

●システム管理エラー処理のまとめ
 今までに述べた、本言語システムが管理するエラー代替とエラーワープを、次の表に
まとめて示します。

 エラー代替   エラーワープ先   エラーの種類 
 (なし)  OnError  全エラー共通
 DefValue  OnValueError  読み出し対象の箱が不在/不正
 DefDest  OnDestError  代入先が不在/不正
 DefFunc  OnFuncError  関数コール先が不在/不正
 DefOpFunc
 DefOpValue
 OnOpError  不正演算(全演算子共通)
 DefOpV["op"]
 DefOpF["op"]
 OnOpErr["op"] 不正演算(各演算子個別)
 DefLabel  OnLabelError  行き先のラベルが不在/不正
 DefSubr  OnSubrError  サブルーチンコールのラベルが不在/不正 
 (なし)  OnFileError  ファイル関連エラー
 (なし)  OnFileEnd  ファイル終端検知
●ユーザー独自の例外処理  例外処理には、いろいろな形態がありますが、本言語では、ワープによって、C++ 言語 等の try 〜 throw 〜 catch ような処理が行なえるようになっています。なお、ワープ については、「制御の流れ(ジャンプ系)」の章の「warp 文」の節で説明しています。  ワープによる例外処理は、基本的に、前述のエラーワープによる例外処理と同様ですが、 ユーザー独自に任意の行先へワープを起動できます。その際、その例外に関するデータを、 スレッドローカルスコープ内等に、格納しておくこともできます。  例外処理でのワープ先は通常、その例外処理のエントリーになります。このエントリー は、固定ラベルにすることもできますが、ラベル変数にしておけば、各種のタイミングで、 動的に変更できます。また、その有効化/無効化も、動的に切り換えることができます。  このように、ワープによる例外処理は、try 〜 throw 〜 catch による例外処理と比較 して、簡素で柔軟性がありますが、ラベルジャンプに対する悪評を盲信している人には、 抵抗があるかもしれません。  ワープの例は、関係各所で示していますが、以下では、例外処理でのワープの使い方の 基本となる例を示します。ここでは、SomeFunc という関数の実行時に、例外処理のため のワープが起動されます。この関数を、F1, F2, F3 の各関数から呼んでいますが、その 際に各種の方法で、その例外処理のエントリーを設定しています。
    $OnException = :MainExceptionEntry;
    F1();
    F2();
    F3();
    return;

  MainExceptionEntry:
    print "MainExceptionEntryに来ました! --- 例外データ=" : $ExceptionData;
    return;

    function F1()
    {
        SomeFunc( "F1" );   // このコール先で OnException へのワープ起動
        return;

      OnException:    // 固定ラベルの例外処理エントリー
        print "F1_ExceptionEntry に来ました! --- 例外データ=" : $ExceptionData;
    }

    function F2()
    {
        OnException = :F2_ExceptionEntry;
        SomeFunc( "F2" );   // このコール先で OnException へのワープ起動
        return;

      F2_ExceptionEntry:  // ラベル変数 OnException の示す例外処理エントリー
        print "F2_ExceptionEntry に来ました! --- 例外データ=" : $ExceptionData;
    }

    function F3()
    {
        SomeFunc( "F3" );   // このコール先で OnException へのワープ起動
            // この関数内には、このワープに対するエントリーがないので、
            // ワープ先は、ラベル変数 $OnException の示すエントリーになる
        print "ここには来ない!";
        return;
    }

    function SomeFunc( v )
    {
        $ExceptionData = v;     // 例外データを設定
        warp OnException;       // 例外処理のワープ起動
    }
このプログラムを実行すると、次の通り、プリントされます。
    F1_ExceptionEntry に来ました! --- 例外データ=F1
    F2_ExceptionEntry に来ました! --- 例外データ=F2
    MainExceptionEntryに来ました! --- 例外データ=F3

●補足説明
 try 〜 throw 〜 catch による例外処理は、本言語の例外処理と比較して、以下の短所
があります。
  ・代替処理ができない
  ・throw された例外が catch される場所を動的に変更できない
  ・例外処理の有効化/無効化のタイミングを動的に切り換えられない
  ・例外処理の対象になる範囲を動的に変更できない

 一方、本言語の例外処理では、try 〜 throw 〜 catch による例外処理のように、送出
された例外オブジェクトのクラス別の捕捉はできません。また、ラベルの使用が必須です。