====== PicoPicoScript の文法 ======
PicoPicoScript はスクリプト言語として [[https://github.com/awemorris/NoctLang|Noct]] を使用します。
===== 代入 =====
Noct の変数は動的型付けであり、明示的な宣言は不要です。
代入演算子(`=`)を使って、変数を作成し値を代入します。
以下のように、Noct では整数、浮動小数点数、文字列など、さまざまなデータ型をサポートしています。
また、変数は実行中に異なる型へ再代入することが可能です。
func main() {
var a = 123;
print(a);
var b = 1.0;
print(b);
var c = "string";
print(c);
}
===== ローカル変数 =====
var キーワードを使うと、変数をローカル変数として宣言できます。
var を付けずに代入すると、その変数はグローバル変数として作成されます。
func main() {
var a = 123;
print(a);
}
===== 配列 =====
配列はインデックスでアクセスできる、順序付きの値のコレクションです。
配列は for ループ構文を使った反復処理をサポートしており、各要素を直接順番に処理できます。
func main() {
var array = [0, 1, 2];
for (value in array) {
print(value);
}
}
Noct は動的型付けシステムであるため、配列は異なる型の値を同時に保持できます。
func main() {
var array = [123, "string"];
}
Noct には配列の末尾に要素を追加するための組み込み関数 push() が用意されています。
func main() {
var array = []
push(array, 0);
push(array, 1);
push(array, 2);
}
===== 辞書 =====
辞書はキーと値のペアを格納するデータ構造で、他の言語におけるハッシュマップやオブジェクトに似ています。
中括弧 {} を使って定義し、キーと値のペアはコロンで区切ります。
辞書は反復処理をサポートしており、キーと値の両方に同時にアクセスできます。
func main() {
var dict = {key1: "value1", key2: "value2"};
for (key, value in dict) {
print("key = " + key);
print("value = " + value);
}
}
辞書はシングルステップ方式で構築することもできます。
代入の方法としては、[] を使う配列スタイルと、. を使うオブジェクトスタイルの両方が利用できます。
func main() {
var dict = {};
dict["key1"] = "value1";
dict.key2 = "value2";
}
ビルトイン関数 remove() はキーを削除できます。
func main() {
var dict = {key1: "value1", key2: "value2"};
remove(dict, "key1");
}
===== for ループ =====
for ループ構文は、範囲・配列・辞書といったシーケンスを反復処理するための簡潔な記法を提供します。
範囲構文(.. 演算子を使用)は、開始値から終了値のひとつ前までの値を生成するイテレータを作成します。
func main() {
for (i in 0..10) {
print(i);
}
}
for ループは配列やその他のコレクション型を直接反復処理することもできます。
配列は for-value 構文 を使って順に処理できます。
func main() {
array = [0, 1, 2];
for (value in array) {
print(value);
}
}
辞書は for-key-value 構文 を使って反復処理できます。
func main() {
var dict = {key1: "value1", key2: "value2"};
for (key, value in dict) {
print(key + "=" + value);
}
}
===== while ループ =====
while ループは、指定した条件が真(true)である限り処理を繰り返す、古典的な反復構文です。
コレクションの反復処理を目的とした for ループとは異なり、while ループはより柔軟で、事前に反復回数がわからないアルゴリズムを実装するときなどに利用できます。
以下の例では、0 から 9 までカウントアップする基本的なカウンターの実装を示しています。
func main() {
var i = 0;
while (i < 10) {
print(i);
i = i + 1;
}
}
===== if / else ブロック =====
条件式を評価してブロックを条件付きで実行することができます。
if-else 構文はおなじみの書式で、条件が順に評価されていきます。
func main() {
var a = readint();
if (a == 0) {
print("0");
} else if (a == 1) {
print("1");
} else {
print("other");
}
}
===== ラムダ関数 =====
Noct では、関数は第一級オブジェクトとして扱われます。
匿名関数(lambda 式とも呼ばれる)を使うことで、名前を付けずに関数を作成できます。
func main() {
var f = (a, b) => { return a + b; }
print(f(1, 2));
}
ラムダ関数は、コンパイル時に単純に名前付き関数へ変換されます。
そのため、外側の関数で宣言された変数をキャプチャすることはできません。
===== インクリメント / デクリメント (+=, -=, ++, --) =====
func main() {
var a = 123;
a += 321;
a++;
var b = 123;
b -= 321;
b--;
}
++ と -- は、単独の文(a++;, b--;)としてのみサポートされています。
式の中で使用することは禁止されており、これは複雑な副作用を避けるための仕様です。
===== オブジェクト指向 =====
Noct のオブジェクト指向モデルは、プロトタイプベース OOP の軽量なバリエーションです。
* クラスは単なる辞書のテンプレートです
* 継承やインスタンス化は、辞書の複製とマージによって実現されます
* プロトタイプチェーンは存在せず、クラスを変更しても既存のインスタンスには影響しません
辞書は第一級オブジェクトとして扱われ、Dictionary-based OOP(辞書ベースOOP)と呼ばれます。
func main() {
// 基底クラスの定義 (クラスの実体はただの辞書です)
Animal = class {
name: "Animal",
cry: (this) => {
}
};
// サブクラスの定義 (辞書のマージにすぎません)
Cat = extend Animal {
name: "Cat",
voice: "meow",
cry: (this) => {
print(this.name + " cries like " + this.voice);
}
};
// インスタンス化 (これも辞書のマージです)
var myCat = new Cat {
voice: "neee"
};
// this渡し呼び出しには -> () 構文を使います (myCat.cry(myCat) と等価です)
myCat->cry();
}
===== 組み込み関数 =====
==== substring() ====
func main() {
var s1 = "ABCDEFG";
var s2 = s1.substring(1, -1); // 1番目から、末尾まで
}
==== length() ====
func main() {
var str = "ABCDEFG";
var slen = length(str);
var array = [0, 1, 2];
var alen = array.length();
}
==== push() ====
func main() {
var array = [0, 1, 2];
array.push(3); // 末尾に追加
}