Kuina-chan

くいなちゃん2018年12月11日


プログラミング言語Kuin」の言語仕様5、文とブロックについてです。

1Kuinの文とブロック

Kuinで関数の中に書くことができる文とブロックは、概ね表1-1の構造をしています。
表1-1: 文とブロック

文の名前 文の中身

ブロック名 識別子()
    ブロックの中身
end ブロック名

例えば図1-1のように、変数定義は文、関数定義はブロックの構造になっています。
  1. var n: int
  2.  
  3. func f(x: float)
  4.   var y: float
  5. end func
図1-1: 変数定義と関数定義
文は1行で書かれ、ブロックは複数行に渡って書かれるという違いがあります。 ブロックの中にはさらに文やブロックを入れることができます。
Kuinにおける文とブロックは表1-2の通りです。
表1-2: Kuinの文とブロック
名前 分類 説明
alias 型の別名定義
assert アサーション
break ブロックを抜ける
const 定数定義
do 式の実行
ret 関数を抜ける
skip ループを1回飛ばす
throw 例外の発生
var 変数定義
block ブロック 基本ブロック
class ブロック クラス定義
enum ブロック 列挙型定義
for ブロック 回数ループ
func ブロック 関数定義
if ブロック 条件分岐
switch ブロック 値による分岐
try ブロック 例外処理
while ブロック 条件ループ
それぞれの文とブロックについて、以下で詳しく説明します。
設計の理由

ブロック終端の「end ブロック名」でブロック名を指定させている理由は、C言語の「}」やPascalの「end」などのブロック終端記号が並んだときにどのブロックに対応するものかが解りにくくなるという可読性上の理由です。

コンパイル時の型チェックと同様に、ユーザが意図したコードになっているかをなるべくコンパイラがチェックしたほうがバグが減らせるため、多少冗長になってもブロック名を指定させて、コンパイル時に対応関係をチェックするようにしました。

2alias文

「alias文」は既存の型に別名を付けて新しい型を定義します(表2-1)。
表2-1: alias文の構文

alias 新しい型名: 既存の型名

alias文は、関数の外のグローバルな領域や、クラス内にも記述できます。

3assert文

「assert文」は条件を満たさなかった場合、デバッグ実行時に例外(E9170000)を発生させる文です(表3-1)。
表3-1: assert文の構文

assert 条件式

満たすべき条件を書くことで、プログラムの信頼性を高めること目的です。 条件はboolで書きます。
assert文は、リリースビルド時には存在しないものとして読み飛ばされるため、実行時の処理負荷はありません。

4break文

「break文」はブロックの処理を抜ける文です(表4-1)。
表4-1: break文の構文

break ブロックの識別子

break文の仕様は図4-1の通りです。
  • 指定した識別子の、blockブロック、forブロック、ifブロック、switchブロック、tryブロック、whileブロックの処理を抜ける。
  • ブロックの処理を抜けるとは、そのブロックの「end」の直後にジャンプすることである。
図4-1: break文の仕様
設計の理由

多くの言語では直近のブロックしか抜けることができないため、多重ループを抜ける場合にはフラグの変数を追加するなどの回りくどい方法を行う必要がありました。 そこでKuinでは抜けるブロックの識別子が指定できるようにしました。

またKuinではifブロックを含む多くのブロックがbreak文で抜けられるため、抜けるブロックを混乱しないように識別子は省略できないようにしました。

5const文

「const文」は定数を定義します。 定数とは、値を書き換えることができない変数です。 定義の方法は変数と同様です(表5-1)。
表5-1: const文の構文

const 定数名: 型名 ::

const文は、関数の外のグローバルな領域や、クラス内にも記述できます。
const文の仕様は図5-1の通りです。
図5-1: const文の仕様

6do文

「do文」は式を実行する文です(表6-1)。
表6-1: do文の構文

do

do文の仕様は図6-1の通りです。
  • 最後に評価される演算は副作用がなければならない。
図6-1: do文の仕様
設計の理由

単なる式の実行に「do」という構文を必要とする理由は、必ず行頭で処理の意味を示すというルールを一貫することで可読性とコンパイル速度を向上させる狙いがあります。

とはいえ式の実行は高頻度で行われるため、なるべくタイプ数が少なくなるように2文字に抑えました。

7ret文

「ret文」は関数を抜ける文です(表7-1)。
表7-1: ret文の構文

ret

ret 戻り値

