プログラミング言語を作っているよというお話.
自作言語をかれこれ8ヶ月ぐらい作ってるんですが,ちょっとドキュメント(笑)的なものは書いとくかと思ったので投稿します.
随時更新していきます.
リポジトリはここ!
特徴
インタプリタ
内部でソースコードをバイトコードにコンパイルして,それをVM(仮想機械)上で実行します.replもついてます.
repl
>> 10 10 : int >> [10] [10] : [int] >> 10 + 40 50 : int >> let a = 10 >> a * 20000000 200000000 : int >> "println" + "orintln" printlnorintln : string
普通のreplですが,pythonやrubyのreplとは違い型を書いてくれるので便利です.
静的型付け
コンパイル時に型を解析します.
フルスクラッチ
(言語の特徴では無いですが,)解析,実行までの全フェーズ自分の手で書いています.
サンプルコード
なんか色々説明するよりソースコードを見てもらった方がよさそう.
Rustのsyntax colorを適用してるのでちょっと変な感じです.
ハロワ
println("Hello, World!");
標準出力は組み込み関数のprint,printlnで行います(printlnは改行がつきます).
式や文のデリミタは;(セミコロン)です.
変数定義
let a: int = 100; let b: float = 100.0; let c = "StrStrStr"; println(a, " ", b, " ", c);
実行結果
100 100.000000 StrStrStr
型の推論が可能な場合,型注釈を省略することができます. また,宣言をまとめることができます.
let { a = 100; b = 100.0; c = "StrStrStr"; }
if・while
なんかfor文は面倒くさくてまだ実装してません.明日から本気出します.
def fizzbuzz(n: int) { let i = 1; while i <= n { if i % 15 == 0 { println("fizzbuzz"); } else if i % 5 == 0 { println("buzz"); } else if i % 3 == 0 { println("fizz"); } else { println(i); } ++i; } } 15.fizzbuzz();
実行結果
1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz
関数定義・関数呼び出し
関数定義はdef
キーワードで行います.前まではfn
でした.
def dump_int(n: int) { println(n); } def add(a: int, b: int): int { return a + b; } def mul(a: int, b: int) = a * b; dump_int(add(mul(10, 10), 400));
実行結果
500
戻り値の型がnone
(なし)のときは,省略することができます.
また,関数の中身が式1つだけなら,中括弧を使わず=
で一行で書くことが出来ます.この場合,戻り値の型が推論可能な場合は省略することができます.
UFCS
UFCS(Unified Function Call Syntax)は,一般的な関数呼び出しfunction(arr1, arr2)
をarr1.function(arg2)
のように記述できる糖衣構文です.
これによって,上の関数呼び出しは以下のように記述することができるようになります.
10.mul(10).add(400).dump_int();
上のと比較すると,単純に左から読んで行けばいいので分かりやすくなっています.
また,引数が一個でUFCSを使用する場合は()
を省略できます.
10.mul(10).add(400).dump_int;
関数のオーバーロード
def test() { println("No Arg"); } def test(a: int) { println("arg 1: ", a); } def test(a: int, b: int) { println("arg 2: ", a, " ", b); } def test(a: float) { println("arg 1(float): ", a); } test(); test(19); test(22, 42); test(32.3);
実行結果
No Arg arg 1: 19 arg 2: 22 42 arg 1(float): 32.300000
if式
def fibo(n: int): int = if n <= 1 n else fibo(n - 1) + fibo(n - 2); 34.fibo.println;
実行結果
5702887
if
式 + 関数の中の式が一個の場合中括弧を省略できる機能でフィボナッチ級数を求めるプログラムを短く記述できます.
でもこのif式,若干見にくいので改良予定.
リスト
let a = [10, 41, 43, 582, 1]; println(a);
実行結果
[10,41,43,582,1]
rangeベースのfor文の実装とかはまだ.あと追加とかもまだ無理です.明日頑張る
できました.
let a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; let res = 0; for i in a { res = res + i; } if res == 550 { println("success"); }
2重for文はまだバグります.なぜだ.
import(2019-9-19追加)
別ファイルに記述してあるプログラムを読み込むことができます.
import.mxc
import aaa; println(aaa@aaaaa());
aaa.mxc
def aaaaa(): string { return "aaaaa"; }
実行結果
aaaaa
assert
assert 10 == 10; assert 10 != 10;
[runtime error] assertion failed in tmp/assert.mxc::<global>
オブジェクト(構造体)
object Human { name: string, age: int } def speak(h: Human) { println(h.name, " uonuonuonuon ", h.age); } let marimo = new Human {}; marimo.name = "marimo"; marimo.age = 17; marimo.speak;
実行結果
marimo uonuonuonuon 17
構造体の初期化はnew TagName {}
で行います.将来的には
let marimo = new Human {name: "marimo", age: 17};
みたいなことができるようになると思います.
メソッドの定義
(メソッドといってもただの関数定義ですが)オブジェクトを第一引数に持ってくる + UFCSで関数がオブジェクトに束縛されているように記述できます.
def speak(h: Human) { println(h.name, " uonuonuonuon ", h.age); } marimo.speak;
組み込み関数一覧
print
println
echo
println
と同じです.len
文字列長を返します.objectid
オブジェクトの識別子を返します.tofloat
int型の値をfloatにキャストして返します(ノリで実装したけど普通のキャストで良い気がする).
ないもの
オブジェクトの継承 無い
メンバ変数の隠蔽 無い
リスト 昔あったけど無い(明日からがんばる) さっき実装した(2019/8/28)
タプル 昔あったけど無い(明日からがんばる)
多分岐(switch-caseみたいなやつ) 無い(明日からがんばる)
その他もろもろ色々できません(明日からがんばります)
TODO
いっぱい
随時更新していきます.