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


プログラミング言語Kuin」の言語仕様3、演算子についてです。

1Kuinの演算子

Kuinにおける演算子と優先順位は表1-1の通りです。
表1-1: Kuinの演算子
優先順位 結合 演算子 説明
1 f() 関数呼び出し
a[] 配列参照
a.b メンバ参照
a $ t キャスト
a $> t バイナリ符号化
a $< t バイナリ復号化
2 a ^ b
3 +a
-a
符号
!a 論理否定
^a 要素数演算子
#t インスタンス生成
##a ディープコピー
4 a * b 乗算
a / b 除算
a % b 剰余
5 a + b 加算
a - b 減算
6 a ~ b 配列連結
7 a = b
a <> b
a < b
a > b
a <= b
a >= b
比較演算子
a =& b
a <>& b
参照比較演算子
a =$ t
a <>$ t
型比較演算子
8 a & b 論理積
9 a | b 論理和
10 a ?(b, c) 条件演算子
11 a :: b
a :+ b
a :- b
a :* b
a :/ b
a :% b
a :^ b
a :~ b
代入演算子
括弧なしに演算子が使われたとき、優先順位が小さい演算子のほうが先に計算されます。 優先順位が同じ演算子が並ぶと、結合が左なら左側の演算子が、結合が右なら右側の演算子が先に計算されます。
それぞれの演算子について、以下で詳しく説明します。

2関数呼び出し

関数を呼び出す演算子です(図2-1)。

関数名(引数1, 引数2, ...)

図2-1: 関数呼び出しの構文
関数呼び出しの仕様は図2-2の通りです。
  • 関数型の値の後に「(引数の式1, 引数の式2, ...)」を書くと、その関数が呼び出され、戻り値がある場合はその値が返る。
  • 引数を参照渡しするときには、引数の変数名の前に「&」をつけなければならない。 例えば「f(&n)」のように書く。
  • 参照渡しの引数で結果を受け取る必要がないとき、引数の変数名を省略して「&」とだけ書ける。 例えば「f(&)」のように書く。
  • 関数に渡す引数や戻り値の型および個数は、関数で定義されているものと一致していなければならない。
図2-2: 関数呼び出しの仕様
1つの式の中に関数呼び出しを複数書いた場合、関数が呼び出される順序は不定です。 副作用がある関数を複数呼び出すときには、呼び出し順序が影響しないようにご注意ください。 ただし「&」「|」「?(,)」演算子においては、式が必ず左から順番に評価され、不要な式は評価がスキップされる仕様なため、関数呼び出しの順序も左から呼び出される前提で書くことができます。

3配列参照

配列の要素を参照する演算子です(図3-1)。

配列名[要素番号]

図3-1: 配列参照の構文
配列参照の仕様は図3-2の通りです。
  • intの値「n」を用いて、配列の値の後に「[n]」を付けると、配列のn番目の要素が返る。
  • このとき、nは0以上かつ要素数未満でなければならない。 nがこの範囲外だった場合、デバッグ実行時に例外(0xE9170002)が発生する。
図3-2: 配列参照の仕様

4メンバ参照

値のメンバを参照する演算子です(図4-1)。

.メンバ名

図4-1: メンバ参照の構文
メンバ参照の仕様は図4-2の通りです。
  • 任意の型の値の後に「.メンバ名」を付けると、その型に定義されているメンバが返る。
図4-2: メンバ参照の仕様
メンバとは、クラスにおけるメソッドとプロパティ、および型に応じて用意されている組み込みメソッドのことです。

5キャスト

値を指定した型に変換する演算子です(図5-1)。

$ 型名

図5-1: キャストの構文
キャストの仕様は図5-2の通りです。
  • 任意の型の値の後に「$ 型名」を付けると、その型に変換された値が返る。
  • クラスのインスタンスを、そのクラスおよびその親クラス以外の型にキャストすると、実行時に例外(0xE9170001)が発生する。
  • クラスのインスタンスがnullの場合、例外は発生せずキャストは成功する。
