MikoScript3 言語仕様
演算子
「演算子」は、式に対して作用します。つまり、式は、演算子の作用対象(オペランド)
になります。作用対象が1つだけの演算子を「単項演算子」と呼びます。単項演算子は、
作用対象の前または後に置きます。作用対象の前に置く演算子を「前置演算子」、後に置
く演算子を「後置演算子」と呼びます。後置演算子の中には、作用対象を省略できるもの
もあります。その場合「無項演算子」となります。作用対象が2つある演算子を「二項演
算子」と呼びます。二項演算子は、2つの作用対象の間に置きます。二項演算子の中には、
左項を省略できるものもあります。その場合、前置演算子になります。作用対象が3つあ
る特殊な三項演算子もあります。関数名や配列名等の後に置く括弧も、文法上の分類では、
演算子に含めます。これらは、引数並びを持つ特種な後置演算子になります。
●演算子一覧
本言語で使用する演算子を、以下の一覧表に示します。
演算子 | 機能概要 | 基本用法 | 結合性 | 種類 |
:: | グローバルスコープ規定 | ::箱名 | 右 | 前置 |
. | メンバースコープ規定 | .箱名 | 右 | 前置 |
^ | モジュールローカルスコープ規定 | ^箱名 | 右 | 前置 |
$ | スレッドローカルスコープ規定 | $箱名 | 右 | 前置 |
@ | 関数スタティックスコープ規定 | @箱名 | 右 | 前置 |
:: | 箱内スコープ規定(参照型) | 箱名::箱名 | 左 | 二項 |
. | 箱内スコープ規定(降下型) | 箱名.箱名 | 左 | 二項 |
[ ] | 連想名生成 | 箱名[ インデックス ] | 左 | 特種 |
( ) | 標準型関数コール | 関数名( 引数並び ) | 左 | 特種 |
( ) | インスタンス生成 | クラス名( 引数並び ) | 左 | 特種 |
( ) | 純粋配列の要素 | 純粋配列名( 引数並び ) | 左 | 特種 |
+ | 数値の正負維持 | +式 | 右 | 前置 |
- | 数値の正負反転 | -式 | 右 | 前置 |
: | ラベル名文字列への変換 | :ラベル名 | 右 | 前置 |
' | リレー型関数コール | 式'関数名( 引数並び ) | 左 | 二項 |
++ | 1の加算代入(式値は演算後値) | ++代入先 | 右 | 前置 |
++ | 1の加算代入(式値は演算前値) | 代入先++ | 左 | 後置 |
-- | 1の減算代入(式値は演算後値) | --代入先 | 右 | 前置 |
-- | 1の減算代入(式値は演算前値) | 代入先-- | 左 | 後置 |
! | 真偽の論理否定 | ! 式 | 右 | 前置 |
~ | ビットの反転 | ~ 式 | 右 | 前置 |
~ | コマンド型関数コール | 関数名~ 引数並び | 左 | 特種 |
* | 乗算 | 式 * 式 | 左 | 二項 |
/ | 除算 | 式 / 式 | 左 | 二項 |
% | 剰余算 | 式 % 式 | 左 | 二項 |
+ | 加算 | 式 + 式 | 左 | 二項 |
- | 減算 | 式 - 式 | 左 | 二項 |
<< | ビットの左シフト | 式 << 式 | 左 | 二項 |
>> | ビットの右シフト(符号付) | 式 >> 式 | 左 | 二項 |
& | ビットの論理積 | 式 & 式 | 左 | 二項 |
^ | ビットの排他的論理和 | 式 ^ 式 | 左 | 二項 |
| | ビットの論理和 | 式 | 式 | 左 | 二項 |
< | より小さいかの比較 | 式 < 式 | 左 | 二項 |
<= | より小さいか等しいかの比較 | 式 <= 式 | 左 | 二項 |
> | より大きいかの比較 | 式 > 式 | 左 | 二項 |
>= | より大きいか等しいかの比較 | 式 >= 式 | 左 | 二項 |
== | 等しいかの比較 | 式 == 式 | 左 | 二項 |
!= | 等しくないかの比較 | 式 != 式 | 左 | 二項 |
&& | 真偽の論理積 | 式 && 式 | 左 | 二項 |
|| | 真偽の論理和 | 式 || 式 | 左 | 二項 |
? : | 真偽による二者択一 | 式 ? 式 : 式 | 右 | 三項 |
= | 複製代入(標準代入) | 代入先 = 式 | 右 | 二項 |
:= | 参照代入 | 代入先 := 式 | 右 | 二項 |
<- | 移動代入 | 代入先 <- 式 | 右 | 二項 |
*= | 乗算して代入 | 代入先 *= 式 | 右 | 二項 |
/= | 除算して代入 | 代入先 /= 式 | 右 | 二項 |
%= | 剰余を代入 | 代入先 %= 式 | 右 | 二項 |
+= | 加算して代入 | 代入先 += 式 | 右 | 二項 |
-= | 減算して代入 | 代入先 -= 式 | 右 | 二項 |
&= | ビットの論理積を代入 | 代入先 &= 式 | 右 | 二項 |
|= | ビットの論理和を代入 | 代入先 |= 式 | 右 | 二項 |
^= | ビットの排他的論理を代入 | 代入先 ^= 式 | 右 | 二項 |
<<= | ビットを左シフトして代入 | 代入先 <<= 式 | 右 | 二項 |
>>= | ビットを右シフトして代入 | 代入先 >>= 式 | 右 | 二項 |
:== | 代入型関数コール | 関数名(引数並び):==式 | 左 | 二項 |
この表内で、同じ枠内にある演算子は、同じ優先度を持ち、別の枠内にある演算子は、
上の方が下の方よりも高い優先度を持ちます。たとえば、乗算演算子 * は、除算演算子
/ と同じ優先度ですが、加算演算子 + よりも高い優先度になります。
この表の基本用法欄には、演算子とその作用対象との用法上の基本的な関係を示してい
ます。このなかで、作用対象が式であれば、文法上、任意の式が作用対象として可能であ
ることを意味します。作用対象が、〜名(例えば、箱名、関数名など、但し、ラベル名は
除く)であれば、その対象は箱に限定されます。また、作用対象が代入先であれば、その
対象は代入可能な対象(箱または純粋配列の要素)に限定されます。引数並びに関しては、
別途説明します。
●式の評価
式の値を求めることを「式を評価する」と言います。演算子を含む式は、その式内の各
演算子を作用させることによって、評価が行なわれます。演算子を含まない式、つまり、
定値や識別名だけの式では、その値自身が評価値になります。
式内に複数の演算子があると、作用対象が競合する場合があります。たとえば、
A + B * C
の場合、項 B は、加算演算子 + と乗算演算子 * の両方の対象になっています。
式の評価は、競合がなければ、左から右の順に行なわれます。つまり、左側にある演算子
の方が、右側にある演算子よりも先に適用されます。
競合があれば、優先度の高い方の演算子が先に適用されます。上の例では、乗算演算子 *
の方が、加算演算子 + よりも優先度が高いので、項 B は、乗算演算子 * の対象になり
ます。加算演算子 + は、項 A と乗算結果を対象とすることになります。
同じ優先度の演算子どうしで競合がある場合は、演算子の結合性でどちらを優先するか
が決まります。演算子が左結合なら、左の方の演算子を優先し、右結合なら、右の方の演
算子を優先します。たとえば、
A + B - C
の場合、加算演算子 + と減算演算子 - は、同じ優先度で、左結合ですから、左の方の加
算演算子 + の方が優先されることになります。
次に右結合の例を示します。
A = B = C
の場合、代入演算子 = は、右結合なので、まず、項 C が項 B に代入されてから、項 A
に代入されることになります。なお、優先度が同じで結合性が異なる組合せはありません。
このように、式の評価順序は、演算子の優先度と結合性によって一律に決まります。こ
の評価順序を変えたい場合は、丸括弧 ( ) を使います。丸括弧は、任意の式を囲うこと
ができます。文法上、丸括弧で囲われた式もまた式で、演算子の作用対象になり得ます。
演算子の作用対象が丸括弧で囲われた式である場合、丸括弧内の式の評価値が、その演算
子の作用対象になります。つまり、丸括弧内の式の評価のほうが、その演算子の適用より
も、先に行なわれます。たとえば、
A * ( B + C )
の場合、乗算演算子 * の右側は、丸括弧で囲われた ( B + C ) という式なので、この式
が先に評価されます。なお、この例で、もし、括弧がなければ、
A * B + C
となりますが、この場合は、A * B が先に実行されます。
二項演算子では、左項と右項がともに、丸括弧で囲われた式になる場合があります。こ
の場合は、左項の方が先に評価されます。たとえば、
( A + B ) / ( C - D )
の場合、( A + B ) が先に評価され、次に ( C + D ) が評価されてから、除算演算が行
なわれます。
以下に、各演算子について、説明します。
●算術二項演算子
算術二項演算子には、+ - * / % があります。各演算子の作用は以下の通りです。
A + B A と B を足した値
A - B A から B を引いた値
A * B A に B を掛けた値
A / B A を B で割った値
A % B A を B で割った余りの値(符号は A と同じ)
算術二項演算子の作用対象は、整数値か、実数値です。作用対象の二項がともに整数値
の場合、結果は整数値になり、二項のうちどちらかが実数値の場合、結果は実数値になり
ます。
整数の算術演算は、2の補数表現の 32-bit の範囲でのみ有効で、オーバーフローと
アンダーフローは、無視されます。実数の算術演算で、オーバーフローが起きると特別
な値になり、アンダーフローが起きると値は0になります。いずれの場合でも、例外は
発生しません。
加算演算子 + と減算演算子 - に関しては、文字列に対しても適用できます。
文字列A + 文字列B の結果は、文字列Aに文字列Bを連結した文字列になります。
文字列A - 文字列B の結果は、文字列Aと文字列Bを比較した結果を示す以下の整数値
になります。
0・・・文字列Aと文字列Bは等しい
正・・・文字列Aは文字列Bよりも、文字コードの値が大きい
負・・・文字列Aは文字列Bよりも、文字コードの値が小さい
この場合の文字列比較は、文字コード列の単純な比較です。そのため、英字の大小等は、
区別されます。なお、もっと高度な文字列の比較を行なうには、専用の関数を使う必要が
あります。
算術二項演算子の作用対象が、上記以外のデータ型の場合、不正演算になります。
不正演算は、定値の場合なら、コンパイル時にエラーになりますが、そうでなければ、
実行時に例外が発生します。以降の説明で、不正演算になる場合も同様です。
●算術前置演算子
算術前置演算子には、+ と - があります。どちらも作用対象は、整数値か、または、
実数値に限ります。
+ 数値 の結果は、その数値と同じです。
- 数値 の結果は、その数値の符号(正負)を反転した値になります。
単項の + と - 演算子の作用対象が、数値以外の場合、不正演算になります。
●ビット操作演算子
ビット操作演算子には、各ビットごとの論理演算を行なう
& | ^ ~
と、各ビットのシフト演算を行なう
<< >>
があります。各ビット操作演算子の作用は、以下の通りです。
A & B A と B の各ビットごとの論理積を取った値
A | B A と B の各ビットごとの論理和を取った値
A ^ B A と B の各ビットごとの排他的論理和を取った値
~ A A の各ビットを反転した値
A << B A の各ビットを B の値の回数だけ左にシフトした値
A >> B A の各ビットを B の値の回数だけ右にシフトした値(符号付き)
ビット操作演算子の作用対象は、整数値に限ります。それ以外の場合は、不正演算にな
ります。
整数値の各ビットを左にシフトすると、符号付きでも符号無しでも、最右端のビット(
LSB )には 0 が入ります。一方、整数値の各ビットを右にシフトすると、符号付きの場合、
最左端の符号ビット( MSB )は、変わりませんが、符号無しの場合には、0 が入ります。
演算子 >> は、符号付きの右シフトです。符号なしの右シフトを行なうには、リレー型
関数の 'shift を使います。
なお、本言語では、ビット操作演算子 & | ^ の優先度は、比較演算子の優先度よりも
高くなっています。これは、C 言語等の優先度とは異なりますが、この方が、感覚的には
自然なので、敢えてこのようにしています。
●比較演算子
比較演算子には、大小関係を判定する二項演算子
< <= > >=
と、等価性を判定する二項演算子
== !=
があります。比較演算子の演算結果は、その演算が不正でなければ、必ず、真(1)か、偽
(0)になります。
大小関係を判定する演算子の作用対象は、数値どうしか、または、文字列どうしに限り
ます。これ以外の場合は、不正演算になります。数値どうしの比較では、二項とも整数値
の場合、整数値の比較になり、どちらかが実数値の場合、実数値の比較になります。
文字列どうしの比較方法は、二項減算演算子 - の場合と同様です。
大小関係を判定する各演算子の作用は、以下の通りです。
A < B A が B よりも小さければ、真(1)、さもなくば、偽(0)
A <= B A が B よりも小さいか等しければ、真(1)、さもなくば、偽(0)
A > B A が B よりも大きければ、真(1)、さもなくば、偽(0)
A >= B A が B よりも大きいか等しければ、真(1)、さもなくば、偽(0)
等価性を判定する演算子の作用対象は、特に限定はなく、任意の式が可能です。等価性
を判定する各演算子の作用は、以下の通りです。
A == B A と B が等しければ、真(1)、さもなくば、偽(0)
A != B A と B が等しくなければ、真(1)、さもなくば、偽(0)
等価性の判定は、以下のように行なわれます。
・二項とも数値、または、二項とも文字列の場合
大小関係の判定と同じ方法で比較して判定します。
・それ以外の場合
対象の実体が同じなら等しいと判定、異なれば等しくないと判定します。
ただし、二項とも null の場合、等しいと判定します。
たとえば、数値と文字列は、その値に関わらず、等しくないと判定されます。null は
null 以外のものと等しいと判定されることはありません。中身が数値、文字列、空、の
どれでもない箱は、二項とも実体が同一の箱である場合に限り、等しいと判定され、実体
が異なれば、たとえ中身の構成と各値が全く同じでも、等しくないと判定されます。
●論理演算子
論理演算子には、|| と && の二項演算子と、! の前置演算子があります。各論理演算
子の作用は、以下の通りです。
A || B A か B のどちらかが真ならば、真(1)、さもなくば、偽(0)
A && B A と B のどちらもが真ならば、真(1)、さもなくば、偽(0)
! A A が真ならば、偽(0)、さもなくば、真(1)
論理演算子の作用対象( 上記の項 A と項 B )には、任意の式が可能です。この式の
評価値は、以下のように「真偽判定」されます。
・数値は、値が0でなければ真、0であれば偽
・文字列は、空でなければ真、空であれば偽
・null は、常に偽
・中身が数値、文字列、空、のどれでもない箱は、常に真、但し、複合箱に関しては、
その真偽判定を行なう演算子関数が定義されていれば、その結果に従います。この
詳細は、「演算子の多重定義」の章の以下の節に記述しています。
・後置演算子の多重定義
二項論理演算では、左項の真偽判定結果が確定すれば、右項を評価しなくても、その論
理演算の結果が確定する場合があります。つまり、
A || B は、A が真ならば、B を評価しなくても、真になり、
A && B は、A が偽ならば、B を評価しなくても、偽になります。
このような場合、右項の評価は行なわれません。この影響は、たとえば、以下のような場
合に出ます。
f1() || f2() で、関数 f1() の返値が真なら、関数 f2 はコールされません。
x++ && y++ で、x の初期値が 0 なら、y の値は増えません。
複数の二項論理演算子がある場合も、各二項論理演算子に関して、上記の規則が適用さ
れます。たとえば、
A || B && C
では、演算子の優先度から、A || ( B && C ) となりますが、この評価は、A が真ならば、
B && C を評価しないで、真に確定され、A が偽ならば、B && C の評価に際し、B が偽な
らば、C を評価しないで、偽に確定されます。また、
A && B && C
では、演算子の結合性から、( A && B ) && C となりますが、この評価は、A が偽ならば、
B と C を評価しないで、偽に確定され、A が真でも、B が偽ならば、C を評価しないで、
偽に確定さます。
二項論理演算式では、このように、評価されない項がありますが、評価される項に関し
ては、どのような複雑な式でも、左側にある項(先に現れる項)のほうが、右側にある項
(後に現れる項)よりも、必ず先に評価されます。
●真偽による二者択一演算子
真偽判定の結果に基づいて二者のうちの1つを選択する演算子があります。この演算子
の表記形式は、以下のような三項形態になります。
C ? A : B
この表記全体で1つの式になります。この式の値は、C の評価値の真偽判定結果が、真な
らば、A の評価値になり、偽ならば、B の評価値になります。なお、この C, A, B は、
任意の式で、C の評価値の真偽判定は、論理演算子の真偽判定と同じです。
真偽による二者択一は、この演算子を使わなくても、if-else 文でも実現できますが、
この演算子を使ったほうが、簡素に表記できます。たとえば、変数 x と変数 y の最小値
を引数にして、関数 func をコールする場合、この演算子を使うと、
func( x <= y ? x : y );
と書けますが、これを、if-else 文で書くと、
if( x <= y ) func( x );
else func( y );
のようになります。
上記の表記形式 C ? A : B において、C の評価値の真偽判定結果が、真ならば、評価
されるのは A だけで、B は評価されません。また、偽ならば、A は評価されません。た
とえば、
X = ( Y < 0 ) ? f1() : f2();
では、Y が負の時に、関数 f1() の返値が X に代入されます。この時、関数 f2() は、
コールされません。また、Y が負でない時、関数 f2() の返値が X に代入されます。こ
の時、関数 f1() は、コールされません。
上記の表記形式 C ? A : B において、A の評価値のデータ型と、B の評価値のデータ
型は、必ずしも、同じである必要はありません。たとえば、
X = ( Y > 1 ) ? 2 : "1以下";
では、Y が 1 よりも大きい時は、X に整数値 2 が代入され、Y が 1 以下の時は、X に
"1以下" という文字列が代入されます。
●代入演算子
代入演算子には、 = := <- の3つがあります。各代入演算子の作用の概要は、以下
の通りです。
A = B A に B の複製を代入する(複製代入)
A := B A に B の参照を代入する(参照代入)
A <- B A に B の実体を移動する(移動代入)
各代入演算子の作用の違いは、主に、右辺( 上記の右項 B )の対象が箱である場合に
現れます。また、左辺( 上記の左項 A )の対象の扱いが、各代入演算子で異なります。
この詳細は後述します。まず、どの代入演算子にも共通する事項について述べます。
どの代入演算子でも、左辺の対象、つまり、代入先は、箱、または、純粋配列の要素、
または、箱を特定する式、になります。箱を特定する評価結果にはならない定値や算術演
算式等は、代入先にはなれません。代入先が複数個ある「多重代入」も可能です。多重代
入に関しては、後に詳述します。以下に、代入先として可能な例を挙げます。
i デフォールトスコープ内にある i という名前の箱
::ABC グローバルスコープ内にある ABC という名前の箱
.X.Y メンバースコープ内にある X という名前の箱の中にある
Y という名前の箱
^V[k+1] モジュールスコープ内にある V という名前の連想配列の
インデックス k+1 に対応する要素の箱
@d(i) 関数スタティックスコープ内にある d という名前の純粋配列の
インデックス i の要素
f() デフォールトスコープ内にある f という名前の関数が返す
参照先の箱(箱への参照以外を返す関数は不可)
(x,y,z) デフォールトスコープ内にある x, y, z という名前の3つの箱
(これは多重代入になります)
代入先の箱は、代入前に宣言しておく必要はありません。代入時点で、
代入先の箱が存在しない場合、
その箱は、新規に生成されて、その中身が、代入される内容に設定されます。
この場合、代入先の箱は、そのスコープが特定されていれば、そのスコープ内に
生成されます。デフォールトスコープの時には、現実行中の関数のローカル
スコープ内に生成されます。
代入先の箱が存在する場合、
その箱の中身は、代入される内容に入れ代わります。この時、もとの内容は消失
します。箱の中身のデータ型は、固定的ではないので、代入の前後で、箱の中身
のデータ型が変わってもまったく問題ありません。
たとえば、グローバルスコープ内に XYZ という名前の箱が存在しないときに、
::XYZ = "This is XYZ";
を実行すると、グローバルスコープ内に XYZ という名前の箱が新規に生成されて、
その中身が、"This is XYZ" という文字列になります。次に、
::XYZ = 123;
を実行すると、もとの "This is XYZ" の文字列は破棄されて、その箱の中身は、123
という整数値に変わります。
また、現実行中の関数のローカルスコープ内に P という名前の箱が無い時に、
P.X = 45.6;
P.Y = 78.9;
を実行すると、現実行中の関数のローカルスコープ内に P という名前の箱が、新規に
生成されて、その箱の中に、実数値 45.6 が入った箱 X と、実数値 78.9 が入った箱 Y
が、生成されます。この状態で、次に、
P = 0;
を実行すると、箱 P の中にあった箱 X と箱 Y は、破棄されて、箱 P の中身は、
整数値 0 に変わります。
代入先には、純粋配列の要素を指定できます。たとえば、
V(i,j) = 10;
とすると、デフォールトスコープ内にある V という名前の純粋配列のインデックス
(i,j) が指す要素に、10 を代入します。
代入先が純粋配列の要素の場合、その純粋配列は、代入前に宣言しておく必要があります。
また、純粋配列の要素のデータ型は固定的で、数値以外は代入できません。純粋配列に関
しては、「純粋配列」の章で詳しく説明します。
代入先には、箱への参照を返す関数が使えます。たとえば、グローバルスコープ内の箱
A への参照を返す関数 F が、
function F() { return ::A'ref; }
と定義されていて、
F() = 1;
を実行すると、箱 A に 1 が代入されます。なお、この場合、箱 A は事前に存在してい
る必要があります。
次に、各代入演算子の作用の違いについて述べます。この主な違いは、右辺の対象が箱
である場合に現れます。以下の説明では、特に断らない限り、この場合を前提にします。
●複製代入
複製代入では、左辺の対象が箱の場合、その箱に、右辺の箱の中身の複製が代入されま
す。その結果、左辺の箱は、右辺の箱の言わばクローンになります。つまり、左辺の箱と
右辺の箱は、中身はそっくり同じですが、実体は個別に存在します。たとえば、
X = "This is a string.";
Y = X;
を実行すると、"This is a string." という文字列データは、2つ存在して、1つは、箱
X の中に、もう1つは、箱 Y の中に存在することになります。
階層構造の箱を複製することも可能です。以下に、簡単ですが、2個の箱が入っている
箱を複製する例を示します。
X.A = 1;
X.B = 2;
を実行すると、箱 X の中には、整数値 1 が入った箱 A と、整数値 2 が入った箱 B が、
生成されます。次に、
Y = X;
を実行すると、箱 Y の中には、整数値 1 が入った箱 A と、整数値 2 が入った箱 B が、
箱 X の中からコピーされます。その結果、箱 X.A と箱 Y.A は、同じ値で、各実体は、
別々に存在します。また、箱 X.B と箱 Y.B に関しても同様です。ちなみに、箱 X と箱
Y は、中身の構成と各値は同じでも、その実体が別の構造体なので、X == Y は、偽にな
ります。
本言語では、複製代入が標準の代入になります。そのため、複製代入を「標準代入」と
いうこともあります。どの代入演算子を使ってもその作用が同じ場合には、通常、複製代
入演算子を使います。参照代入演算子と移動代入演算子は、各固有の用途に限定して使い
ます。
●参照代入
参照代入では、左辺の対象が箱の場合、その箱には、右辺の箱への参照が代入されます。
参照は、実体の箱への言わばポインターです。参照代入では、このポインターが設定され
るだけで、複製代入のように、箱の実体の複製が作られるわけではありません。そのため、
複製を作る必要が特になければ、参照代入を使うほうが、複製代入を使うよりも、高速で、
かつ、メモリーの節約になります。
参照が代入された箱(その中身が他の箱への参照である箱)にアクセスすると、その実
質の対象は、その箱ではなく、その箱の参照先の箱になります。ただ、参照代入の左辺の
対象の箱に限っては、その箱自身が実質の対象になります。(参照に関しては、「参照」
の章で詳しく説明します。)たとえば、
X = 1; // 箱 X に整数値 1 を入れる
Y := X; // 箱 Y に箱 X への参照を設定する
を実行して、次に、
Y = 2;
を実行すると、この標準代入の左辺の実質の対象は、箱 Y ではなく、箱 Y の参照先の箱
X になり、箱 X に整数値 2 が代入されます。この時、箱 Y の中身はそのままです。
参照代入では、右辺を、まず参照に評価しようとしますが、右辺が、箱でない場合、つ
まり、定数か、または、何らかの値が導かれる演算式のような場合には、参照にはなり得
ないので、右辺の評価値は、通常の評価値になります。したがって、この場合、左辺の箱
には、右辺の通常の評価値が代入されます。なお、この場合も、代入先は、左辺の箱自身
になります。たとえば、上例の続きで、箱 Y に箱 X への参照が設定されている時、
Y := 3;
を実行すると、参照代入の左辺の実質の対象は、左辺の箱 Y 自身になるので、箱 Y の中
身が整数値 3 に変わります。なお、この時、箱 X の中身は変わりません。
●移動代入
移動代入では、左辺の対象が箱の場合、その箱へ、右辺の箱が移動されます。つまり、
右辺の箱の実体の名前と所属スコープが、左辺の箱の名前と所属スコープに変わります。
たとえば、
X = 1;
Y <- X;
を実行すると、箱 Y に整数値 1 が入り、箱 X は実在しなくなります。これは、
X = 1;
Y = X;
delete X;
を実行するのと、結果的には同じですが、移動代入では、実体が移動するだけなので、実
体の複製と削除を行なうよりも、高速に処理されます。また、実体の移動では、使用メモ
リー量は増減しません。ただし、名前とスコープの変更に伴う使用メモリー量の増減はあ
ります。
階層構造の箱を移動することも可能です。この移動で実質的に変わるのは、根元の箱の
名前と所属スコープだけです。根元の箱よりも下位の階層にあるどの箱も、その名前と
中身は変わりません。また、上下左右の相対関係もそのまま保たれます。以下に、簡単で
すが、2個の箱が入っている箱を移動する例を示します。
P.A = "This is A.";
P.B = "This is B.";
Q <- P;
を実行すると、箱 Q の中に、箱 A と箱 B が入ります。これらの箱の中身は、それぞれ、
文字列 "This is A." と "This is B." です。この時、箱 P は実存しなくなっています。
階層構造の箱を移動する場合、その階層構造の中の箱へ、その階層構造の根元の箱を移
動することはできません。たとえば、
X.A <- X;
は、実行エラーになります。
●多重代入
代入式の左辺と右辺には、複数項を指定できます。複数項の表記形式は、
( 項, 項, ・・・ )
のように、各項をコンマで区切り、全体を丸括弧で囲みます。項の個数には特に制限はあ
りません。
複数項を使った代入を、多重代入と呼びます。多重代入は、複製代入、参照代入、移動
代入のどの代入でも、可能です。多重代入では、左辺と右辺の複数項内で、各同順位の項
が対応します。たとえば、複製代入で、
( A, B ) = ( 1, 2 );
を実行すると、箱 A に 1 が代入され、箱 B に 2 が代入されます。次に、
( X, Y, Z ) = ( A + 1, B + 2, 3 );
を実行すると、箱 X, Y, Z には、それぞれ、2, 4, 3 が代入されます。
また、移動代入で、
( A, B, C ) <- ( X, Y, Z );
を実行すると、箱 A, B, C へ、それぞれ、箱 X, Y, Z の実体が移動されます。
多重代入で、左辺の項数が、右辺の項数よりも多い場合、余った左辺の各項は、空箱に
なります。たとえば、
( A, B, C ) = ( 1, 2 );
を実行すると、箱 A に 1 が代入され、箱 B に 2 が代入されますが、箱 C は、空にな
ります。また、
( X, Y ) = 1;
を実行すると、箱 X に 1 が代入され、箱 Y は、空になります。
多重代入で、左辺の項数が、右辺の項数よりも少ない場合、余った右辺の各項は、評価
されますが、どこにも代入されません。たとえば、
( A, B ) = ( 1, 2, 3 );
を実行すると、箱 A に 1 が代入され、箱 B に 2 が代入されますが、3 の項は、どこに
も代入されません。この状態から、
X = ( A++, B++ );
を実行すると、箱 X には、1 が代入され、箱 A の中身は 2 になります。B++ の結果は
どこにも代入されませんが、評価は行なわれるので、箱 B の中身は 3 になります。
関数が返す複数の値を、多重代入することも可能です。たとえば、
function F() { return ( 10, 20, 30 ); }
という関数は、10, 20, 30 の3値を返しますが、この関数を使って、
( X, Y, Z ) = F();
を実行すると、箱 X, Y, Z には、それぞれ、10, 20, 30 が代入されます。
関数が返す複数の参照先への箱に、多重代入することも可能です。たとえば、
function G() { return ( ::A'ref, ::B'ref ); }
という関数は、グローバルスコープ内の箱 A と箱 B への参照を返しますが、この関数を
使って、
G() = ( -1, -2 );
を実行すると、グローバルスコープ内の箱 A, 箱 B に、それぞれ、-1, -2 が代入されま
す。なお、この場合、グローバルスコープ内の箱 A と 箱 B は、事前に存在している必
要があります。
多重代入でも、代入を連結できます。たとえば、
( A, B ) = ( X, Y ) = ( 1, 2 );
を実行すると、整数値 1, 2 は、それぞれ、まず、箱 X, Y に代入されて、次に、箱 A,
B に代入されます。
複数項は、複製代入、参照代入、移動代入以外の演算子には、使用できません。たとえ
ば、( A, B ) + ( X, Y ) とすることはできません。
複数項内の項を、複数項にもできますが、結果は、複数項にしない場合と同じです。た
とえば、( A, ( B, C )) は、( A, B, C ) と同じです。
複数項は、必ず、丸括弧で囲う必要があります。さもなければ、各項の区切りのコンマ
は、式の区切りのコンマとして解釈されます。たとえば、
A, B = X, Y;
は、A という式と、B = X という式と、Y という式の、3つの式なら成る文として、
解釈されます。
●演算併用の代入演算子
算術演算またはビット操作演算を併用した代入を行なう演算子が10個あります。以下
に、これらの演算子の作用を示します。
A += B A = A + B と同じ
A -= B A = A - B と同じ
A *= B A = A * B と同じ
A /= B A = A / B と同じ
A %= B A = A % B と同じ
A &= B A = A & B と同じ
A |= B A = A | B と同じ
A ^= B A = A ^ B と同じ
A <<= B A = A << B と同じ
A >>= B A = A << B と同じ
これらの演算子を使うほうが、算術/ビット操作演算子と標準代入演算子を組み合わせ
て使うよりも、以下の利点があります。
・処理速度が速い
・中間コード量が少ない
・表記が簡素
演算併用の代入演算子の左辺の対象、つまり、代入先は、箱、または、純粋配列の要素
になる必要があります。また、代入先は、代入前の演算で必要になるので、適切なデータ
型の値であることが必要です。これらの制約やその他の制約は、基本的に、その演算子と
同等の作用になる算術/ビット操作演算子と標準代入演算子を組み合わせて使うときの制
約と共通です。
●増減代入演算子
増減代入演算子には、作用対象に1を加算して代入する単項演算子 ++ と、作用対象に
1を減算して代入する単項演算子 -- があります。以下に、これらの演算子の作用を示し
ます。
++A A に 1 を加算した値を A に代入する この式値は代入後の値
A++ A に 1 を加算した値を A に代入する この式値は代入前の値
--A A から 1 を減算した値を A に代入する この式値は代入後の値
A-- A から 1 を減算した値を A に代入する この式値は代入前の値
増減代入演算子は、前置演算子としても、後置演算子としても使えます。どちらの場合
でも、代入される値は同じですが、その式としての値が異なります。前置の場合、その式
値は、代入後の値になり、後置の場合、その式値は、代入前の値になります。たとえば、
X = Y = 0;
print ++X, Y++;
を実行すると、1, 0 とプリントされます。次に、
print X, Y;
を実行すると、1, 1 とプリントされます。
作用対象に単に1を加減算して代入するのは、加減算演算子と代入演算子の組み合わせ、
または、演算併用の代入演算子 += と -= でも可能ですが、増減代入演算子を使うほうが、
・処理速度が速い
・中間コード量が少ない
・表記が簡素
という利点があります。
増減代入演算子の対象、つまり、代入先は、箱、または、純粋配列の要素になる必要が
あります。また、代入先が箱の場合、その中身は数値に限定されます。
●スコープ規定演算子
スコープ規定演算子には、システムスコープを規定する5つの前置演算子
:: . ^ $ @
と、一般の箱内のスコープを規定する2つの二項演算子
:: .
があります。
スコープに関しては、「スコープ」の章で詳しく説明します。ここでは主に、スコープ
規定演算子の機能について述べます。
システムスコープを規定する前置演算子と対象の箱の所属するスコープの関係を、以下
に示します。( ここで、A は任意の箱名 )
::A グローバルスコープ内の箱 A
.A メンバースコープ内の箱 A
^A モジュールローカルスコープ内の箱 A
$A スレッドローカルスコープ内の箱 A
@A 関数スタティックスコープ内の箱 A
A デフォールトスコープ内の箱 A
一般の箱内のスコープを規定する二項演算子は、以下のようになります。( 以下で、A
は任意のスコープ規定式、B は任意の箱名 )
A::B スコープ A 内の箱 B (参照型)
A.B スコープ A 内の箱 B (降下型)
一般に、箱 X の中に、箱 Y が入っている場合、箱 X からは直接、箱 Y が見えるので、
箱 X は、箱 Y のスコープになります。箱 X が、たとえば、デフォールトスコープ内に
あると、箱 X の中に入っている箱 Y は、デフォールトスコープから、
X.Y
として指定できます。もし、箱 X がグローバルスコープ内にあれば、
::X.Y
として指定できます。
箱 X の中にある箱 Y の中に、さらに、箱 Z が入っていると、この箱 Z は、デフォー
ルトスコープから、
X.Y.Z
として指定できます。もし、箱 X がスレッドローカルスコープ内にあれば、
$X.Y.Z
として指定できます。この箱 Z の中に、さらに別の箱が入っている場合も同様です。こ
のように、二項演算子「.」は、通常、箱の中のスコープを降下するのに使います。
スコープは、参照を使っても規定できます。たとえば、上記の箱 X, Y, Z の包含関係
( デフォールトスコープ内の箱 X の中に箱 Y があり、さらに、その箱 Y の中に箱 Z
がある場合)において、
P := X.Y;
として、箱 X の中にある箱 Y への参照を、デフォールトスコープ内の箱 P に設定すると、
P::Z
で、参照箱 P から、箱 X の中にある箱 Y の中にある箱 Z を指定できます。もし、この
参照を、モジュールローカルスコープ内の箱 Q に設定すると、
^Q::Z
で、参照箱 Q から、箱 X の中にある箱 Y の中にある箱 Z を指定できます。このように、
二項演算子「::」は、通常、参照によるスコープの指定に使います。
慣習上、スコープの降下用には、二項演算子「.」を使い、参照によるスコープの指定
用には、二項演算子「::」を使います。しかし、機能的にその用途に限定されているわけ
ではありまん。
二項演算子「.」と「::」との本質的な違いは、それが、代入先のスコープを規定してい
て、そのスコープが存在しないか、または、不正な場合に現れます。この場合、二項演算
子「.」で規定したスコープであれば、そのスコープが強制的に形成されます。一方、二
項演算子「::」で規定したスコープであれば、例外が発生します。たとえば、デフォール
トスコープ内に、箱 S が無いか、または、箱 S があっても、スコープとして有効でない
状態で、
S.X = 1;
を実行すると、箱 S は、強制的にスコープとして有効な箱として形成され、その中に、
箱 X が生成されて 1 が代入されます。一方、同じ状態で、
S::X = 1;
を実行すると、例外が発生します。なお、この場合、もし、箱 S がスコープとして有効
だったら、箱 X は、代入前に存在しなくても、代入時点で自動的に生成されます。この
時、例外は発生しません。
●括弧演算子
本言語では、丸括弧 ( ) と角括弧 [ ] を、特殊な演算子として扱います。これらをそ
れぞれ、「丸括弧演算子」「角括弧演算子」と呼ぶことにします。また、両者を総称して
「括弧演算子」と呼ぶことにします。括弧演算子は、括弧の左外側に置かれた項が、作用
対象で、括弧の内側が、引数並びになります。
●丸括弧演算子
丸括弧演算子では、以下のような表記形態になります。
箱名( 引数並び )
丸括弧演算子は、作用対象の箱の中身の種類に応じて、以下の作用があります。
・関数の場合、その関数をコールする
・クラスの場合、そのクラスのインスタンスを生成する
・純粋配列の場合、その純粋配列の要素を特定する
いずれの場合も、括弧内の引数並びがパラメーターになります。
引数並びは、空か、1つだけの式か、または、たとえば、
式, 式, 式
のように、2つ以上の各式をコンマ , で区切ったリストになります。この場合の各式は、
空であっても構いません。たとえば、
式, , 式
では、2番目の式が空になっています。引数並びに書ける式の最大個数は、現状、100 ま
でです。
関数、クラスとインスタンス、純粋配列に関しては、関連各章で詳しく説明します。
●角括弧演算子
角括弧演算子では、以下のような表記形態になります。
箱名[ インデックス ]
この表記は、角括弧演算子の作用対象の「箱名」の箱内にある「インデックス」に対応
する連想名の箱を特定することになります。たとえば、
X[3,4]
は、デフォールトスコープ内の箱 X の中にある、[3,4] という連想名の箱になります。
角括弧演算子のインデックスは、丸括弧演算子の引数並びと同様です。
角括弧演算子の作用対象の箱は、省略可能で、その場合、[ インデックス ] の連想名
の箱自身を特定することになります。たとえば、
["Content-Type"]
は、デフォールトスコープ内にある ["Content-Type"] という連想配列名の箱になります。
角括弧演算子を並べて書くと、多次元の連想配列になります。たとえば、
::Y["C-3PO"][5]
は、グローバルスコープ内の箱 Y の中の ["C-3PO"] という連想名の箱の中の [5] という
連想名の箱になります。
連想配列に関しては、「連想配列」の章で詳しく説明します。
●関数コール演算子(非標準型)
本言語では、標準型の関数コール形式以外にも、以下の演算子を使った関数コール形式
があります。
リレー型関数コール演算子 '
これは、以下のような形式で関数を呼び出す時に使います。
式 ' 関数名( 引数並び )
この関数コール形式の詳細は、「関数」の章の以下の節で説明しています。
・リレー型(後置型)の関数コール形式
代入型関数コール演算子 :==
これは、以下のような形式で関数を呼び出す時に使います。
関数名( 引数並び ):== 式
この関数コール形式の詳細は、「関数」の章の以下の節で説明しています。
・代入型の関数コール形式
コマンド型関数コール演算子 ~
これは、以下のような形式で関数を呼び出す時に使います。
関数名~ 引数並び
この関数コール形式の詳細は、「関数」の章の以下の節で説明しています。
・コマンド型の関数コール形式
●ラベル名演算子
ラベル名演算子 : は、作用対象のラベル名を、文字列へ変換する前置演算子です。こ
れは、例外処理で、エラーワープ先( OnValueError, OnFuncError 等 )や、代替ラベル(
DefLabel )に、ラベル名を設定する時などに使います。たとえば、
OnValueError = :ErrorEntry;
を実行すると、エラーワープ先を指定する OnValueError 変数に、ErrorEntry というラ
ベル名に対応する文字列が代入されます。また、
DefLabel = :Label[i];
を実行すると、代替ラベルを指定する DefLabel 変数に、当時点での変数 i の値におけ
る Label[i] という連想配列型のラベル名に対応する文字列が代入されます。
ラベル名演算子の作用対象が不正な場合、その評価値は null になります。この場合、
例外は発生しません。
なお、ラベル名を、文の前に付ける時、
ラベル名: 文
という表記になりますが、このコロンと、ラベル名演算子のコロンとは、関連はあります
が、文法的/機能的には全く別物です。
例外処理に関しては、「例外処理」の章で詳しく説明します。