前回は、型と変数領域の確保について考えて実装することが出来ました。
- 型と変数領域の確保について考える
- ヴァリアント値の初期化について考える
- パターンマッチ構文について考える
3つに分けて考える事でうまく考えが進みました。今回は、2番目の値の初期化について考えます。
コード例
まずは、ヴァリアント値の初期化のコードの例を書いてみます。
val vt = TVariant(List(
"A"->TStr(List("a"->Ti(32))),
"B"->TStr(List("a"->Ti(32), "b"->Ti(32)))
))
EVal(t,"data", null)
EAssign(vt, EId(vt, "data"), ETag(vt, "A",List(ELdc(Ti(32), 2))) )
と書く事にしましょう。ETag式で初期化出来る事にするのです。この式が以下のLLVMのコードに変換出来ればよい訳です。
%.struct1 = type {i32, i32}
%.struct2 = type {i32}
%.struct3 = type {i32, %.struct2}
; データの領域を確保
%data = alloca %.struct3
; data.tagのアドレスを取り出し
%data.tag = getelementptr inbounds %struct.Data* %data, i32 0, i32 0
; 値0を設定
store i32 0, i32* %data.tag, align 4
; dataのaに1を設定
%data.b = getelementptr inbounds %struct3* %data, i32 0, i32 1
; bからaにキャスト
%data.a = bitcast %struct1* %data.b to %struct2*
; 値を設定
%data.a.a = getelementptr inbounds %struct2* %data.a, i32 0, i32 0
store i32 1, i32* %data.a.a, align 4
実装
コード例が出来たので実装してみます。式は以下のよう定義します。
case class ETag(t:T, id:String, ls:List[E]) extends E
先ほどの例をテストコードに加えて実行してみましょう。エラーが起きる箇所が修正箇所です。
まずは、alphaでエラーが起きました。alphaのEBlockの処理をコピペしてすませます。はい、妖怪コピペおじさんです。ということで、後でリファクタリングすることになりますよ。
case e @ ETag(t: T, id:String, ls: List[E]) =>
val (ls1, env1) = ls.foldLeft(List[E](), env) {
case ((ls, env), a) =>
val (a1, env1) = f(a, env)
((a1 :: ls), env1)
}
(e.copy(t, id, ls1.reverse), env1)
実装中止と方針変更
駄目だ、進めない!この先は、構造体の初期化の構文が使えるようになってから、タグの初期化に進んだ方が良いと気がつきました。焦る気持は置いておいて、構造体の初期化が出来るようになりましょう。今回の目標は変更して、構造体の初期化にします。
テストコードから、ETagのコードは一度消しましょう。
構造体の値設定構文を考える
構造体の値を纏めてする処理は、前に、作った事があります。前作ったコードを見て、何をしていたかを調べてみましょう。
def o2(e: E):String = e match {
case ELd(_,v) => ""+v
case ELdd(_,v) => ""+v
case ETuple(_,ls) =>
"{"+ls.foldLeft("") {
case ("", l) => llt(l.t) + " " + o2(l)
case (v, l) => v + ", " + llt(l.t) + " " + o2(l)
}+"}"
}
def out(t: T, e2: E) {
(t, e2) match {
case (t, ELd(_, v)) => asm("@"+id+" = global "+llt(t)+" " + v)
case (t, ELdd(_, v)) => asm("@"+id+" = global "+llt(t)+" " + v)
case (t: TStruct, v@ETuple(_, _)) => asm("@"+id+" = global "+llt(t)+" " + o2(v) )
case (t, null) => asm("@"+id+" = common global "+llt(t)+" zeroinitializer")
//case (_, EId(_, v)) => asm("@"+id+" = @"+v)
}
}
out(t, e2)
というのがグローバル変数にはありました。グローバル変数のみの初期化だったので、ローカル変数用の処理はありませんでした。
ローカル変数の初期化はやった事がないようなので、何処でやったら良いのかを考えます。グローバル変数の初期化処理は、emit内にありました。そして、タプルの式で値を設定していました。
ローカルの構造体の初期化も、同様にタプルの式で初期化する事にしましょう。Mincamlはどうだったかと思ってみましたが、LetTupleとTupleがあってVariantはありませんでした。
やはり、Variantの前に構造体の初期化が必要なようです。
作り直すことにする
実は作っている自分ですらソースの内容が良くわかりません。この拡張に次ぐ拡張で分からなくなっているのと、1回目の実装なので、経験不足なので不安なのです。このまま作り続けるのは嫌になってきました。
どちらにせよ、ちゃんとパターンマッチの作り方を書くためには、最初から作る必要があります。
急がば回れです。1から作り直してみたほうがよいでしょう。作り直します。気持は焦るのですが、技術がなければ出来ないのです。
まとめ
今回は、ヴァリアント型のデータの初期化について考えました。考えた結果、構造体の初期化の技術がないと出来ないと気がつきました。
そして、リファクタリングの必要性に迫られたので、作り直す事にしました。