図5-2: キャストの仕様
キャストしようとしたクラスの型が、そもそもそのクラスや親クラスになり得ない型だった場合、コンパイル時にエラーになります。
変換できる型には制約があり、表5-1の通りです。
表5-1: キャストできる型
変換前の型 変換できる型
int int、float、char、bool、ビット型、列挙型
float int、float、ビット型
char int、char、ビット型
bool int、bool、ビット型
ビット型 int、float、char、bool、ビット型、列挙型
列挙型 int、ビット型、列挙型
クラス クラス

6バイナリ符号化

値を、[]bit8型のバイナリ列に変換する演算子です(図6-1)。

$> []bit8

図6-1: バイナリ符号化の構文
バイナリ符号化の仕様は図6-2の通りです。
  • 任意の型の値の後に「$> []bit8」を付けると、バイナリ列に変換された値が返る。 型名に[]bit8以外を指定することはできない。
  • 再帰的にディープコピーしてバイナリ化するため、2つの変数が同じ参照を保持していた場合などには参照関係は失われる。
  • 関数型をバイナリ化すると、「$<」演算子で復元したときにnullになるような値に変換される。
  • nullをバイナリ化すると、nullが復元される。
  • クラスと継承関係にあるインスタンスをバイナリ化すると、クラスではなくインスタンスの型が尊重されて正しく復元される。
  • list型をバイナリ化すると、リストのポインタも正しく復元される。
  • 標準ライブラリのうち、「#」演算子で直接インスタンスが作れないクラスは、正しく復元されないことがある。
図6-2: バイナリ符号化の仕様

7バイナリ復号化

バイナリ符号化演算子によって変換された[]bit8型のバイナリ列を、値に復元する演算子です(図7-1)。

$< 型名

図7-1: バイナリ復号化の構文
バイナリ復号化の仕様は図7-2の通りです。
  • バイナリ列に変換された値の後に「$< 型名」を付けると、バイナリ列に変換される前の値が返る。
  • 型名にはバイナリ符号化演算子によって変換される前の型を指定しなければならない。
  • クラスのインスタンスが復元されるときは、指定したクラスの型ではなく、復元前のインスタンスの型で復元される。
  • 標準ライブラリのうち、「#」演算子で直接インスタンスが作れないクラスは、正しく復元されないことがある。
図7-2: バイナリ復号化の仕様

8

aのb乗を計算する演算子です(図8-1)。

a ^ b

図8-1: 冪の構文
冪の仕様は図8-2の通りです。
  • intもしくはfloatの2つの値「a」「b」に対し、「a ^ b」とすると、aのb乗の値が返る。
  • aとbの型は等しくなければならず、演算結果はその型で返る。
  • aとbが両方とも0のとき、1を返す。
  • 結果が実数でないとき、intの場合は0に、floatの場合はNaNになり、例外は発生しない。
図8-2: 冪の仕様
設計の理由

この演算子「^」はBASICに由来します。 日常的に使われるほど身近な演算ですので、演算子として直感的に書けたほうが良いと考え採用しました。

9符号

プラスやマイナスの符号を表す演算子です(図9-1)。

+

-

図9-1: 符号の構文
符号の仕様は図9-2の通りです。
  • int、float、ビット型の値の前に「+」を付けると、そのままの値が返る。
  • int、float、ビット型の値の前に「-」を付けると、正負を反転させた値が返る。
  • コンパイル時に定数になる値への演算の場合、コンパイル時に演算されて結果も定数になることが保証される。
図9-2: 符号の仕様

10論理否定

真偽を反転させる演算子です(図10-1)。

!

図10-1: 論理否定の構文
論理否定の仕様は図10-2の通りです。
  • boolの値の前に「!」を付けると、真偽を反転させた値が返る。
  • コンパイル時に定数になる値への演算の場合、コンパイル時に演算されて結果も定数になることが保証される。
図10-2: 論理否定の仕様

11要素数演算子

配列などの要素数を取得する演算子です(図11-1)。

^

図11-1: 要素数演算子の構文
要素数演算子の仕様は図11-2の通りです。
  • 配列、list、stack、queue、dictの前に「^」を付けると、要素数がint型で返る。 これらの型の値は内部に要素数を保持しており、この演算は高速()に行われる。
