[前へ] [目次へ] [次へ]

制御文(分岐・ループなど)


FGESの制御文はメソッド定義空間にのみ記述できます。
C/C++の構文がベースとなっており、多くの部分で似たような記述が可能です。

●ブロックの定義
●ローカル変数の定義(var)
●局所クラスの定義(class)
●局所メソッドの定義(method)
●クロージャの定義(closure)
●初期化(#Init)
●分岐(if/elif/else/switch)
●ループ(for/while/do)
●ジャンプ(break/continue/retry)
●復帰(return/retval/retref)
●中断(pause/debug_break)
●中断可能宣言(haltmode)
●例外(throw)


●ブロックの定義

   FGESにおけるブロックの扱いは概ねC/C++と同様で、 { で始まり } で終わります。
   また、ローカルスコープの区切りや分岐、ループの範囲を示すことに使用されます。
   メソッドの定義を意味する { } もブロックであるため、
   メソッドは最低一つのブロックを持つことになります。
   また、このブロックには引数オブジェクトとしてローカル変数 arglist が暗黙に定義されるため、
   ローカルスコープも定義された状態になっています。
   なお、 arglist の型は Arguments で、呼び出しに使用された引数が参照代入されて格納されています。
   格納されている要素は引数オブジェクト自体を示しており、保護のため全て読み取り専用属性になっています。
   編集可能属性の参照が引数として渡されている場合、 System::GetArg を使用して編集可能属性の参照を取得できます。
   可変長引数はこの配列を通じて読み取りますが、引数なしの呼び出しが可能である場合、この変数は空参照になっている可能性があります。

   例:
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
   method void Sample(){//←この{もブロックとして扱います
      {//単体でも定義できます
         
if(1){//分岐などの範囲指定にも使用します
         }
      }
   } 


●ローカル変数の定義(var)

   変数定義の構文については 変数 の項目を参照してください。

   定義したブロックのローカルスコープにローカル変数を定義します。
   メソッド定義空間への記述に限り、 var で始めなくても文の最初が変数宣言における属性か、
   グローバル名前空間に定義されたクラス名であればローカル変数の定義とみなされます。
   
   定義したブロックがローカル変数を一つも持っておらず、
   ローカルスコープが導入されていない場合はローカルスコープが導入されます。
   このことは「現在のローカルスコープ」に定義するがローカルスコープの導入をしない
   式中の局所クラス、無名メソッド、クロージャなどのスコープ範囲や寿命に関わります。
   (注:制御文としての局所クラスや局所メソッド、クロージャの定義はスコープの導入を行います)
   
   C/C++と異なり、別のローカルスコープであっても可視であるローカル変数と同じ名前は使用できません。
   また、制御文の対象文や switch ブロックではローカル変数を定義できません。
   例:
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
   method void Sample(){//ブロックA
      //引数がなくても引数オブジェクトarglistは定義されており、
      //ローカルスコープも導入されています。
      //ただし、引数がない場合arglistは空参照である可能性があります。
      
      
int a;//varで始まらなくてもintは型名として解釈できるのでローカル変数定義となります。
      
      {
//ブロックB
         
a=5;//ネストされたブロックの中からでも参照できます。
      }
      {
//ブロックC
         //int a;//異なるブロック/ローカルスコープですが、可視の名前であるaは定義できません。
         
int b;//これはブロックCのローカルスコープに所属し、ブロックCの終点で消滅します。
      }
      
//b=5;//既に変数bは消滅しているのでエラーになります。
      {//ブロックD
         
int b;//これはブロックDのローカルスコープに所属し、ブロックDの終点で消滅します。
         //同名の変数がブロックCにありますが、ブロックCはここでは不可視なので定義できます。
      }
   } 

●局所クラスの定義(class)

   定義したブロックのローカルスコープに局所クラスを定義します。
   定義したブロックがローカル変数を一つも持っておらず、
   ローカルスコープが導入されていない場合はローカルスコープが導入されます。
   
   定義した局所クラスのクラスオブジェクトは定義した位置にハードコードされ、
   定義したメソッドのメソッドオブジェクトと同じメモリ管理オブジェクトに接続されます。
   
   構文は クラス定義 の構文と同じです。

●局所メソッドの定義(method)

   定義したブロックのローカルスコープに局所メソッドを定義します。
   定義したブロックがローカル変数を一つも持っておらず、
   ローカルスコープが導入されていない場合はローカルスコープが導入されます。
   
   定義した局所メソッドのメソッドオブジェクトは定義した位置にハードコードされ、
   定義したメソッドのメソッドオブジェクトと同じメモリ管理オブジェクトに接続されます。
   
   構文は メソッド定義 の構文と同じですが、今のところ使用できるデフォルト引数は空参照のみです。

   局所メソッドは原則として定義したメソッドと同じクラスを対象にコンパイルされます。
   ただし、メソッド宣言で名前部分にクラス名を記述した場合、そのクラスを対象にコンパイルされます。
   
   局所メソッドは式を評価しているメソッドとは独立しているので、
   外側のメソッドのローカル名前空間にはアクセスできません。
   

●クロージャの定義(closure)

   定義したブロックのローカルスコープにクロージャを定義します。
   定義したブロックがローカル変数を一つも持っておらず、
   ローカルスコープが導入されていない場合はローカルスコープが導入されます。
   
   定義したクロージャのメソッドオブジェクトは定義した位置にハードコードされ、
   定義したメソッドのメソッドオブジェクトと同じメモリ管理オブジェクトに接続されます。
   そしてこの制御文を実行する度にクロージャのメソッドオブジェクトと
   実行時のローカルスコープを結合したクロージャオブジェクトが生成されローカル変数として生成されます。
   なお、クロージャオブジェクトは結合したローカルスコープ寿命となります。
   
   構文は method ではなく closure で始めること以外、 メソッド定義 の構文と同じですが、今のところ使用できるデフォルト引数は空参照のみです。

   クロージャは定義したメソッドと同じクラスを対象にコンパイルされます。
   クロージャは結合しているローカルスコープもローカル名前空間として扱うことができますが、
   クロージャオブジェクトを生成したスレッド以外では実行できません。
   

●初期化(#Init)

   初期化文は、プロセス内で初めて文が実行される時、一度だけ実行されます。
   2回目以降に初期化文を実行した場合は、 else 節の対象文が実行されます。
   
   「対象文」がブロックである場合、ブロック全体を対象として扱います。
   また、 else は不要な場合は省略できます。
   構文:
   #Init 対象文
   else 対象文


   例: val,val2,val3 は最初に呼び出した時は1、2回目以降は2になります。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
   method void Sample(){
      
int val,val2,val3;
      
      
#Init val=1;
      
else val=2;
      
      
#Init{
         val2=1;
         val3=1;
      }
      
else{
         val2=2;
         val3=2;
      }
   } 

●分岐(if/elif/else/switch)

   FGESの条件分岐は if/elif/else と switch の二種類です。

●if/elif/else
   基本となる条件分岐で、基本的にC/C++と同様です。
   「条件式」の結果はフラグレジスタか、 INativeBool をサポートするオブジェクトである必要があります。
   if/elif の「対象文」は「条件式」が真の場合だけ実行されます。
   また、「対象文」がブロックである場合、ブロック全体を対象として扱います。
   
   elif はC/C++でいう else if に相当し、複数記述できます。なお、 else if 形式でも記述できます。
   elif/else は対応する if/elif の「条件式」が偽になった場合だけ評価されます。
   また、 elif/else は不要な場合は省略できます。
   
   構文:
   if( 条件式 ) 対象文
   elif( 条件式 ) 対象文
   else 対象文


   例: ret,ret2,ret3 は全て100になる
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
   method void Sample(){
      
int n=10,ret,ret2,ret3;
      
      
if(n<0)ret=10;
      
elif(n<50)ret=100;
      
else if(n<100)ret=200;
      
else ret=300;
      
      
if(n<0){
         ret2=10;
         ret3=10;
      }
      
elif(n<50){
         ret2=100;
         ret3=100;
      }
      
else if(n<100){
         ret2=200;
         ret3=200;
      }
      
else{
         ret2=300;
         ret3=300;
      }
   } 

●switch
   一つの式の結果を使って複数の分岐条件を設定する条件分岐です。
   分岐の判定のため、「比較対象式」の結果オブジェクトは #operator== を定義している型である必要があります。

   分岐条件の設定には case を使用しますが、
   C/C++同様、 case はフォールスルー(直前の条件の終了を意味せず、貫通実行される)します。
   switch ブロックの実行を中断するには break を使用します。

   また、C/C++とは異なり、 case でも式が使用できます。
   各 case の「比較式」は記述順に評価され、結果を「比較対象式」の結果オブジェクトの #operator== の右辺値に使用して判定し、
   最初に真が返された case が分岐先に採用されます。
   この時、「比較式」の先頭が二項比較演算子 (==,!=,<,>,<=,>=) 、AND演算子 (&) 、メンバ参照演算子 (.) の
   いずれかであった場合、上記の手順ではなく、「比較対象式」の結果を左辺として当該演算子を適用します。
   なお、メンバ参照演算子を適用する場合、式の結果を INativeBool で評価できなければいけません。
   
   全ての case が偽になった場合、定義されていれば default が分岐先に採用されます。
   全ての case が偽になり、 default が定義されていない場合は switch ブロックの終点にジャンプします。

   構文:
   switch( 比較対象式 ){
   case 比較式 :
   default:
   }


   例: ret は10になり、 ret2 は100になる
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
   method void Sample(){
      
int n=10,ret,ret2;
      
      
switch(n){
      
case 0:
         ret=0;
         
break;
      
case 10:
         ret=10;
//breakしないので次も実行される
      
case 100:
         ret2=100;
         
break;
      
case <50://これより上のcaseを満たさず、n<50が成立すればこれが採用される
         
ret2=50;
         
break;
      
case .IsInRange(60,70)://これより上のcaseを満たさず、n.IsInRange(60,70)が真を返せばこれが採用される
         
ret2=50;
         
break;
      
default:
         ret2=1000;
         
break;
      }
   } 

●ループ(for/while/do)

   「継続条件式」の結果が真である間、「対象文」を繰り返します。
   「継続条件式」の結果はフラグレジスタか、 INativeBool をサポートするオブジェクトである必要があります。
   do for および do while 書式では初回の「継続条件式」が評価されず、「対象文」は最低一回は実行されます。
   for および do for の「初期化式」はループの開始前に一度だけ実行されます。
   また、「継続式」は「対象文」を最後まで実行した場合、または continue で戻った場合のみ実行されます。
   また、「対象文」がブロックである場合、ブロック全体を対象として扱います。

   「初期化式」、「継続式」が空の場合は何も実行されず、「継続条件式」が空の場合は常に真として評価されます。
   また、「初期化式」、「継続式」は , 区切りで複数の式を記述できます。

   構文:
   for( 初期化式 ; 継続条件式 ; 継続式 ) 対象文
   while( 継続条件式 ) 対象文
   do for( 初期化式 ; 継続条件式 ; 継続式 ) 対象文
   do while( 継続条件式 ) 対象文


   例:
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
   method void Sample(){
      
int i;
      
int[] arr;
      
      
for(i=0;i<5;i++)arr.Push()=i;
      
//arrの状態:[0,1,2,3,4]
      
while(i<10){
         arr.Push()=i;
         i++;
      }
      
//arrの状態:[0,1,2,3,4,5,6,7,8,9]
      
do for(i=100;i<5;i++)arr.Push()=i;
      
//arrの状態:[0,1,2,3,4,5,6,7,8,9,100]
      
do while(i<10){
         arr.Push()=i;
         i++;
      }
      
//arrの状態:[0,1,2,3,4,5,6,7,8,9,100,101]
   } 

●ジャンプ(break/continue/retry)

   対象となる制御文を脱出、またはループ位置にジャンプします。
   ジャンプできるのは同一メソッド内だけです。
   これらの文でメソッドやクロージャを越えてジャンプすることはできません。
   
●break
   記述位置に近い対象の制御文( switch/for/while/do for/do while )を脱出し、
   その次から実行を再開させます。
   「段数」に1〜9を指定することで、近い順に数えて指定した番号の制御文を脱出対象にします。
   「段数」を省略した場合は1として扱います。

   構文:
   break;
   break 段数;


   例:
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
   method void Sample(){
      
do while(0){
         
if(1){
            
while(1){
               
while(1)break 2;//この位置でbreakした場合
               //break; break 1;であればここから再開
            }
            
//break 2;であればここから再開
         }
         
//ifは対象ではないのでここでは止まらない
      }
      
//break 3;であればここから再開
   } 

●continue/retry
   記述位置に近い対象のループ文( for/while/do for/do while )のループ処理にジャンプします。
   「段数」に1〜9を指定することで、近い順に数えて指定した番号のループ文をジャンプ対象にします。
   「段数」を省略した場合は1として扱います。
   「段数」に2以上を指定した場合、内側のループは無条件に脱出します。
   
   continue は for/do for の継続式から、 while/do while は継続条件式から実行を再開します。
   retry は継続条件式から実行を再開します。 while/do while では retry と continue の動作は同じです。
   いずれも継続条件式が偽になった場合はループを終了します。

   構文:
   continue;
   continue 段数;
   retry;
   retry 段数;


   例:
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
   method void Sample(){
      
do while(0){//continue 3;であればここから再開
         
if(1){//ifは対象ではないのでここでは止まらない
            
while(1){//continue 2;であればここから再開
               
while(1){//continue; continue 1;であればここから再開
                  
continue 3;//この位置でcontinueした場合
               }
            }
         }
      }
      
      
int i;
      
for(i=0;i<10;i++){
         
if(1)continue;//continueは継続式を実行するのでループは10回で終了します
         
else retry;//retryは継続式を実行しないのでこの状態では無限ループになります
      }
   } 

●復帰(return/retval/retref)

   現在実行中のメソッドを終了して呼び出し元に復帰します。
   使用する文はメソッドの戻り値型によって決定されます。
   戻り値型が void であれば return 、値返しであれば retval 、参照返しであれば retref を使用します。
   戻り値があるメソッドでは、メソッドは retval または retref で終わらなければいけません。
   戻り値の扱いについては メソッド#戻り値 の項も参照してください。

   構文:
   return;
   retval 対象式 ;
   retref 対象式 ;


   例:
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
   method void Sample1(){
      
return;
   }
   
method int Sample2(){
      
retval 5;//値返しでは定数やローカル変数、一時オブジェクトを使用できます
   }
   
method readonly int@ Sample3(int v){
      
int i;
      
//retref i;//ローカル変数を参照返しできません
      //retref 0;//定数は一時オブジェクトになります。一時オブジェクトを参照返しできません
      
retref v;
   } 

●中断(pause/debug_break)

●pause
   この文を実行したFGESスレッドのタイムスライスを解放し、他のFGESスレッドに再割り当てします。
   この時、優先度の低いFGESスレッドでもタイムスライスを受け取る場合があります。
   高優先度のFGESスレッドがアクティブだからといって、
   低優先度のFGESスレッドが全く動作しないことを期待してはいけません。
   
   この文はネイティブでいうところの sleep(0) と考えて構いません。
   この文を実行するとFGESスレッドが切り替わり、次にタイムスライスが割り当てられるまで、
   FGESスレッドの実行が中断されます。
   
   「条件」は整数定数の10進表記のみが認められ、
   実行した時の残りタイムスライスが指定値以下の場合のみ pause が実行されます。
   また「条件」で pause した場合、次にタイムスライスが割り当てられた場合にも再評価されます。
   環境によってはタイムスライスは最大10000程度の値しか与えられないため、
   大きな値を条件にした場合はスレッドが永久停止する可能性があります。
   
   「条件」構文は極めて短い時間のみが必要なクリティカルセクションを手軽に扱うことができます。
   
   構文:
   pause;
   pause time< 条件 ;


●debug_break
   この文を実行した位置でFGES VMを一時停止し、利用可能なデバッガを呼び出します。
   実行しているFGES VMでデバッガが有効化されていない場合は無視されます。

   構文:
   debug_break;

●中断可能宣言(haltmode)

   この文を実行したメソッドの中断可能属性を変更します。
   詳細は スレッド の項目を参照してください。

●例外(throw)

   この文を実行するとプログラム定義例外が投入されます。
   FGESの例外は続行不可能な致命的な事態を通知するもので、全てFGES VMが捕捉します。
   多くの言語であるような例外捕捉からの回復といったコードは使用できません。
   
   コンソール版FGESではVMが例外を捕捉すると例外の内容と、
   例外が発生したスレッドの呼び出し履歴が表示され、異常終了します。
   
   「説明式」の結果は例外のエラーメッセージとして扱われます。
   「説明式」の結果は INativeString をサポートするオブジェクトである必要があります。
   
   構文:
   throw 説明式;

   例:
<  1>
<  2>
<  3>
   method void Sample(){
      
throw 'エラーメッセージ';
   } 

[前へ] [目次へ] [次へ]

最終更新 2022/05/30