この文書のライセンスはDiksamのライセンスに準じます。
この文書の使用によって生じたいかなる損害についても、 作者は一切の責任を負いません。 また、商用、非商用を問わず、自由に複製、改変、再配布していただいてかまいません。 全体を再配布するのも、部分的に抜き出して自分の配布物に組み込むのも自由です。
宣言部は、require宣言の並び、 rename宣言の並びから構成される。 宣言部は省略可能であるが、記述する場合は、 ソースファイルの冒頭に記述しなければならない。
トップレベル(toplevel)は文(statement) の並びである。Diksamのプログラムは、 処理系より指定されたソースファイルのトップレベルから開始される。
関数定義(function definition)は、 他の個所から呼び出されることを想定した関数を定義する。 トップレベルの文が順に実行される際、 関数定義は飛ばして実行される。 関数は前方参照が許されており、 呼び出しの個所の前にあっても後ろにあってもよい。 関数宣言(function declaration)は、関数の シグネチャ(signature)だけを宣言したものである。 関数宣言には、それに対応する定義が存在しなければならない。
型定義は、クラス定義(class definition)または インターフェイス定義(interface definition)または 列挙型定義(enumerated type definition)または delegate型定義(delegate type definition)のいずれかである。
型定義は、別の場所で使用されることを想定したデータ型の定義である。 関数同様、トップレベルの文が実行される際には飛ばして実行され、 前方参照が許される。
以下にいくつかの例を示す。
トップレベルのみから構成されるDiksamプログラムの例:
println("hello, world.");
冒頭に宣言部を含むDiksamプログラムの例:
rename diksam.lang.println print_line; print_line("hello, world.");
関数定義を含むDiksamプログラムの例:
void func() { println("hello, world."); } func();
クラス定義を含むDiksamプログラムの例:
public class Point { private double x; private double y; void print() { println("x.." + this.x + ", y.." + this.y); } constructor initialize(double x, double y) { this.x = x; this.y = y; } } // Pointクラスのインスタンスを生成 Point p = new Point(10, 20); // その内容を表示する。 p.print();
宣言部のrequire宣言においてパッケージ (package)を指定することで、他のソースファイルで定義された関数、 クラス等を使用することができる。
Diksamでは、ひとつのソースファイル(拡張子.dkh) またはふたつのソースファイル(拡張子.dkhと.dkm) の組み合わせでひとつのパッケージを構成する。
以下のようにhoge.piyo.fugaパッケージを requireすることにより、fuga.dkh にで定義されたクラスおよび関数が使用可能となる。
require hoge.piyo.fuga;
.dkhファイルに関数のシグネチャ宣言のみが記述されている場合、 実際にその関数が呼び出された時点で、処理系はfuga.dkh に対応する実装ファイルfuga.dkmを検索し、 コンパイル/ロードする。これをダイナミックロード (dynamic load)と呼ぶ。
requireされたソースファイルの検索ディレクトリは、 環境変数DKM_REQUIRE_SEARCH_PATHに、UNIX系の環境ではコロン、 Windows系の環境ではセミコロンで区切って記述する。 DKM_REQUIRE_SEARCH_PATHが設定されていない場合、 カレントディレクトリが唯一の検索ディレクトリとなる。
また、ダイナミックロード時の検索ディレクトリは、環境変数 DKM_LOAD_SEARCH_PATHから取得する。
diksam.langパッケージはデフォルトでrequire されているので、ユーザが自分でrequireする必要はない。
requireされたソースファイルのトップレベルは無視され、 実行は行なわれない。
複数のソースファイルをrequireした場合、関数名、 クラス名の衝突が発生する可能性がある。そのような場合は、 宣言部のrename宣言により識別子を改名することで回避する。 以下は、diksam.langパッケージのprint関数を myprintに改名する例である。
rename diksam.lang.print myprint;
トップレベルで宣言された変数(グローバル変数)は、 ソースファイルごとに独立しており、リンクは行なわれない。 あるパッケージのグローバル変数の他パッケージから参照する場合、 get_xxx(), set_xxx() のような関数を用意する必要がある。
Diksamのソースファイルは、処理系において自然な文字コードで記述する。 現状では、UNIX環境ではEUC、Windows環境ではShift-JISにて動作を確認している。
非ASCII文字は、コメントと文字列リテラル以外では使用できない。
abstract boolean break case catch class const constructor continue default delegate do double else elsif enum false final finally for foreach if instanceof int interface native_pointer new null override private public rename require return string super switch this throw throws true try virtual void while
空白(ASCIIコードにおける0x20)とタブは、ソースファイル中では、 識別子の区切り以外の意味を持たない。改行文字も同様である。
変数名、関数名、クラス名等に使用する識別子は、 以下の規則に従わなければならない。
Diksamにおいて、識別子には、日本語等、 ASCII文字セットに含まれない文字列を使うことはできない。
真偽値リテラルは、true(真)またはfalse (偽)のいずれかである。
整数リテラルは、「0」あるいは、 「1〜9の後ろに、 0個以上の0〜9が並んだもの」である。
また、Cと同様、0xまたは0Xを先頭に付加することで 16進表記が可能である。現状では、8進表記はサポートしていない。
実数リテラルは、「1個以上の0〜9の並び、 ピリオド、1個以上の0〜9の並び」により構成される。
「.5」や「1.」は実数リテラルではない。
文字リテラルは、1文字をシングルクオートで囲んだものである。
文字リテラルの型はintである。 実際の値は、処理系におけるワイド文字(通常はUNICODE)である。
文字列リテラルは、ダブルクォートで囲まれた文字列である。 \nは改行を、\tはタブを表わす。 \\は\そのものを意味する。
nullリテラルは、 参照型が何も指していないことを意味するリテラルである。
波括弧「{ }」の中に要素をコンマで区切って記述することで、 配列のリテラルを生成できる。 型は、配列リテラルの最初の要素の型の配列となる。
最後の要素の後ろには、コンマがあってもなくてもよい。
void型は、関数の戻り値がないことを示す特殊な型である。 関数の戻り値の型としてのみ使用可能である。
trueまたはfalseの値を取る。
整数を表現する型。表現できる値の範囲は、 コンパイラおよびVMがコンパイルされるC言語環境の int型と一致する。
実数を表現する型。表現できる値の範囲は、 コンパイラおよびVMがコンパイルされるC言語環境の double型と一致する。
文字列を表現する型。内部の表現形式は、コンパイラおよびVM がコンパイルされるC言語環境におけるワイド文字列となる。
文字列型は参照型である。よって、文字列型の変数には nullが代入可能である。 ただし、文字列自体は変更不能(immutable)であるため、 ユーザが参照型であることを意識する必要は通常はない。
ネイティブポインタ型は、ファイルポインタ(FILE*)のように、 ネイティブ関数が内部的に使用する値を保持するための型である。 ネイティブポインタ型は、ネイティブ関数以外では、 代入と等値比較以外の操作はできない。
クラス型およびインターフェイス型は、ユーザが定義する型である。 クラス定義、インターフェイス定義の詳細は、 「クラス・インターフェイス定義」を参照のこと。
クラス型、インターフェイス型はともに参照型である。
派生型は、基本型、クラス型、インターフェイス型から派生して 作り出される型である。
派生型には、以下のふたつが存在する。
Diksamにおける配列は、宣言の時点では要素数を決定せず、 動的に確保される参照型である。
関数およびメソッドは関数型となる。関数型の変数は存在せず、 関数型は、適合するdelegateに代入可能である。
intの配列の配列の宣言:
int[][] a;関数型は、delegate型を経由することで、「関数の配列」 「関数を返す関数」等を実現できる。
列挙型は、以下の形式で定義する。最後の要素の後ろには、 コンマがあってもなくてもよい。
enum 列挙型名 { 列挙子名, 列挙子名, 列挙子名, … }
列挙型の値(列挙子)は、以下のように「列挙型名.列挙子名」 の形で表現する。
Fruits.ORANGE
列挙型は、文字列を左辺とした+演算子の右辺に置くことで、 文字列への変換が可能である。
列挙型は同値比較(==, !=)および switch文の分岐での使用が可能である。
delegate型は関数を指す参照型である。 予約語delegateの後ろに関数シグネチャ宣言と同じ形式で定義する。
以下は、intふたつと ButtonKindという列挙型を引数に取り、 booleanを返すdelegate型の定義例である。
delegate boolean MouseButtonClicked(int x, int y, ButtonKind kind);
このような型定義を行なうことで、以下のように、 delegate型の変数の宣言が可能になる。
MouseButtonClicked mouseButtonHandler;
delegate型をパラメータとして受け取ったり、 戻り値として返す関数の定義も可能である。
式の中に関数名を単に記述することで、関数型の値が生成される。 関数型の値は、代入互換性のある delegate型変数に代入可能である。 関数の実引数は代入と同じなので、引数として渡すこともできる。
boolean mouseButtonHandler(int x, int y, ButtonKind kind) { …… } // buttonオブジェクトのsetMouseButtonHandlerメソッドに、 // mouseButtonHandler関数を引数として渡す。 button.setMouseButtonHandler(mouseButtonHandler);
delegate型変数には、 関数だけでなくメソッドも代入可能である。 その場合、delegate型変数は、 メソッドに対応するインスタンスを併せて保持する。
delegate型への代入は、 以下の条件を満たす場合に可能である。
配列および文字列型には、いくつかの組み込みメソッドが用意されている。
配列に対する組み込みメソッドは以下のとおり。
形式 | 意味 |
---|---|
void a.add(T value); | 配列の末尾に要素を追加する。 Tの型は、配列の要素に代入可能な型でなければならない。 |
int a.size(); | 配列のサイズを取得する。 |
void a.resize(); | 配列のサイズを変更する。 現在のサイズより小さなサイズを指定した場合、 新たなサイズより後ろの要素は捨てられる。 現在のサイズより大きなサイズを指定した場合は、 追加分の要素はその型のデフォルト値が設定される。 |
void a.insert(int pos, T value); | posで指定された添字の要素の前に、 valueで指定された値を挿入する。 Tの型は、配列の要素に代入可能な型でなければならない。 |
void a.remove(int pos) | posで指定された添字の要素を削除し、 その部分を詰める。 結果として配列のサイズは1減少する。 |
文字列に対する組み込みメソッドは以下のとおり。
形式 | 意味 |
---|---|
int s.length(); | 文字列の長さを取得する。 |
string s.substr(int pos, int len); | pos>で指定された位置の文字(先頭の文字は0)から、 第2引数で指定された長さの部分文字列を切り出し、 新しい文字列として返す。 |
また、文字列型は、配列同様[]演算子を適用することで 指定した位置の文字を取得することができる。
int ch = "日本語"[1]; // にch'本'が代入される
これにより取得する文字の型はintであり、文字リテラルと比較可能である。
式(expression)とは、 一次式(primary expression)を演算子でつないだものである。
一次式には以下のものが存在する。
Diksamでは、下記の条件において、暗黙の型変換が行なわれる。
2項算術演算子(+, -, *, /, %)および比較演算子(==, !=, >, >=, <, <=) において両辺の型が異なる場合、以下の規則にもとづき型の拡張が行なわれる。
この変換は、算術代入演算子(+=, -=, *=, /=, %=)においても同様に実施される。
Diksamにおける演算子の一覧は以下のとおり(優先度の高い順)。
演算子 | 意味 |
---|---|
++ -- () instanceof ::型:> | インクリメント、デクリメント、関数呼び出し、 クラスの型チェック、ダウンキャスト |
(単項の)- | 符号の反転 |
* / % & | ^ | 乗算、除算、剰余、ビットAND、ビットOR、ビットXOR |
+ - | 加算、減算。加算演算子は文字列の結合も可能。 | == != | 等値比較。文字列も(参照ではなく値で)比較可能。 | < <= > >= | 大小の比較。文字列の大小も比較可能(仕様はCのstrcmp() に準ずる)。 |
&& | 論理AND演算子。短絡演算子なので、a && b という式ではaが偽であればbは評価されない。 |
|| | 論理OR演算子。短絡演算子なので、ab || b という式ではaが真であればbは評価されない。 |
= += -= *= /= %= | 代入演算子。意味はCと同様。 |
, | カンマ演算子。左→右の順で式を評価し、右の式の値を返す。 |
宣言文(declaration statement)は、 変数の宣言を行なう文である。
宣言文は、
型 変数名;
もしくは
型 変数名 = 初期化式;
の形を取る(「= 初期化式」の部分を初期化子 (initializer)と呼ぶ)。
宣言文にfinalを指定することで、 その変数を書き込み禁止にすることができる (ただし、初期化子の代入は、その宣言文が実行された時点で行われるので、 .dkhファイルにて、Cの#defineに相当する定数定義を finalで行うことはできない。この用途ではconstを使用する)。
final int a = 10;
final指定した宣言文に初期化子がない場合は コンパイルエラーである。
式文(expression statement)とは、 式の後ろにセミコロンを付加したものである。
// println()関数の呼び出し式にセミコロンを付けたもの println("hello, world."); // インクリメント式にセミコロンを付けたもの i++; // 副作用のない式も式文にできるが、意味はない。 5;
if文は条件分岐を行なう文である。
if (条件式1) { // 条件式1が真の時に実行される。 } elsif (条件式2) { // 条件式1が真でなく、式2が真の時に実行される。 } else { // すべての条件式が偽の時に実行される。 }
elsif節、else節は省略可能である。 また、elsif節は任意の個数記述可能である。
C等とは異なり、文がひとつであっても、 波括弧{ }を省略することはできない。
switch (a) case 1 { // aが1の時に実行される。 } case 2, 3 { // aが2または3の時に実行される。 } default { // aが1でも2でも3でもない場合に実行される。 }
Diksamのswitch文は、Cとは異なりfall throughではない。 あるcaseにマッチした場合、breakを書かなくても、 次のcaseに処理が移ることはない。 また、各case節には波括弧({ })が必須である。
複数の値について共通のcaseを実行する場合は、 値をコンマで並べて書くことができる。
どのcaseにもマッチしなかった場合、 default節が実行される。
各case節は、
switchに記述された式 == caseに記述された式
が成立する場合に実行される。==演算子が使える限り、 式の型に制限はない。ただし、 ==で両辺の型を合わせるために行なわれる型変換は、 switch文では行なわれない。
while文は繰り返しを行なう文である。
識別子: while (条件式) { // 条件式が真の間、この個所が繰り返し実行される。 }
「識別子:」の部分をラベル(label)と呼ぶ。 ラベルは省略可能である。
if文と同様、文がひとつであっても波括弧 ({ })を省略することはできない。
do while文は後判定型の繰り返しを行なう文である。
識別子: do { // この個所は最初の一度は必ず実行され、 // 以後、条件式が真である間、繰り返し実行される。 } while (条件式);
「識別子:」の部分をラベル(label)と呼ぶ。 ラベルは省略可能である。
if文と同様、文がひとつであっても波括弧 ({ })を省略することはできない。
for文は繰り返しを行なう文である。
識別子: for (第1式; 第2式; 第3式) { // 第2式が真の間、この個所が繰り返し実行される。 }
「識別子:」の部分をラベル(label)と呼ぶ。 ラベルは省略可能である。
上記のfor文は、continue時に 式3が実行される点を除き、以下のwhile文と同等である。
第1式; while (第2式) { // 文の並び 第3式; }
第1式、第2式、第3式はともに省略可能である。 第2式を省略した場合、真を意味する。
return文は関数から脱出する文である。
return 式;
関数がvoid型の場合、式は省略可能である。
break文は、ループを脱出する文である。
break 識別子;
識別子は省略可能である。省略した場合、 もっとも内側のループから脱出する。
識別子を指定した場合、 その識別子のラベルを持つループから脱出する。
continue文は、ループの末尾にジャンプする文である。
continue 識別子;
識別子は省略可能である。省略した場合、 もっとも内側のループが対象となる。
識別子を指定した場合、 同じ識別子のラベルを持つループが対象となる。
for文を対象としてcontinueを実行した場合、 continueの次にfor文の第3式が評価される。
do while文を対象としてcontinueを実行した場合、 continueの次に条件式が評価され、 偽の場合はループを終了する。
try文は、例外処理を行なうための文である。
try { // 文の並び } catch (例外の型 変数名) { // 文の並び } catch (例外の型 変数名) { // 文の並び } finally { // 文の並び }
try節において例外が発生した場合、 その例外のクラスにマッチするcatch節が上から順に検索される。 いずれかのcatchにマッチした場合、そこに処理が移る。 発生した例外は、catch節にて宣言された変数にセットされる。 この変数は暗黙にfinalであり、代入はできない。
finally節は、例外が発生してもしなくても、 例外がcatchにマッチしてもしなくても、必ず実行される。 try節またはcatch節においてbreak, continue, return等の文が実行され 処理が中断された時も、finally節は必ず実行される。 逆に、finally節の実行をbreak, continue, return等の文で中断しようとすると、コンパイルエラーとなる。
throw文は、例外をスローする文である。
通常は、下記のように、例外を表す式を付加する。
throw e;
try節内で発生した例外は、 対応するcatch節が存在すれば、catch節にて捕捉される。 対応するcatch節が存在しないか、 例外がtry節以外で発生した場合、 現在実行中の関数を中断し、呼び出し元に処理が戻る。 呼び出し元でも例外がcatchされなければ呼び出し階層を順に遡り、 トップレベルにおいても補足されなければ、 スタックトレース(stack trace)を出力して処理を中止する。
Diksamにおいては、スタックトレースは、 例外がthrowされた時点でクリアされ、 関数の呼び出し階層を戻る時点、 もしくはトップレベルを中断する時点で追加される。
よって、catch節で補足した例外を再度スローしなおす場合、 スタックトレースをクリアしなくなければ、 以下のようにthrow;だけを単独で記述する。
throw;
関数は、以下の形式で定義する。
型 関数名 (パラメータの並び) throws節 { 文の並び }
throws節は省略可能である。 その関数が発生する可能性がある例外を、コンマ区切りで列挙して記述する。
以下に例を示す。
// 引数としてふたつの整数を受け取り、その和を返す関数 int sum(int a, int b) { return a + b; } // 引数として配列とインデックスを受け取り、 // その個所の要素を返す関数。 int getAt(int[] array, int index) throws ArrayIndexOutOfBoundsException { return array[index]; }
関数の仮引数は代入済みのfinal なローカル変数として扱われる。 よって、仮引数に代入することはできない。
関数が別のソースファイルで定義されている場合、 以下のようにシグネチャ宣言を記述することで、 その関数を呼び出す式のコンパイルが可能となる。
int func(double x);
Cのプロトタイプ宣言等とは異なり、 Diksamでは、仮引数名(上記のx)を省略することはできない。 また、仮引数名も型の一部とみなされ、関数定義と異なる場合はエラーとなる。
たとえば、以下のようなシグネチャ宣言に対し、
void drawLine(double x1, double y1, double x2, double y2);
以下のような関数定義は許されない。
void drawLine(double x1, double x2, double y1, double y2);
関数内で宣言された変数はローカル変数(local variable) となる。
ローカル変数のスコープは、以下の両方の条件を満たす範囲である (現状で、グローバル変数のスコープは、ブロックの影響を受けない)。
ローカル変数の生存期間は、その関数を抜けるまでである。
クラスは、以下の形式で定義する。
クラス修飾子 class クラス名 : スーパークラス, 実装するインターフェイスの並び { フィールド、メソッド、コンストラクタ定義の並び }
クラス修飾子には以下のものがある。
アクセス修飾子とabstract修飾子は順不同である。
アクセス修飾子にpublicを指定すると、そのクラスは、 別のパッケージから使用可能となる。無指定の場合、 該当のパッケージからのみ使用可能である。
abstractを指定したクラスは抽象クラス (abstract class)とみなされる。抽象クラスはnew によりインスタンス化することができない。 逆にabstract指定のないクラスは 具象クラス(concrete class)であり、 abstractメソッドを含むことができない。
スーパークラスおよび実装するインターフェイスは、 C++, C#と同様、コロンの後ろにコンマで区切って列挙する(順不同)。 継承しない場合、コロン以下は省略可能である。
Diksamはクラスについては単一継承のみ可能である。 また、Diksamでは、継承可能なのは抽象クラスのみである。
インターフェイスは、複数実装することができる。
インターフェイスは、以下の形式で定義する。
アクセス修飾子 interface { // メソッド定義の並び }
インターフェイスは、abstract メソッド以外記述できないことを除き、クラスと同等の機能を持つ。
アクセス修飾子は、クラス同様、 publicを指定するか無指定かのいずれかである。 インターフェイスは常にabstractであるので、 abstractを指定するとコンパイルエラーとなる。
Diksamでは、 インターフェイスが他のインターフェイスを継承することはできない。
フィールドは、以下の形式で定義する。
アクセス修飾子 final修飾子 型 フィールド名 初期化子;
フィールドに対するアクセス修飾子は、public、 private、もしくは無指定である。 publicは別のパッケージから使用可能、 無指定は同一パッケージ内から使用可能、 privateはクラス内のみで使用可能であることを それぞれ意味する。
finalを指定することで、そのフィールドは代入不能となる。
メソッドは、以下の形式で定義する。
メソッド修飾子 型 メソッド名 (パラメータの並び) throws節 { 文の並び }
関数定義と同様、throws節は省略可能である。 また、abstract修飾子を付けた場合、 関数本体(文の並びを含むブロック)は記述できない。
メソッド修飾子には以下のものがある。
virtual指定のないメソッドはオーバーライドできない。 また、オーバーライドする場合は、 必ずoverrideを指定しなければならない。
スーパークラスもしくは 実装するインターフェイスのメソッドと同名のメソッドを、 サブクラスで定義することをメソッドオーバーライド (method override)と呼ぶ。 メソッドオーバーライドの際は以下の条件が満たされなければならない。
コンストラクタは、 インスタンスの生成時に自動的に呼び出されるメソッドである。 Diksamでは、コンストラクタの定義には、constructor修飾子を使用する。
public constructor initialize() { // コンストラクタの処理を記述する。 }
constructor修飾子は、型名と同じ場所に記述する。 よってpublicやvirtual等のメソッド修飾子よりは 後ろに記述しなければならない。
Java, C++, C#等とは異なり、Diksamでは、 コンストラクタに任意の名前を付けることができる。 インスタンスをnewする際に、以下の形式でコンストラクタを指定する。
// myinit()はユーザが作成したコンストラクタ Point p = new Point.myinit(x, y);
メソッド名を指定せず、 単にnew Point(x, y)のように記述した場合、 initializerという名前のコンストラクタが呼び出される。 また、クラス定義の際、コンストラクタをひとつも定義しなければ、 コンパイラは自動的に以下のようなデフォルトコンストラクタ (default constructor)を生成する。
public virtual constructor initialize() { super.initialize(); ←スーパークラスが存在する場合のみ }
Diksamのコンストラクタは、 サブクラスにてオーバーライド可能である。 ただし、コンストラクタの場合、 「メソッドオーバーライド」 で記述した引数と戻り値の型チェックは実施されない。
また、コンストラクタはインスタンス生成の時のみ使用可能であり、 インスタンス生成後に通常のメソッドのように呼び出すことはできない。
定数は、キーワードconstを使用して定義する。
const int MINUTES_A_DAY = 24 * 60;
constの初期化子は、そのソースファイルがロードされた時点で 評価される。
Fileクラスは、CのFILE*に相当する型である。 内部にnative_pointer型としてCのFILE*を 保持している以外、特にメソッド等は存在しない。
例外クラスのクラス階層のトップにあるのがExceptionクラスである。 以下のフィールドとメソッドを持つ。
なお、StackTraceクラスの定義は以下のとおり。
class StackTrace { int line_number; string file_name; string function_name; }
Diksamには以下の算術関数が存在する。意味はCのmath.hと同じである。