図11-2: 要素数演算子の仕様

12インスタンス生成

クラスなどのインスタンスを生成する演算子です(図12-1)。

#型名

図12-1: インスタンス生成の構文
インスタンス生成の仕様は図12-2の通りです。
  • list、stack、queue、dict、クラスの型名の前に「#」を付けると、それらのインスタンスが生成されて返る。
  • 標準ライブラリの一部のクラスは、「#」演算子で直接インスタンスが生成できず、コンパイルエラーとなる。 各ライブラリのドキュメントを参照のこと。
  • 任意の型名の前に「#[要素数1, 要素数2, ..., 要素数n]」を付けると、その型名を要素とするn次元配列のインスタンスが生成されて返る。
  • 生成した配列の各値は型のデフォルト値になる。
図12-2: インスタンス生成の仕様
例えば、「#[3,2]int」とすると、3×2の要素の「[][]int」型の2次元配列が返ります。

13ディープコピー

インスタンスをディープコピー(参照ではなく値を複製)する演算子です(図13-1)。

##

図13-1: ディープコピーの構文
ディープコピーの仕様は図13-2の通りです。
  • 配列、list、stack、queue、dict、クラスの値の前に「##」を付けると、その値が別のメモリ領域にコピーされ、その参照が返る。
  • 再帰的にディープコピーするため、2つの変数が同じ参照を保持していた場合などには参照関係は失われる。
  • 関数型をディープコピーすると、関数への参照がコピーされる。
  • nullをディープコピーすると、nullがコピーされる。
  • クラスと継承関係にあるインスタンスをディープコピーすると、クラスではなくインスタンスの型が尊重されて正しくコピーされる。
  • list型をディープコピーすると、リストのポインタも正しくコピーされる。
  • 標準ライブラリのうち、「#」演算子で直接インスタンスが作れないクラスは、正しくコピーされないことがある。
図13-2: ディープコピーの仕様
設計の理由

参照のコピーではなく中身のコピーを行いたいことは多いですが、これを用意している言語は意外と少なくユーザ側でコピー処理を実装する必要があったため、Kuinでは標準で用意しました。

14乗算

を計算する演算子です(図14-1)。

a * b

図14-1: 乗算の構文
乗算の仕様は図14-2の通りです。
  • int、float、ビット型のいずれかの2つの値「a」「b」に対し、「a * b」とすると、の値が返る。
  • aとbの型は等しくなければならず、演算結果はその型で返る。
  • コンパイル時に定数になる値同士の演算の場合、コンパイル時に演算されて結果も定数になることが保証される。
図14-2: 乗算の仕様

15除算

を計算する演算子です(図15-1)。

a / b

図15-1: 除算の構文
除算の仕様は図15-2の通りです。
  • int、float、ビット型のいずれかの2つの値「a」「b」に対し、「a / b」とすると、の値が返る。
  • aとbの型は等しくなければならず、演算結果はその型で返る。
  • intもしくはビット型の演算では、bは0であってはならない。 コンパイルエラーもしくは例外が発生したり、不定値になることがある。
  • floatの演算で、aとbが両方とも0.0のとき、コンパイルエラーもしくはNaNになる。
  • コンパイル時に定数になる値同士の演算の場合、コンパイル時に演算されて結果も定数になることが保証される。
図15-2: 除算の仕様

16剰余

の余りを計算する演算子です(図16-1)。

a % b

図16-1: 剰余の構文
剰余の仕様は図16-2の通りです。
  • int、float、ビット型のいずれかの2つの値「a」「b」に対し、「a % b」とすると、の余りの値が返る。
  • aとbの型は等しくなければならず、演算結果はその型で返る。
  • intもしくはビット型の演算では、bは0であってはならない。 コンパイルエラーもしくは例外が発生したり、不定値になることがある。
  • floatの演算で、aとbが両方とも0.0のとき、コンパイルエラーもしくはNaNになる。
  • コンパイル時に定数になる値同士の演算の場合、コンパイル時に演算されて結果も定数になることが保証される。
図16-2: 剰余の仕様

