Palanとは
Palanとは、より簡単で安全なC言語の代替となりうる言語を目指して、少しづつ開発しているプログラム言語(コンパイラ)です。ver0.1はドラフト版としての最初のコードFixバージョンとなります。まだ、最低限の言語機能しか搭載されていません。アセンブラにコンパイルして実行ファイルを生成するため、スクリプト言語に比べて高速に動作すると思われますが、まだパフォーマンスの最適化はしていません。
Qiitaに開発日記を公開しています。プログラミング言語Palan開発日記
このリファレンスは古くなりました。ver0.2をご覧ください。v0.2 リファレンス
設計思想・方針
明確さと簡易さを両立させた言語を目指しています。また、感覚的なことになりますが開発者本人にとっての書き味のよさを重要視しています。
サンプルプログラムと解説
ver0.1では最低限の言語機能しかありませんが、下記のようなクイックソートのプログラム程度であれば作成できます。
ccall int32 printf();
const N = 10;
// start
int16[N] data;
int32 i=0;
// init data.
while i<N {
i*13 % 9 -> data[i];
i+1 -> i;
}
// sort
printf("before:");
show(data);
printf("after:");
data ->> quicksort(0, N-1)
->> data
-> show();
func show(int16[N] data)
{
int32 i=0;
while i<N {
printf(" %d", data[i]);
i+1 -> i;
}
printf("\n");
}
func quicksort(int16[N] >>data, int32 left, right)
-> int16[N] data
{
if left >= right {
return;
}
int32 i, mid;
int32 temp;
left -> mid;
left+1 -> i;
while i <= right {
if data[i] < data[left] {
mid+1 -> mid;
data[mid] -> temp;
data[i] -> data[mid];
temp -> data[i];
}
i+1 -> i;
}
data[left] -> temp;
data[mid] -> data[left];
temp -> data[mid];
data ->> quicksort(left, mid-1)
->> quicksort(mid+1, right)
->> data;
}
コンソールから下記のように、pacコマンドでコンパイルして実行ファイルを作成できます。実行ファイルを実行すると、ソート前とソート後の数字の並びが表示されます。
$ ./pac quicksort.pa -o a.out
linking: a.out
$ ./a.out
before: 0 4 8 3 7 2 6 1 5 0
after: 0 0 1 2 3 4 5 6 7 8
オプションを指定しない場合は、コンパイル後、すぐに実行されます。
$ ./pac quicksort.pa
before: 0 4 8 3 7 2 6 1 5 0
after: 0 0 1 2 3 4 5 6 7 8
プログラムの解説
Palanのファイルの拡張子は".pa"となります。
mainのようなスタート関数はなく、基本的に上から順に実行されます。
ccall int32 printf();
ver0.1のPalanではデフォルトで標準Cライブラリとリンクします。ccallで宣言することでライブラリの任意の関数を使えるようになります。ver0.1では引数の宣言は不要です。
const N = 10;
ソートする配列の要素数の定数宣言です。定数はスコープ内であればどこでも使えます。値としてはリテラルおよびリテラル同士の計算が指定できます。
// start
int16[N] data;
int32 i=0;
ソート対象となる配列と、ループのカウントに使う変数の宣言です。int16は16bit整数、*[]は配列、int32は32bit整数であることを示しています。//*はコメントです、行末まで無視されます。
// init data.
while i<N {
i*13 % 9 -> data[i];
i+1 -> i;
}
配列をランダムな数字で初期化します。whileは繰り返し文で、i<Nの条件をみたす限り *{}内の文を繰り返します。->は代入を表し、左の値を右の変数に代入します。他の言語では代入に=を使うことが多いのですが、Palanでは=は初期化時のみで、代入は->*を使うところが一つの特徴になります。
// sort
printf("before:");
show(data);
C標準ライブラリのprintfをコールして、コンソールに文字を表示します。*show()*は配列の中の数字をコンソールに表示するPalanの関数コールです。show関数の定義は後にあります。スコープ内であれば、関数の定義と呼び出しを記述する順番は前後しても構いません。
printf("after:");
data ->> quicksort(0, N-1)
->> data
-> show();
data配列をquicksort関数に入れてソートを実行し、結果を取得、表示します。代入と同じ*->で関数の引数に、値を指定して関数コールできます。また、代入と関数コールを->で繋げることができ、Palanではチェーンコールと呼んでいます。->>*は、配列の所有権を渡します。所有権を渡した変数は所有権を得るまで使えません。
func show(int16[N] data)
{
func quicksort(int16[N] >>data, int32 left, right)
-> int16[N] data
{
配列の中を表示する関数と、ソートを行う関数を定義しています。
int16[N] dataと定義された引数は、コピーされた配列が引数に渡りますので、関数の中で配列を変更しても呼び出し元の配列には影響がありません。
int16[N]>> data と定義された引数は、所有権を必要としますが、リファレンスが渡るためコピーは発生しません。-> int16[N] dataが戻り値の定義となり、関数から戻る際、変数dataのリファレンスを返します。
ここまででクイックソートのプログラムの解説を終わります。Palanの雰囲気が感じられたでしょうか?
動作環境
Palanコンパイラpacは下記の環境で動作します。
- CPU: X86-64 Intel系 CPU
- Memory: 2GB
- OS: Ubuntu 16.04 LTS (64bit)
- Library: g++ (gcc 5.4.0/ as,ld 2.26.1) or later, libboost-program-options
インストール
ver0.1ではインストール用のバイナリファイルは準備していません。githubよりソースをクローンしてビルドする必要があります。ビルド方法はREADME.mdをお読みください。
https://github.com/tosyama/palan
コマンドラインリファレンス
Palanコンパイラ(pac)はコンソールからコマンドラインで使用します。
- ヘルプの表示
$ pac -h
コマンドラインのオプションが確認できます。
- バージョン情報の表示
$ pac -v
- コンパイルしてアセンブリを表示
$ pac -S foo.pa
標準出力にアセンブリが出力されます。
- コンパイルしてオブジェクトファイルを作成
$ pac -c foo.pa
foo.oがカレントディレクトリに作成されます。オブジェクトファイル名はソースコードの拡張子を除いたファイル名+".o"となります。
※ オブジェクトファイルはldでリンクすることで実行ファイルにできますが、複数のオブジェクトファイルの結合は今のPalanではサポートできてません。
- コンパイルして実行ファイルを作成
$ pac -o foo foo.pa
上記の例では、foo.oというオブジェクトファイルと、fooという実行ファイルが生成されます。
- コンパイルして、すぐに実行。
$ pac foo.pa
a.outという名称の実行ファイルを生成して実行します。実行後、オブジェクトファイルと実行ファイルは削除されます。
コメント
"//"の記述以降はコメントとなります。記述は行末まで無視され、プログラムとはみなされません。
// コメントです。
基本型
ver0.1では整数型のみ使用できます。
型名 | 説明 | 最小値 | 最大値 |
---|---|---|---|
sbyte | 符号付き8bit整数 | -128 | 127 |
byte | 符号なし8bit整数 | 0 | 255 |
int16 | 符号付き16bit整数 | -32,768 | -32,767 |
uint16 | 符号なし16bit整数 | 0 | 65535 |
int32 | 符号付き32bit整数 | -2147483648 | 2147483647 |
uint32 | 符号なし32bit整数 | 0 | 4294967295 |
int64 | 符号付き64bit整数 | -9223372036854775808 | 9223372036854775807 |
uint64 | 符号なし64bit整数 | 0 | 18446744073709551615 |
変数
変数宣言
変数を使用するには事前に宣言する必要があります。
型名の後に変数名を書くことで変数を宣言します。変数はスコープ内("{"と"}"で囲まれた領域)で使用できます。
int32 i; // 32bit整数の変数iの宣言
int16 x, y; // 16bit整数の変数 xとyの宣言
byte b, sbyte sb; // 符号なし8bit整数bと符号あり8bit整数sbの宣言
=を使って宣言時に変数を初期化できます。複数の変数宣言の場合は、=の右側に","で区切って初期値となる式を記述します。
int32 i = 0;
int16 x, y = 3, 4;
byte b, sbyte sb = 1u, -1;
x + y -> i; // iに3+4の結果が入る
配列変数
固定長配列を使用できます。配列変数を使用するには事前に宣言する必要があります。
[]内に要素数を記述します。","で区切ることで多次元配列の宣言もできます。指定する要素数は固定値である必要があり、整数リテラル、定数およびそれ同士の計算に限られます。
使用するときは*[]*に0から要素数-1までのインデックスを指定します。インデックスには任意の式を指定できますが、範囲を超えないようにしてください。範囲を超えて指定した場合はクラッシュの原因になります。
int32[10] a1, a2; //要素数10の32bit整数の配列変数1 a1, a2の宣言
int32[3,4] m; //3x4の2次元配列の宣言。メモリの並びは要素4の配列が3つ。
// 要素へのアクセス
1 -> a1[0]; 10 -> a2[9];
12 -> m[2,3];
配列の配列
*[]*を繋げることで配列の配列も使用できます。多次元配列と異なり、要素となる配列とその参照の配列が生成されます。
int32[10][3,4] arrayOfMat; // int32[3,4]を要素とする要素数10の配列
123 -> arrayOfMat[9][2,3];
int32[3,4] m = arrayOfMat[9]; // 2次元配列として扱われる
式
値を返す構文を式と呼びます。式は”;”で区切られるまで繋げて一つの文(ステートメント)を形成できます。
リテラル
ソースコード中に固定値を直接記述できます。
名称 | 説明 | 例 |
---|---|---|
整数リテラル | 整数の固定値です。符号付64bit整数として扱われます。10進数で記述します。 | -12345 12345 |
符号なし整数リテラル | 正の整数の固定値です。符号なし整数として扱われます。10進数の後ろにuを付けます。 | 12345u |
文字列リテラル | 文字列の固定値です。ver0.1ではC言語関数の引数にしか使えません。”で囲みます。エスケープシーケンスが使えます。 | ”Hello World!\n” |
ver0.1では配列リテラルはありません。
変数
変数宣言した変数名を記述するとその変数の値が返ります。代入、算術演算、関数引数などで使用できます。
代入
代入演算子*->*を使うと計算結果などを変数に格納できます。左辺から右辺に値がコピーされます。
複数の変数に代入することもできます。
121+2 -> i; // 123をiに代入
i + 3 -> i; // 126をiに代入
123, 456 -> i, j; // 複数の代入。123をiに、456をjに代入
f() -> i, j; // 関数fが複数の値を返し、それぞれ代入できる
注意: ver0.1では左辺値評価後すぐに代入が行われるため、値の入れ替えはできません。また、将来的にこの動作は変わる可能性があります。
int32 i, j = 1, 2;
i,j -> j,i; // 入れ替わらない。iもjも1になる
配列変数や、配列の配列に代入演算子を使用すると全ての要素がコピーされます。(Deep Copy)
int32[10] a, b;
...
a -> b; // 深いコピー
所有権の移動(Move)
配列などのオブジェクトに関して所有権が移動できます。所有権の移動にはMove演算子*->>を使います。2番目以降の変数にも移動したい場合は移動先の変数に>>*を付加します。配列の中身をコピーせずに参照のみを移動先の変数に渡すため、代入に比べて高速に値を渡せます。
所有権移動元の変数は、移動後は所有権を獲得するまで使用できません。もし使用した場合、クラッシュの原因となります。
int32[10] a, b, c, d;
...
a ->> b; // 配列の中身の所有権をaからbへ移動
// 2->a[2]; // 使用不可
b, c ->> a, >>d; // aは所有権を取り戻すと同時にcからdに所有権を移動
int32[10] e <<= a; // 所有権を移動して初期化
Moveは主に関数との大きなオブジェクトの受け渡しで使用します。配列変数同士の直接のMoveでは、受け渡し先が保持していたメモリは解放されます。
算術演算
整数に対して下記の算術演算ができます。
演算子 | 名称 | 説明 | 例 |
---|---|---|---|
+ | 加算 | 足し算を行います。 | 1+2 -> i; // 3 |
- | 減算 | 引き算を行います。 | 4-3 -> i; // 1 |
* | 乗算 | 掛け算を行います。 | 3*2 -> i; // 6 |
/ | 除算 | 割り算の商を求めます。少数点は切り捨てられます。 | 10/3 -> i; // 3 |
% | 余算 | 割り算の余りを求めます。 | 10%3 -> i; // 1 |
- | 負 | 負数を返します。 | -i -> i; |
演算子には優先順位があります。*, / , % は +, -よりも優先順位が高くなります。
基本的にリテラル同士の計算はコンパイル時に行われます。
比較演算
比較演算子を使って整数の比較ができます。比較の結果が真の場合、1となります。偽の場合、0となります。式が使用できるところであればどこでも使えますが、if文やwhile文の条件として使用する機会が多いでしょう。
演算子 | 名称 | 説明 | 例 |
---|---|---|---|
== | 等しい | 左値と右値が等しい場合1、それ以外の場合0になります。 | if a==10 {...} |
!= | 等しくない | 左値と右値が等しくない場合1、等しい場合0になります。 | if a!=10 {...} |
> | 大なり | 左値が右値より大きい場合1、等しいか小さい場合0になります。 | if a>10 {...} |
< | 小なり | 左値が右値より小さい場合1、等しいか大きい場合0になります。 | if a<10 {...} |
>= | 大なりイコール | 左値が右値より大きいか等しい場合1、小さい場合0になります。 | if a>=10 {...} |
<= | 小なりイコール | 左値が右値より小さいか等しい場合1、大きい場合0になります。 | if a<=10 {...} |
整数の昇格
整数型は64bit整数に昇格されてして計算や比較が行われます。代入時に結果の上位ビットが切り捨てられます。
符号付き整数と符号なし整数の計算の場合は、符号なし整数は符号付き整数として扱われて計算されます。
注意: C言語と仕様が異なります。
条件演算
複雑な条件を判断するために下記の条件演算が使用できます。
演算子 | 名称 | 説明 | 例 |
---|---|---|---|
&& | かつ | 左値が0でない、かつ右値が0でない場合1、それ以外は0になります。左値が0であった場合、右値の式は計算されません。 | if a>0 && a <= 10 {...} |
¦¦ | または | 左値が0でない、または右値が0でない場合1、それ以外は0になります。左値が1であった場合、右値の式は計算されません。 | if a<0 ¦¦ a >= 10 {...} |
! | 否定 | 式が0の場合1、0でない場合1になります | if !(a<0) {...} |
注意: 他の言語では論理演算と呼ぶことが多いですが、実質的な動作は条件分岐であるため、Palanでは条件演算と定義しています。
関数の呼び出し
関数名と()の中に引数を指定することで宣言、定義されたスコープ内の関数を呼び出すことができます。スコープ内であれば呼び出し後に関数定義があっても構いません。
引数には所有権を要求するものがあります。その場合には所有権を手放すことを明確にするため">>"を引数の後ろに付加する必要があります。
戻り値は代入演算子、またはMove演算子を使って受け取ることができます。
戻り値を受け取ることは必須ではありません。受け取らなかった戻り値が使用していたメモリは自動解放されます。
int32 x, y;
int32[10] a;
int32 result;
// 関数呼び出しの例
foo(); // 引数なし、戻り値を受け取らない。
foo(x,y) -> result; // 引数あり、戻り値あり。
foo(a>>, x, y)->>a, result; // 所有権移動の引数、複数の戻り値
foo(a) -> a; // 内容のコピーによる受け渡し
func foo() { ... } // 関数定義は後でもよい
func foo(int32 x, y) { ... }
...
オーバーロード
Palanでは関数名が同じで引数が異なる関数を定義できます(オーバーロード)。呼び出し時に引数の型と数が完全に一致するものがあればその関数が呼び出されます。
型が完全に一致しない場合、整数など暗黙に型変換した上で一致する関数が1つである場合、その関数が呼び出されます。2つ以上の関数に一致した場合はコンパイルエラーとなります。
チェーンコール
代入やMove演算子を使って引数を指定して関数呼び出しを行ったり、さらに関数の戻り値を次の関数呼び出しの引数に適用できます。
通常の関数呼び出しと比較し、関数を左から右に順番に記述できるので、データや処理の流れやを表現しやすくなります。
代入演算子の左値、または関数の戻り値は、引数の先頭から順に割り当てられ、関数が呼び出されます。
int32 a,b;
add(a,3)->b; // 通常の関数呼び出し
a->add(3)->b; // 代入(1つ)で引数指定
a,3 -> add() -> b; // 代入(2つ)で引数指定
add(add(a,3),9)->b; // 通常の関数呼び出し(ネスト)
a->add(3)->add(9)->b; // 関数の戻り値を次の関数へ渡す
func add(int32 a, b)->int32 c
{...}
Move演算子は最初の引数にのみ適用されます。2番目以降の引数に適用する方法は提供していませんので、その場合は通常の関数呼び出しを使用する必要があります。
byte[10] s1, s2;
s1 ->> mix(>>s2) ->> s1, >>s2;
// s1, s2 ->> mix(); // NG: 2番目の引数は代入と見なされる
func mix(byte[10] >>a, >>b)
-> byte[10] a, b
{...}
定数
constキーワードを使用するとプログラム上で共通に使用する数値を定数として定義できます。リテラルおよびリテラル同士の計算を指定できます。式の中で使う以外に、配列の型の宣言の要素数等でも使用できます。
const M,N = 3,4;
const FORMAT = "bar: %d";
const LEN = M*N; // リテラル同士の計算
int32 l,z = LEN, Z; // 式。Zは定義前だが使用可能
func foo(int32[M,N] m) // 固定配列の宣言
{
const FORMAT = "foo:%d"; // 定数の上書き
printf(FORMAT, LEN); // 親のブロックで定義された定数LENは使用可能
}
const Z = LEN;
定数はスコープ内であれば定義前でも使用できます。ただし定数の定義中で別の定数使用する場合は、その定数は前に定義しておく必要があります。
子のブロックでは親ブロックの同じ名前の定数を定義すると上書きされます。
制御文
ifやwhile等の制御文を使うことで、処理を分岐させたり繰り返したりして流れを制御できます。
トップレベル
関数定義の外に記述されたコードをトップレベルと呼びます。プログラムはトップレベルの上から順次実行され、トップレベルの最後に来ると終了します。
トップレベルではreturn文は使用できません。任意の場所で終了する場合はC標準ライブラリのexit関数等を使用してください。
ブロック
*{}*で囲まれた領域がブロックです。ブロックを使用する事で、変数、関数、定数やその名前の有効な領域を制限できます。
通常、ブロック内で宣言した変数はそのブロックと子のブロック内で使用でき、ブロックの外側では使用できません。
処理がブロックを抜けると、ブロック内で宣言した変数が使用しているメモリは全て解放されますので、ブロックを適切に使用すると使用メモリを節約できます。
if文
条件によって処理を分岐させたい場合はif文を使用します。
if文 は条件式を評価し、条件式が真(0以外)の場合は、直後のブロックを実行します。
if a==2 { // ifの後に、任意の条件式を記述
// 条件式が真(aが2)の場合に実行
}
条件式が偽(0)の場合に別の処理を行わせたい場合は、if文のあとに else文を記載します。
if a==2 {
// 条件式が真(aが2)の場合に実行
} else {
// 条件式が偽(aが2以外)の場合に実行
}
else if文をつなげることで、複数の条件分岐ができます。
if a==2 {
// 条件式が真(aが2)の場合に実行
} else if a==3 {
// 条件式が真(aが3)の場合に実行
} else {
// 全ての条件式が偽(aが2と3以外)の場合に実行
}
while文
条件によって処理を繰り返す場合はwhile文を使用します。while文は条件式を評価し、条件式が真(0以外)である限り直後のブロックを実行し続けます。
無限ループによるCPUビジーの原因になりやすいため条件式のコーディングは特に気をつける必要があります。
int32 i=0;
while i<10 { // 条件式が真(iが10より小さい)の限り繰り返す
printf(“%d”,i);
i+1 -> i;
}
ver0.1ではループの中断はサポートされていません。(C言語のbreak文)
関数
関数とは一連の処理をまとめたものです。入力を引数として受け取り、処理を行ったあと出力を戻り値として返します。Palanの関数定義では入力、出力を明確に表現できます。
関数定義
Palanの関数を定義するにはfuncキーワード、関数名を記述し、その後に*()内に呼び出し元から受け取る仮引数を宣言します。
関数が戻り値を返す場合は、->*の後に戻り値を宣言します。
上記宣言の直後に関数で行う処理をブロックの中に記述します。
// 引数なし、戻り値なしの関数定義
func foo()
{
// ここに処理を記述
}
// 引数あり、戻り値なしの関数定義
func foo(int32 a, b, int16 c)
{
printf("a:%d, b:%d c:%d\n", a, b, c); // 仮引数は通常の変数として扱える
}
// 引数なし、戻り値ありの関数定義
func bar() -> int32 a, b, int16 c
{
// 戻り値も変数として扱える。
// 関数終了時の値が呼び出し元に返る。
1 -> a; 2-> b; 3->c;
}
// 引数あり、戻り値ありの関数定義
// 引数と、戻り値で同じ変数名aを指定可能。
func bar(int32 a, b) -> int32 a, c
{
1 -> c;
}
仮引数
関数呼び出し時に指定された引数の値は、仮引数に渡ります。
仮引数には型名と、変数名を記述します。複数の仮引数を宣言するには”,”で繋げます。前の引数と型が同じ場合は型名を省略できます。
通常、仮引数には呼び出し元の変数の値がコピーされます。仮引数の値を変更しても、呼び出し元の変数の値は変わりません。
call int32 printf();
func foo(int32 i, j, sbyte b, int32[10] a) // ()で囲まれた部分が仮引数の宣言
{
printf("before: i=%d, a[3]=%d¥n", i, a[3]);
8,9 -> i, j;
33 -> b;
88 -> a[3];
printf("after: i=%d, a[3]=%d¥n", i, a[3]);
}
// 引数に指定する変数
int32 ii, jj = 3, 4;
sbyte bb = 1;
int32[10] arr;
99 -> arr[3];
foo(ii, jj, bb, arr); // 関数を呼び出し
printf("original: ii=%d¥n, arr[3]=%d", ii, arr[3]);
出力は下記のようになります。元の変数は変更されていません。
before: i=3, a[3]=99
after: i=8, a[3]=88
original: ii=3, arr[3]=99
仮引数の前に*>>*を指定した場合、コピーではなく、呼び出し元の変数の所有権を要求します。参照が渡されるのでディープコピーは発生せず高速に受け渡しが可能です。このままだと呼び出し元の変数は使用できなくなりますので、通常、呼び出し元に変更後の値を返す戻り値も宣言します。
仮引数の順番は、他の関数でも使うようなメインとなるデータを第一引数にして、オプション等を後ろにするとチェーンコールで使いやすくなります。
戻り値
処理の結果を呼び出し元に返すには戻り値を使用します。
戻り値は*->の後に型名と変数名を宣言します。複数の戻り値を宣言するには”,*”で繋げます。前の引数と型が同じ場合は型名を省略できます。
func foo() -> int32 i, j, int16 l
{
1,2,3 -> i, j, l;
}
戻り値に指定した変数の関数終了時の値が、呼び出し元に返されます。
"%d, %d, %d\n", foo() -> printf(); // => 1, 2, 3
仮引数と同じ変数を戻り値として宣言することができます。その場合は呼び出し元の引数の値が変数代入されて呼び出され、そのまま戻り値にも使用されます。
func foo(int32 j) -> int32 i, j, int16 l // jは引数にも戻り値にも使用
{
j+1 -> j;
1,2 -> i, l;
}
"%d, %d, %d\n", foo(10) -> printf(); // => 1, 11, 2
型だけ指定し、変数名を指定しない書き方もできます。その場合はすべての戻り値を型のみの宣言にする必要があります。また、関数を終了する際は、return文で戻り値をすべて記載する必要があります。
func foo() -> int32, int32, int16
{
return 1,2,3; // 必須
}
"%d, %d, %d\n", foo() -> printf(); // => 1, 2, 3
Return文
関数の任意の場所で処理を終了する場合はreturn文を使用します。
return文には呼び出し元に返す値を式で指定できますが、戻り値の宣言で変数名を指定している場合はその変数の値となるため返す値は不要です。
戻り値の宣言が型名だけの場合は必ず戻り値が必要です。
トップレベルの処理においてはreturn文は使用できません。処理を終了する場合は標準Cライブラリのexit関数等を使用してください。
C言語関数
ver0.1のPalanのプログラムは標準Cライブラリとリンクされます。ccall宣言を記述するとprintfなどの標準Cライブラリの関数が使用できるようになります。C言語の戻り値の型、int / long long等は Palanの型 int32, int64等に置き換える必要があります。voidは記載不要となります。
仮引数の宣言はありません。呼び出し時の引数がそのまま関数に渡されます。
残念ながら、今はドラフトの実装であるため、C言語の構造体、マクロ、グローバル変数等は使用できません。
ccall int32 printf(); // 戻り値がある場合
ccall exit(); // 戻り値がない場合
システムコール
syscall宣言をするとLinux(64bit)システムコールが使用できます。システムコールは任意の名前を指定でき、関数と同じように使用できます。宣言時にシステムコール番号と戻り値の型、対応する関数名を宣言します。
仮引数の宣言はありません。呼び出し時の引数がそのまま関数に渡されます。
syscall 1: int64 write();
syscall 60: exit();
注意: プログラム終了のシステムコールを宣言して使用できますが、printfなどを使用している場合はバッファに出力が残ったまま終了してしまいます。printfを使用する際は、標準Cライブラリのexit()関数を使用した方がよいでしょう。
メモリ管理
Palanにはメモリ確保のnewはありません。配列は宣言時に自動的にヒープ領域に確保されます。
GCやdeleteもありません。ヒープ領域に確保された領域はその所有権を持つ変数がスコープをはずれたり、別の所有権が移動された際に解放されます。
メモリ解放はGCとは違い即座に行われます。GCと比較して解放処理の分その処理速度が落ちることも考えられますが、常に安定したパフォーマンスと最小限のメモリを維持でき、突然のGCで急に処理が止まるようなことはありません。
トップレベルで確保された配列はプログラム終了時にOSより一度に解放されますので、個別の解放処理は行われません。
終わりに
まだまだ使える言語には程遠いですが、地道に開発を続けていきたいです。もし、興味をもっていただけて、一度でも使っていただければ幸いです。