ようやく、循環属性文法を使用して文法の nullability 計算を行う方法の例を書く時間を見つけました。以下のコードは、Scala 用の Kiama 言語処理ライブラリを使用しています。サンプルとテストの完全なソース コードはKiama にあります。nullableなどの主要な属性コードについては SemanticAnalysis.scala を参照してください。
つまり、このアプローチは次のことを行います。
私が使用する属性定義は、LDTA 2003 の Magnusson と Hedin による論文Circular Reference Attribute Grammarsで例として使用されているものと非常によく似ています。彼らはJastAdd システムで循環属性を実装しており、これを理解したい人にはこの論文を強くお勧めします。トピック。基本的に Kiama と同じアルゴリズムを使用します。
この例で使用する AST の定義を次に示します。Tree
いくつかの一般的な動作を提供する Kiama タイプです。
sealed abstract class GrammarTree extends Tree
case class Grammar (startRule : Rule, rules : Seq[Rule]) extends GrammarTree
case class Rule (lhs : NonTermDef, rhs : ProdList) extends GrammarTree
sealed abstract class ProdList extends GrammarTree
case class EmptyProdList () extends ProdList
case class NonEmptyProdList (head : Prod, tail : ProdList) extends ProdList
case class Prod (symbols : SymbolList) extends GrammarTree
sealed abstract class SymbolList extends GrammarTree
case class EmptySymbolList () extends SymbolList
case class NonEmptySymbolList (head : Symbol, tail : SymbolList) extends SymbolList
sealed abstract class Symbol extends GrammarTree
case class TermSym (name : String) extends Symbol
case class NonTermSym (nt : NonTermUse) extends Symbol
sealed abstract class NonTerm extends GrammarTree {
def name : String
}
case class NonTermDef (name : String) extends NonTerm
case class NonTermUse (name : String) extends NonTerm
以下のコードは、nullable
属性の定義を示しています。false から始まり、固定小数点の「ループ」に入り、値が安定するまで計算されます。ケースは、AST 内のさまざまなタイプのノードの属性を計算する方法を示しています。
Kiama のcircular
属性コンストラクターには、ストレージ キャッシング、固定小数点検出など、属性のすべての実装が組み込まれています。
val nullable : GrammarTree => Boolean =
circular (false) {
// nullable of the start rule
case Grammar (r, _) =>
r->nullable
// nullable of the right-hand side of the rule
case Rule (_, rhs) =>
rhs->nullable
// nullable of the component productions
case EmptyProdList () =>
false
case NonEmptyProdList (h, t) =>
h->nullable || t->nullable
// nullable of the component symbol lists
case Prod (ss) =>
ss->nullable
case EmptySymbolList () =>
true
case NonEmptySymbolList (h, t) =>
h->nullable && t->nullable
// terminals are not nullable
case TermSym (_) =>
false
// Non-terminal definitions are nullable if the rule in which they
// are defined is nullable. Uses are nullable if their associated
// declaration is nullable.
case NonTermSym (n) =>
n->nullable
case n : NonTermDef =>
(n.parent[Rule])->nullable
case n : NonTermUse =>
(n->decl).map (nullable).getOrElse (false)
}
参照属性decl
は、非終端使用をルールの左側の対応する定義に接続する属性です。parent
フィールドは、AST 内のノードからその親への参照です。
1 つのルールまたはシンボルの null 可能性は、他の null 可能性に依存するため、取得されるのは、依存サイクルに参加する属性オカレンスのセットです。結果は、「教科書」の定義によく似た、null 可能性計算の宣言型バージョンです。(この例では、null 可能性に関して定義される FIRST および FOLLOW セット計算の属性も定義します。) 循環属性は、この種の問題に便利なパッケージでメモ化と固定小数点計算を組み合わせます。