17加算

を計算する演算子です(図17-1)。

a + b

図17-1: 加算の構文
加算の仕様は図17-2の通りです。
  • int、float、ビット型のいずれかの2つの値「a」「b」に対し、「a + b」とすると、の値が返る。
  • aとbの型は等しくなければならず、演算結果はその型で返る。
  • コンパイル時に定数になる値同士の演算の場合、コンパイル時に演算されて結果も定数になることが保証される。
図17-2: 加算の仕様

18減算

を計算する演算子です(図18-1)。

a - b

図18-1: 減算の構文
減算の仕様は図18-2の通りです。
  • int、float、ビット型のいずれかの2つの値「a」「b」に対し、「a - b」とすると、の値が返る。
  • aとbの型は等しくなければならず、演算結果はその型で返る。
  • コンパイル時に定数になる値同士の演算の場合、コンパイル時に演算されて結果も定数になることが保証される。
図18-2: 減算の仕様

19配列連結

2つの配列を連結する演算子です(図19-1)。

a ~ b

図19-1: 配列連結の構文
配列連結の仕様は図19-2の通りです。
  • 配列の2つの値「a」「b」に対し、「a ~ b」とすると、aとbを連結した配列が新規のメモリ領域に確保されて返る。
  • aとbの型は等しくなければならない。
  • aとbはnullであってはならない。 コンパイルエラーもしくは実行時に例外が発生する。
  • コンパイル時に定数になる値同士の演算の場合、コンパイル時に演算されて結果も定数になることが保証される。
図19-2: 配列連結の仕様
設計の理由

この演算子「~」はD言語に由来します。 JavaやC#で見られる「+」では数値の加算と曖昧になり、PHPの「.」ではメンバの呼び出しと曖昧になりますので「~」を採用しました。

20比較演算子

2つの値を比較する演算子です(図20-1)。

a = b

a <> b

a < b

a > b

a <= b

a >= b

図20-1: 比較演算子の構文
比較演算子の仕様は図20-2の通りです。
  • 比較可能な2つの値「a」「b」に対し、「a = b」「a <> b」「a < b」「a > b」「a <= b」「a >= b」のいずれかを行うと、値の比較結果がboolで返る。
  • 比較可能な型とは、「a < b」「a > b」「a <= b」「a >= b」の場合はint、float、char、ビット型、[]char、列挙型、クラスで、「a = b」「a <> b」の場合はこれらにboolが加わる。
  • aとbはnullであってはならない。 コンパイルエラーもしくは実行時に例外が発生する。
  • aとbの型は等しくなければならない。
  • 「a = b」「a < b」「a > b」「a <= b」「a >= b」は数学における比較と同様の意味となる。 「a <> b」は数学における「」に相当する。
  • コンパイル時に定数になる値同士の演算の場合、コンパイル時に演算されて結果も定数になることが保証される。
図20-2: 比較演算子の仕様
設計の理由

不一致の演算子「<>」はBASIC、Pascal、SQLなどで見られるものです。 演算子の1文字目を読み込んだ時点で優先順位が決定できるとコンパイルが速く行えるため、1文字目が「!」と重複する「!=」ではなく「<>」を採用しました。

21参照比較演算子

2つの参照を比較する演算子です(図21-1)。

a =& b

a <>& b

図21-1: 参照比較演算子の構文
参照比較演算子の仕様は図21-2の通りです。
  • 配列、list、stack、queue、dict、クラス、func、nullのいずれかの2つの値「a」「b」に対し、「a =& b」「a <>& b」を行うと、それぞれ「参照が等しいか否か」「参照が異なるか否か」がboolで返る。
  • aとbの型は等しくなければならない。
図21-2: 参照比較演算子の仕様

22型比較演算子

クラスのインスタンスが指定したクラスの型もしくはその親クラスの型かどうかを判定する演算子です(図22-1)。

インスタンス =$ 型名

インスタンス <>$ 型名

