我一直在使用Language.C library的抽象语法树(AST)来使用SYB library的泛型转换来修改C程序。这个AST有不同类型的节点(数据类型),每个节点代表一个C结构,即表达式,语句,定义等。我现在需要以某种方式增加语句携带的信息,即注释它们。我认为(也许我错了)我无法修改或重新定义原始数据类型,所以我希望有这样的东西:
annotateAST anns =
everywhere (mkT (annotateAST_ anns))
annotateAST_ astnode anns
| isStmt astnode = AnnStmt astnode (getAnn astnode anns)
| otherwise = astnode
通过这种方式,我会使用带注释的语句而不是原始语句。当然,GHC会抱怨,因为everywhere
应该返回它所获得的相同类型,而这不是它在这里发生的事情。
总而言之,我需要在不修改原始数据类型的情况下通常注释AST,并且很容易返回到原始数据结构。 我一直在为这个问题考虑不同的解决方案,但不相信任何一个,所以我决定在这里分享。
P.S。我被告知SYB库效率不高。考虑到Language.C的AST只导出数据,我是否有更有效的替代方法来进行AST的泛型遍历和修改?
答案 0 :(得分:3)
我不是该库的专家,但似乎是为了允许用户定义的装饰而设计的。
这是因为所有主要数据类型都是NodeInfo
的参数化,标准注释(仅携带位置和名称信息)。例如。图书馆提供
type CTranslUnit = CTranslationUnit NodeInfo
允许您定义
type MyTransUnit = CTranslationUnit MyNodeInfo
data MyNodeInfo = MNI NodeInfo AdditionalStuffHere
所以要按照你的意愿装饰AST。
库提供可影响此类装饰的Functor
个实例,以及Annotated
类型类从任何AST节点检索(可能是用户定义的)注释。
我会尝试采用这种方法。
设计看起来不错。我可以看到的唯一缺点是注释类型对于节点上的所有类型必须是相同的,这基本上迫使人们将其定义为内部可能具有的各种注释的巨大总和。例如:
-- AST library for a simple lambda-calculus
data AST n
= Fun n String (AST n)
| Var n String
| App n (AST n) (AST n)
-- user code
data Annotation
= AnnVar ... | AnnFun ... | AnnApp ...
type AnnotatedAST = AST Annotation
我们不会对仅使用AnnFun
修饰的函数提供静态保证。
人们可能希望有更高级的图书馆设计来利用GADT,例如:
-- AST library for a simple lambda-calculus
data Tag = TagFun | TagVar | TagApp
data AST (n :: Tag -> *)
= Fun (n 'TagFun) String (AST n)
| Var (n 'TagVar) String
| App (n 'TagApp) (AST n) (AST n)
-- user code
data Annotation (n :: Tag) where
AnnFun :: String -> Annotation 'TagFun
AnnVar :: Int -> Annotation 'TagVar
AnnApp :: Bool -> Annotation 'TagApp
type AnnotatedAST = AST Annotation
保证每个节点都有正确的注释。 AST
不再是Functor
,但至少可以定义类似Functor
的类。
仍然 - 我很感激至少该库允许某种形式的用户定义注释。