作ろうと思った理由
私は普段PHP/Javascriptを使っているのですが色々「ここがこうだったらいいのに」という部分があります。たとえばPHPはconst(immutable)な変数が気軽に使えたら便利だと思いますし、Javascriptの文字列の結合にも思うところがあって、例えば数値型の足し算にも文字列の結合にも「+」を使いますが、そうすると「'5'+'10'」が意図せず'510'に結合されてバグの原因になるということがあると思います。いやそんな操作しないでしょと思うかもしれませんが、'5'と'10'がそれぞれnum_fiveとnum_tenという変数に入っていて「num_five + num_ten」ならうっかりやってしまいそうですし、やってしまった場合に見ただけでは(変数の型がわからないので)ここが原因とは気づけないじゃないですか。
なので、PHPみたいな「'5'+'10'」は足し算、「'5'.'10'」は文字列の結合という使い分けが簡単にできたらいいのに、とずっと思っていました。
でもそういう私の普段あったらいいなと思っている機能を持っているスクリプト言語は調べてもなかったので、勉強のために自分でスクリプト言語を作ってみようと思いました。中学生がblawnというなかなかいい言語を作ったというニュースをみたので、「中学生にできるなら自分にもできるかも」と思ったのも有ります。
なおいろいろなネットの情報を参考にしつつ勉強のために作ったものなのでオレオレ実装なところが多くあります…。
使用言語
RustというC/C++なみに速いのにメモリの安全性が言語仕様としてデフォルトで保証されているというすごい言語があるらしいです。これを使えばRustの勉強にもなるし一石二鳥なので、Rustを使うことにしました。
作ったもの
GitHubで公開しています。
https://github.com/lechatthecat/oran
以下のように実行できます。(最新の安定版Rustが事前に必要です。)
$ git clone https://github.com/lechatthecat/oran.git
$ cd oran
$ cargo build --release
$ ./target/release/oran -f ./examples/hello.orn
言語の仕様
println("Hello World");でHello Worldできます。以下のファイルにHello World用のコードが書いてあるのでこれを実行すればHello Worldされます。
oran/examples/hello.orn
$ ./target/release/oran -f ./examples/hello.orn
Hello World.
以下のように関数も定義できます。
fn test () {
println("hello");
}
test();
上記コードをoran/examples/hello.ornにコピーアンドペーストして以下のように実行すればコードを試せます。
$ ./target/release/oran -f ./examples/hello.orn
hello
変数の定義もできます。
fn test () {
let test = "hey";
println(test);
}
test();
実行結果
$ ./target/release/oran -f ./examples/hello.orn
hey
Rust風に変数はデフォルトでimmutableです。デフォルトでは変数に代入できません。
fn test () {
let test = "hey";
test = "hello"; // エラー!
println(test);
}
test();
でもmutをつけるとmutableな変数にできます。
fn test () {
let mut test = "hey";
test = "hello"; // これなら動く
println(test);
}
test();
Forループは以下のように書きます。
fn test () {
let test = " test";
for i in 0..5 {
println(i << test);
}
}
test();
実行すると以下のようになります。
$ ./target/release/oran -f ./examples/hello.orn
0 test
1 test
2 test
3 test
4 test
なおデフォルトで0 <= i < 5の範囲で実行するので範囲に5が含まれません。含みたい場合は以下のように書きます。
fn test () {
let test = " test";
for i in 0..=5 {
println(i << test);
}
}
test();
実行結果は以下の通り。
$ ./target/release/oran -f ./examples/hello.orn
0 test
1 test
2 test
3 test
4 test
5 test
なお、文字列の結合には「<<」を使います。「+」は使えません。PHP風に文字列結合に「.」を採用しなかったのは将来的にオブジェクトの関数の呼び出しをObj.some_method()みたいに「.」でやりたいからです。(いまはまだオブジェクトの作成はできないですが…。)
なおループ中に i の値を変更したい場合はmutをつけます。
for mut i in 1..100 {
i = i * 10;
}
if文も使えます。
fn test (test1, test2) {
return test1 + test2;
}
if test(5,5) == 10 {
println("it is ten!");
}
実行結果
$ ./target/release/oran -f ./examples/hello.orn
it is ten!
文字列のエスケープもできます。
fn test (test1, test2) {
test1 + test2
}
let t = 10;
if t == test(5,5) {
println("variable \"t\" is " << t);
}
実行結果
$ ./target/release/oran -f ./examples/hello.orn
variable "t" is 10
Rust風に関数のreturnを省略する記法が使えます。
/* 上がreturnなしの書き方で下がreturnありの書き方 */
fn test (test1, test2) {
test1 + test2
}
fn test2 (test1, test2) {
return test1 * test2;
}
println(test(5,5));
println(test2(5,5));
いろいろな汚いコードが乱雑にexampleファイルに書いてあるのでこれを試しに実行することもできます。
https://github.com/lechatthecat/oran/blob/master/examples/example.orn
$ ./target/release/oran -f ./examples/example.orn
おまけ
n番目のフィボナッチ数列が以下のように求められます。
fn fib (n) {
let mut f0 = 0;
let mut f1 = 1;
let mut f2 = 0;
/* フィボナッチ数の計算 */
for i in 1..n {
f2 = f1 + f0;
f0 = f1;
f1 = f2;
}
println("Answer:"<<f1);
}
fib(50);
-tをつけて実行すると実行速度を測ることができます。
$ ./target/release/oran -f ./examples/hello.orn -t
Answer:12586269025
443.486µs
作ってみた感想
私のようなヘボエンジニアでも筋のいい書き方を強制してくれる感じで、いい加減に書いても実行速度は速いですし、Rustはすごくいい言語だと思いました。でも私自身の力量のせいであまりきれいなコードはかけず…。でもいろいろと勉強になりました。今後Rustのプロジェクトに関われたらいいなあ…。という感じです。あとblawnを作った中学生はすごいと思いました…。
追記:
再帰的な関数呼び出しをサポートしました。
if文、for文の中でreturnが使えないバグを修正しました。