図22-1: 型比較演算子の構文
型比較演算子の仕様は図22-2の通りです。
  • クラスの値「a」に対し、クラスの型「t」を指定して「a =$ t」「a <>$ t」を行うと、それぞれ「そのクラスもしくは親クラスの型であるか否か」「そのクラスもしくは親クラスの型でないか否か」がboolで返る。
  • インスタンスはnullであってはならない。 コンパイルエラーもしくは実行時に例外が発生する。
図22-2: 型比較演算子の仕様
クラスは継承した子クラスのインスタンスを継承元の親クラスの変数に代入できるため、代入されているインスタンスがどの型であるかを調べるのにこの演算子を使います。

23論理積

論理積を計算する演算子です(図23-1)。

a & b

図23-1: 論理積の構文
論理積の仕様は図23-2の通りです。
  • boolの2つの値「a」「b」に対し、「a & b」とすると、aとbが共にtrueのときにtrueが返り、それ以外のときはfalseが返る。
  • aがfalseだった段階で結果がfalseであることが確定するため、aがfalseのときはbの式の評価がスキップされる。 必ずa、bの順で式が評価される。
  • コンパイル時に定数になる値同士の演算の場合、コンパイル時に演算されて結果も定数になることが保証される。
図23-2: 論理積の仕様

24論理和

論理和を計算する演算子です(図24-1)。

a | b

図24-1: 論理和の構文
論理和の仕様は図24-2の通りです。
  • boolの2つの値「a」「b」に対し、「a | b」とすると、aとbが共にfalseのときにfalseが返り、それ以外のときはtrueが返る。
  • aがtrueだった段階で結果がtrueであることが確定するため、aがtrueのときはbの式の評価がスキップされる。 必ずa、bの順で式が評価される。
  • コンパイル時に定数になる値同士の演算の場合、コンパイル時に演算されて結果も定数になることが保証される。
図24-2: 論理和の仕様

25条件演算子

真偽に応じて値を返す演算子です(図25-1)。

条件 ?(真のときの値, 偽のときの値)

図25-1: 条件演算子の構文
条件演算子の仕様は図25-2の通りです。
  • boolの値「a」と任意の型の2つの値「b」「c」に対し、「a ?(b, c)」とすると、aがtrueのときにはbが、aがfalseのときにはcが返る。
  • 「?」と「(」の間にスペースを入れてはならない。
  • bとcの型は等しくなければならない。
  • bとcのうち、返らなかったほうの式は評価がスキップされる。 必ずa、bの順もしくはa、cの順で式が評価される。
  • コンパイル時に定数になる値同士の演算の場合、コンパイル時に演算されて結果も定数になることが保証される。
図25-2: 条件演算子の仕様
設計の理由

この演算子「?(,)」はC言語に由来します。 C言語では「?:」ですが、式が長くなったり複数の「?:」を組み合わせた場合に優先順位が解りにくくなるため、明示的な括弧を用意して「?(,)」としました。 「?」と「(」の間にスペースを入れてはならない仕様にしたのは、この間が離れると、数式の括弧と混同しやすくなるためです。

26代入演算子

変数に値を代入する演算子です(図26-1)。

a :: b

a :+ b

a :- b

a :* b

a :/ b

a :% b

a :^ b

a :~ b

図26-1: 代入演算子の構文
代入演算子の仕様は図26-2の通りです。
  • 任意の型の変数「a」と値「b」に対し、「a :: b」とすると、aにbが代入される。
  • aとbの型は等しくなければならない。
  • この演算子は値を返さない。
  • 「a :+ b」は「a :: a + b」に等しい。 同様に、「a :- b」「a :* b」「a :/ b」「a :% b」「a :^ b」「a :~ b」はいずれも「a :: a 演算子 b」に等しい。
  • aの式は一度しか評価されない。 aが関数呼び出しの場合でも、2回関数呼び出しが行われたりはしない。
図26-2: 代入演算子の仕様
設計の理由

代入演算子「::」はKuinの最も風変わりな設計の一つですが、これはPascalの代入演算子「:=」に由来します。 演算子の1文字目を読み込んだ時点で優先順位が決定できるとコンパイルが速く行えるため、1文字目が「=」と重複する「==」ではなく、かつ「:=」より入力が容易な「::」に決定しました。

1714051313jaf