我终于有时间写一个如何使用循环属性语法进行语法可空性计算的示例。下面的代码使用我们用于 Scala 的 Kiama 语言处理库。您可以在 Kiama中找到示例和测试的完整源代码。有关主要属性代码,请参见 SemanticAnalysis.scala,例如nullable。
简而言之,该方法执行以下操作:
我使用的属性定义与 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
属性的定义。它开始为假,然后输入一个定点“循环”进行计算,直到值稳定。这些案例展示了如何计算 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 中其父节点的引用。
由于一个规则或符号的可空性取决于其他规则或符号的可空性,因此您得到的是一组参与依赖循环的属性出现。结果是与“教科书”定义非常相似的可空性计算的声明性版本。(该示例还定义了 FIRST 和 FOLLOW 集计算的属性,这些计算是根据可空性定义的。)循环属性将记忆和定点计算组合在一个方便的包中,以解决此类问题。