ret文は、戻り値の型を指定した関数では戻り値を返さなければならず、戻り値の型を指定しなかった関数では戻り値を返してはなりません。
ret文の仕様は図7-1の通りです。
  • 戻り値の型を指定した関数でret文を使わずに関数を抜けた場合は、戻り値には型のデフォルト値が返る。
図7-1: ret文の仕様
設計の理由

多くの言語では「return」ですが、x86アセンブリでは省略形の「ret」が使われるため、短いほうを採用しました。

8skip文

「skip文」はループを1回スキップする文です(表8-1)。
表8-1: skip文の構文

skip ブロックの識別子

skip文の仕様は図8-1の通りです。
  • 指定した識別子の、forブロック、whileブロックのループを1回スキップする。
  • ブロックのループをスキップするとは、そのブロックの「end」の直前にジャンプすることである。
図8-1: skip文の仕様
設計の理由

多くの言語では「continue」ですが、長く入力が大変なため「skip」に決定しました。 多くの言語のドキュメントのcontinueの説明に「ループ内の残りの処理をskipする」と書かれているため、「skip」の語のほうが直感的ではないかと思います。

9throw文

「throw文」は例外を発生させる文です(表9-1)。
表9-1: throw文の構文

throw 例外コード

例外コードはintで指定します。 具体的な値は、Kuin例外コードを参照してください。
例外が発生するとその瞬間から関数を次々と抜けてアプリは終了しますが、tryブロックを使うと途中で発生した例外を捕まえて処理を続行することができます。 エラーが起こった場所で例外を発生させ、エラーを処理したいところでtryブロックを使います。
設計の理由

多くの言語では例外クラスをユーザ側で派生させることで高度な例外処理が行えるようになっていますが、逆に煩雑になり適切に活用されないことが多いため、実用的に扱いやすい「例外コード」だけに絞ることにしました。

10var文

「var文」は変数を定義します。 詳しくは、Kuinの変数定義を参照してください。
var文は、関数の外のグローバルな領域や、クラス内にも記述できます。

11blockブロック

「blockブロック」は何もしないブロックです(表11-1)。
表11-1: blockブロックの構文

block
    処理
end block

block ブロック名
    処理
end block

スコープを分けたり、breakで抜ける用途で使われます。

12classブロック

「classブロック」はクラスを定義します。 詳しくは、クラスを参照してください。
classブロックは、関数の外のグローバルな領域や、クラス内にも記述できます。

13enumブロック

「enumブロック」は列挙型を定義します。 詳しくは、列挙型を参照してください。
enumブロックは、関数の外のグローバルな領域や、クラス内にも記述できます。

14forブロック

「forブロック」は数え上げながらループするブロックです(表14-1)。
表14-1: forブロックの構文

for(初期値, 終値)
    処理
end for

for ブロック名(初期値, 終値)
    処理
end for

for(初期値, 終値, 増減値)
    処理
end for

forブロックでは、初期値から終値まで増減値ずつ増減しながら繰り返します。 増減値は省略すると「1」と解釈されます。
forブロックの仕様は図14-1の通りです。
  • 初期値、終値、増減値は共にintでなければならない。
  • 増減値は、コンパイル時に定数に確定できる、0以外の値でなければならない。
  • 数え上げるカウント値は、ブロック名をint型変数のように参照することで取得できる。
  • ループを繰り返す条件は、「増減値>0」の場合は「カウント値<=終値」、「増減値<0」の場合は「カウント値>=終値」となる。
  • 初期値、終値、増減値で指定した式は、forブロックに突入する直前で評価され、ループを繰り返す間に再評価されることはない。
図14-1: forブロックの仕様
設計の理由

多くの言語のforでは、初期値、終値、カウント値のそれぞれで変数を指定する必要があり、間違った変数を指定するとバグの元になっていたため、Kuinではより数え上げに特化させてシンプルな設計にしました。

このため他の言語のように「2倍ずつ増加させる」などの処理が行えなくなっていますが、そのような複雑な処理はwhileを使うべきだと考えます。

15funcブロック

「funcブロック」は関数を定義します。 詳しくは、Kuinの関数定義を参照してください。
funcブロックは、関数の外のグローバルな領域や、クラス内にも記述できます。

16ifブロック

「ifブロック」は条件によって処理を分岐させるブロックです(表16-1)。
表16-1: ifブロックの構文

if(条件)
    処理
end if

if ブロック名(条件)
    処理
end if

if(条件1)
    処理
elif(条件2)
    処理
elif(条件3)
    処理
else
    処理
end if

