我如何处理我的 AST 中的注释?

How can I deal with comments in my AST?

我正在使用 Parsec 编写 Delphi 代码解析器,我当前的 AST 数据结构如下所示:

module Text.DelphiParser.Ast where

data TypeName = TypeName String [String] deriving (Show)
type UnitName = String
data ArgumentKind = Const | Var | Out | Normal deriving (Show)
data Argument = Argument ArgumentKind String TypeName deriving (Show)
data MethodFlag = Overload | Override | Reintroduce | Static | StdCall deriving (Show)
data ClassMember = 
      ConstField String TypeName
    | VarField String TypeName
    | Property String TypeName String (Maybe String)
    | ConstructorMethod String [Argument] [MethodFlag]
    | DestructorMethod String [Argument] [MethodFlag]
    | ProcMethod String [Argument] [MethodFlag]
    | FunMethod String [Argument] TypeName [MethodFlag]
    | ClassProcMethod String [Argument] [MethodFlag]
    | ClassFunMethod String [Argument] TypeName [MethodFlag]
     deriving (Show)
data Visibility = Private | Protected | Public | Published deriving (Show)
data ClassSection = ClassSection Visibility [ClassMember] deriving (Show)
data Class = Class String [ClassSection] deriving (Show)
data Type = ClassType Class deriving (Show)
data Interface = Interface [UnitName] [Type] deriving (Show)
data Implementation = Implementation [UnitName]  deriving (Show)
data Unit = Unit String Interface Implementation deriving (Show)

我想在我的 AST 数据结构中保留注释,我目前正在想办法做到这一点。

我的解析器分为一个词法分析器和一个解析器(都是用 Parsec 编写的),我已经实现了注释标记的词法分析。

unit SomeUnit;

interface

uses
  OtherUnit1, OtherUnit2;

type
  // This is my class that does blabla
  TMyClass = class
  var
    FMyAttribute: Integer;
  public
    procedure SomeProcedure;
    { The constructor takes an argument ... }
    constructor Create(const Arg1: Integer);
  end;

implementation

end.

令牌流如下所示:

[..., Type, LineComment " This is my class that does blabla", Identifier "TMyClass", Equals, Class, ...]

解析器将其翻译成:

Class "TMyClass" ...

Class 数据类型没有任何附加注释的方法,并且由于注释(尤其是块注释)几乎可以出现在令牌流中的任何地方,我必须向所有数据类型添加可选注释在 AST 中?

如何处理 AST 中的注释?

在 AST 上处理带注释的数据的一种合理方法是通过一个额外的类型参数进行线程处理,该参数可以包含您喜欢的任何元数据。除了能够有选择地包含或忽略评论外,这还可以让您在树中包含其他类型的信息。

首先,您将使用额外参数重写所有 AST 类型:

data TypeName a = TypeName a String [String]
{- ... -}
data ClassSection a = ClassSection a Visibility [ClassMember a]
{- ... -}

向所有这些添加 deriving Functor 也会很有用,这样可以轻松转换给定 AST 上的注释。

现在,带有剩余注释的 AST 将具有类型 Class Comment 或类似的效果。您还可以重用它来获取其他信息,例如范围分析,您可以在其中将当前范围包含在 AST 的相关部分中。

如果您想要同时使用多个注释,最简单的解决方案是使用记录,尽管这有点尴尬,因为(至少现在¹)我们不能轻易地在记录字段上编写多态代码。 (即我们不能轻易写出类型"any record with a comments :: Comment field"。)

您可以做的另一件巧妙的事情是使用 PatternSynonyms(可从 GHC 7.8 获得)拥有一套模式,就像您当前未注释的 AST 一样工作,让您重用现有的 case 语句。 (为此,您还必须重命名带注释类型的构造函数,以免它们重叠。)

pattern TypeName a as <- TypeName' _ a as

脚注

¹ 希望第 2 部分 revived overloaded record fields proposal 在实际添加到语言中时能在这方面有所帮助。