はじめに
単なる個人の感想です。
1.ブロック
文の連続を単なるvectorで表しているみたいだけど、個人的にはブロックとしてまとまって管理できるようにしたほうが良い気がしている。
例えば、ブロックの中にブロックがある入れ子構造のとき、変数のスコープをブロック単位で管理する言語は多いと思う。
そんな時には、ブロック単位で管理できるメリットは大きいと思う(個人の感想です)。
2.文と式
ここでは、文とは「値を返さないもの」、式とは「値を返すもの」ぐらいのふわっとした意味で使っています。
言語の文法を定義するとき、文と式はきちんと区別したほうが良いと思う。
特に問題になりやすいのが、if。
ifが文なのか式なのかによって、コンパイラが吐き出すコードは変わってくる。
x = if m < n (1) else (2)
みたいなコードがあったとする(言語は適当)。
このとき、ifが文(値を返さない)だったとすると、このコードは文法エラーになるべき(ifをコンパイルしたコードが値を返さないはずなので、コンパイルした結果がおかしくなる)。
なので、言語の文法を考える時には、式なのか文なのかはきちんと区別したほうが良い(個人の感想です)。
とはいえ
ifが式だと面白いので(個人の感想ry)、Blawnのifを式(値を返す)にしてみた↓。
https://github.com/JunSuzukiJapan/Blawn
function lt(m, n)
return if m < n
(
1
) else
(
0
)
x = lt(5, 7)
print(int_to_str(x))
上記のようなコードがコンパイルできます。
具体的にどんなコードを吐き出すかというとphiを使っていて下記のようなコードになります。
// (略)
define i8 @main() {
entry:
%0 = call i64 @lt.2(i64 5, i64 7)
%x = alloca i64
store i64 %0, i64* %x
%1 = load i64, i64* %x
%2 = load i64, i64* %x
%3 = call %struct.String* @int_to_str(i64 %2)
call void @print(%struct.String* %3)
ret i8 0
}
declare void @lt()
define i64 @lt.2(i64 %m, i64 %n) {
entry:
%0 = alloca i64
store i64 %m, i64* %0
%1 = alloca i64
store i64 %n, i64* %1
%2 = load i64, i64* %0
%3 = load i64, i64* %1
%4 = icmp slt i64 %2, %3
br i1 %4, label %"then of if expr", label %"else of if_expr"
"then of if expr": ; preds = %entry
br label %"merge of if_expr"
"else of if_expr": ; preds = %entry
br label %"merge of if_expr"
"merge of if_expr": ; preds = %"else of if_expr", %"then of if expr"
%condtmp = phi i64 [ 1, %"then of if expr" ], [ 0, %"else of if_expr" ]
ret i64 %condtmp
}
参考:
LLVMのphiとは