ifブロックでは、複数のelif節を連ねたり、最後にelse節を書くこともできますが、いずれも省略できます。
ifブロックの仕様は図16-1の通りです。
  • 条件はboolでなければならない。
  • 条件の式を順番に評価していき、条件が真になった箇所があれば中身を実行してブロックを抜ける。
  • どの条件も真にならなかった場合は、else節の中身が実行される。 このときelse節がなければ何もせずにブロックを抜ける。
  • 条件がコンパイル時に定数に確定できる値になっているために、常に実行される処理(if・elif・elseの中身)が定まる場合、それ以外の実行されない処理はコンパイル時に削除される。
図16-1: ifブロックの仕様
設計の理由

elifは、Pythonなどの一部の言語で用いられている、いわゆる「else if」の略です。

17switchブロック

「switchブロック」は値によって処理を分岐させるブロックです(表17-1)。
表17-1: switchブロックの構文

switch(比較する値)
case 値1
    処理
case 値2
    処理
end switch

switch ブロック名(比較する値)
case 値1
    処理
case 値2
    処理
end switch

switch(比較する値)
case 値1, 値2
    処理
case 値3の下限 to 値3の上限
    処理
end switch

switch(比較する値)
case 値1
    処理
case 値2
    処理
default
    処理
end switch

switchブロックでは、複数のcase節を連ねたり、最後にdefault節を書くこともできます。 default節は省略できます。
case節の値は「,」で区切って複数指定したり、「a to b」と書くことで「a以上b以下」という範囲を指定することもできます。
switchブロックの仕様は図17-1の通りです。
  • case節は1つ以上存在しなければならない。
  • 比較する値と一致する値があるかどうかを順番に調べていき、一致した箇所があれば中身を実行してブロックを抜ける。
  • どの値とも一致しなかった場合は、default節の中身が実行される。 このときdefault節がなければ何もせずにブロックを抜ける。
  • ブロック名を変数のように参照すると、「比較する値」がその型で返る。
図17-1: switchブロックの仕様
設計の理由

C言語などでは、caseの最後でbreakを行わないと次のcaseに突入してしまいますが、次のcaseに突入したいケースは少なくバグの元になりやすいため、Kuinではbreakが無くても自動的にブロックを抜けるようにしました。

その代わりに複数の値に対して同じ処理が行えるよう、case節の条件の書き方を充実させています。

18tryブロック

「tryブロック」は発生した例外を処理するブロックです(表18-1)。
表18-1: tryブロックの構文

try
    処理
catch
    処理
end try

try ブロック名
    処理
catch
    処理
end try

try
    処理
catch 例外コード1, 例外コード2
    処理
catch 例外コード下限 to 例外コード上限
    処理
end try

try
    処理
finally
    処理
end try

try
    処理
catch
    処理
finally
    処理
end try

try節内で例外が発生すると、該当するcatch節にジャンプして処理が行われます。 また、例外が発生しても発生しなくてもfinally節は必ず実行されます。
例外が発生すると次々と関数を抜けていきますが、catch節を使うことで例外を捕まえることができ、finally節を使うことでリソース解放などの確実に行いたい処理を行っておくことができます。
catch節の値は「,」で区切って複数指定したり、「a to b」と書くことで「a以上b以下」という範囲を指定したり、省略することもできます。
最後にfinally節を書くこともできますが、省略できます。
tryブロックの仕様は図18-1の通りです。
  • 少なくともcatch節とfinally節のどちらかは存在しなければならない。
  • 発生した例外の例外コードと一致するcatch節があるかどうかを順番に調べていき、一致した箇所があれば中身を実行してブロックを抜ける。
  • finally節は、catch節の処理を実行したかどうかにかかわらず、ブロックを抜ける直前で必ず実行される。
  • 例外が発生したときにどのcatch節の例外コードにも該当しなかった場合、ブロックを抜けた直後にその例外が再発する。
  • finally節内でret、break、skipなどを使って自身のtryブロックを抜けることは、発生していた例外が再発せずに抜けて処理が続行されるため、行うべきではない。
  • ブロック名を変数のように参照すると、例外コードがintで返る。
図18-1: tryブロックの仕様

19whileブロック

「whileブロック」は条件を満たす限りループし続けるブロックです(表19-1)。
表19-1: whileブロックの構文

while(条件)
    処理
end while

while ブロック名(条件)
    処理
end while

while(条件, skip)
    処理
end while

skipを指定すると、最初の条件判断をスキップし、必ず1回は処理が行われるようになります。
設計の理由

この「skip」は、C言語などの「do-while」に相当します。

1544537416jaf