大学のプロジェクト用にコンパイラを書いています。抽象構文ツリーを制御フロー グラフ (CFG) に変換したいと考えています。
私は、CFGのノード ( V
) は AST のノードであるべきだと考えています。私はエッジセット ( G=(V,E)
) を構築する方法をアルゴリズム的に知っていますが、プロセスをもう少し形式的に書くのに苦労しています
この scala スタイルのパターン マッチング (疑似) を作成しました。
def edges(n:Node)(nestedin_next: Node) : List[(Node,Node)] =
n match {
case (c_1 :: c_2::tl) => (c1,c2) :: edges(c2::tl)(nestedin_next)++
edges(c_1)(c_2)//recurse
case c_1 :: Nil => (c_1,nestedin_next)::Nil
case i@ IF(_,c1,c2) => (i,c1)::(i,c2)::edges(c1)(nestedin_next)++
edges(c2)(nestedin_next)
case _ => Nil
}
次のような AST 構造に一致する必要があります。
( IF(1,
ASSIGN(x,1), // ia1
ASSIGN(x,2) // ia2
) :: // i1
ASSIGN(y,2) :: // a1
ASSIGN(z,ADD(x,y)) :: //a2
IF(z,
RET(z), //i2r1
assign(z,0):: // i2a1
ret(z) // i2r2
) :://i2
Nil
)
次のようなエッジセットを提供します。
{ i1 -> ia1,
i1 -> ia2,
ia1 -> a1,
ia2 -> a1,
a1 -> a2,
a2 -> i2,
i2 -> i2r1
i2-> i2a1
i2a1 -> i2r2
i2r2 -> _|_
i2r1 -> _|_
}
これをスカラ「疑似コード」よりも少し形式的に行う方法についてのヒントはありますか?
私は次のような帰納的なことを考えています:
e[[ IF(_,b1,b2) ]] = (if -> b1) + (if -> b2) \cup e[[ b1 ]] \cup e[[ b2 ]]
e[[ b1, b2 ]] = e[[b1]] \cup e[[b2]]
(ただし、上記はツリーのみを提供し、グラフは提供しません。たとえば、then-branch のエッジから次のステートメントへのエッジはありません)
編集:
私はkiama とscala のデータフローについて調べてきましたが、彼らが使用する「成功」と「フォロー」のアプローチが気に入っています。それにもかかわらず、私はそれをより正式な説明に煮詰めるのに苦労しています。これは主に、正式に指定しようとすると見苦しくなる詳細の一部を隠す気の利いchildAttr
たのせいです。s.next
EDIT2:
私は Dragon Book と「Modern Compiler Implementation in ML」、およびLearning to write a compilerの他の資料のいくつかを読み、一部/ほとんどがデータ フローと制御フローについて言及していますが、作成方法についてはあまり触れていません。正式な方法でCFG。
EDIT3:
Kiamaの著者、准教授である Tony Sloane 博士から、検索するための追加の参考文献をいくつか受け取りました。
私が見ることができる限り、それらの本によると「それを行う方法」は、ASTよりもプログラムの「ステートメントごと」に基づいており、基本ブロックに基づいています。それにもかかわらず、素晴らしいインプット!