Как я могу общаться с комментариями в своем АСТ?

Я пишу парсер кода Delphi с использованием Parsec, мои текущие структуры данных 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)

Я хочу сохранить комментарии в своих структурах данных АСТ, и сейчас я пытаюсь выяснить, как это сделать.

Мой парсер разбивается на лексер и синтаксический анализатор (оба написаны с 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?

Ответ 1

Разумный подход для обработки аннотированных данных по AST - это поток дополнительного параметра типа, который может содержать любые метаданные, которые вам нравятся. Помимо возможности выборочного включения или игнорирования комментариев, это также позволит вам включать другие виды информации с вашим деревом.

Во-первых, вы должны переписать все ваши типы AST с дополнительным параметром:

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

Было бы полезно добавить deriving Functor ко всем из них, что упростит преобразование аннотаций по заданному AST.

Теперь AST с оставшимися комментариями будет иметь тип Class Comment или что-то в этом роде. Вы также можете повторно использовать это для дополнительной информации, такой как анализ области, где вы бы включили текущую область действия в соответствующую часть АСТ.

Если вам нужно сразу несколько аннотаций, самым простым решением будет использование записи, хотя это немного неудобно, потому что (по крайней мере, на данный момент¹) мы не можем легко написать полиморфный код над полями записи. (Т.е. мы не можем легко записать тип "любая запись с полем comments :: Comment".)

Еще одна полезная вещь, которую вы можете сделать, это использовать PatternSynonyms (доступный от GHC 7.8), чтобы иметь набор шаблонов, которые работают так же, как ваш текущий unannotated AST, позволяющий повторно использовать ваши существующие заявления о делах. (Для этого вам также нужно будет переименовать конструкторы для аннотированных типов, чтобы они не перекрывались.)

pattern TypeName a as <- TypeName' _ a as

Сноска

¹ Надеюсь, что часть 2 возрожденное перегруженное предложение полей полей поможет в этом отношении, когда оно действительно добавится к языку.