2024年04月26日くいなちゃん


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

1Kuinの文とブロック

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

文の名前 文の中身


ブロック名 識別子()

    ブロックの中身

end ブロック名

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

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

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

「end ブロック名」という構文は、Visual Basicに由来します。

2alias文

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

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

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

3assert文

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

assert 条件式

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

4break文

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

break ブロックの識別子

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

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

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

5const文

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

const 定数名: 型名 ::

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

6do文

「do文」は式を実行する文です(図6-1)。

do

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

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

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

7excode文

「excode文」は、コンパイル後に生成されるコードを直接記述できる文です(図7-1)。

excode 文字列の値

図7-1: excode文の構文
上級者向け機能です。
コンパイルオプションの「-e」に指定する環境により、仕様が異なります。

8include文

「include文」は、別ファイルに書かれているソースコードを埋め込める文です(図8-1)。

include ファイル名の一部

図8-1: include文の構文
include文を記述したソースコードの名前が「sample.kn」で、include文に指定したファイル名の一部が「part」だった場合、「sample.part.kn」というファイルが読み込まれて埋め込まれます。
「sample.part.part2.kn」のように、多段階に埋め込むことはできません。
埋め込まれたソースコードは、include文を記述したソースコード内に直接書いたのと同じように扱われます。

9ret文

「ret文」は関数を抜ける文です(図9-1)。

ret


ret 戻り値

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

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

10skip文

「skip文」はループを1回スキップする文です(図10-1)。

skip ブロックの識別子

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

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

11throw文

「throw文」は例外を発生させる文です(図11-1)。

throw 例外コード

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

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

12var文

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

13blockブロック

「blockブロック」は何もしないブロックです(図13-1)。

block

    処理

end block


block ブロック名

    処理

end block

図13-1: blockブロックの構文
スコープを分けたり、breakで抜ける用途で使われます。

14classブロック

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

15enumブロック

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

16forブロック

「forブロック」は数え上げながらループするブロックです(図16-1)。

for(初期値, 終値)

    処理

end for


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

    処理

end for


for(初期値, 終値, 増減値)

    処理

end for

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

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

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

17funcブロック

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

18ifブロック

「ifブロック」は条件によって処理を分岐させるブロックです(図18-1)。

if(条件)

    処理

end if


if ブロック名(条件)

    処理

end if


if(条件1)

    処理

elif(条件2)

    処理

elif(条件3)

    処理

else

    処理

end if

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

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

19switchブロック

「switchブロック」は値によって処理を分岐させるブロックです(図19-1)。

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

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

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

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

20tryブロック

「tryブロック」は発生した例外を処理するブロックです(図20-1)。

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

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

21whileブロック

「whileブロック」は条件を満たす限りループし続けるブロックです(図21-1)。

while(条件)

    処理

end while


while ブロック名(条件)

    処理

end while


while(条件, skip)

    処理

end while

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

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

1714101764jaf