Skip to content

Instantly share code, notes, and snippets.

@alanz
Last active May 31, 2024 10:51
Show Gist options
  • Save alanz/e127e7561ddf1cfeb07fbdee9a966794 to your computer and use it in GitHub Desktop.
Save alanz/e127e7561ddf1cfeb07fbdee9a966794 to your computer and use it in GitHub Desktop.
Exact Print Annotation GHC Changes for GHC 9.10

Exact Print Annotation GHC 9.10 Changes

TLDR survival guide

  • remove makeDeltaAst, otherwise there will be no SrcSpan anywhere in the AST (but when ready to make a change, using it to capture spacing of the local thing being edited may help)
  • EpAnnNotUsed is gone, use noAnn instead
  • uniqueSrcSpanT is no longer needed, use an appropriate EpaDelta location instead
  • EpaDelta (DifferentLine row col) interprets col differently. You must add 1 to get the prior spacing.
  • If you add something to a list (possible via hsDecls / replaceDecls), the following item only needs to have setEntryDP called on it, to get the spacing right

Separation of Annotations

We have taken a more principled approach relating to the location and function of annotation types.

  • Located (EpAnn ann) a carries positioning and comments, and some limited annotations, such as trailing ones.

  • Annotations within a, no locations or comments, just keywords etc

Located Element changes

Changes in EpAnn

EpAnnNotUsed is gone

Anchor is now EpaLocation

type LocatedAn an = GenLocated (EpAnn an)

SrcAnn is gone

HasLoc class introduced

It provides a generalisation of getLoc, and getLocA is now defined as a synonym for it.

class HasLoc a where
  -- ^ conveniently calculate locations for things without locations attached
  getHasLoc :: a -> SrcSpan
getLocA :: (HasLoc a) => GenLocated a e -> SrcSpan
getLocA = getHasLoc

This also allowed getting rid of the zoo of conversion functions. All that remains is l2l, la2la, reLoc.

Below are examples of the generalisations this allows (see NoAnn details below)

sortLocatedA :: (HasLoc (EpAnn a)) => [GenLocated (EpAnn a) e] -> [GenLocated (EpAnn a) e]
sortLocatedA = sortBy (leftmost_smallest `on` getLocA)

mapLocA :: (NoAnn ann) => (a -> b) -> GenLocated SrcSpan a -> GenLocated (EpAnn ann) b
mapLocA f (L l a) = L (noAnnSrcSpan l) (f a)

combineLocsA :: Semigroup a => GenLocated (EpAnn a) e1 -> GenLocated (EpAnn a) e2 -> EpAnn a
combineLocsA (L a _) (L b _) = combineSrcSpansA a b

combineSrcSpansA :: Semigroup a => EpAnn a -> EpAnn a -> EpAnn a
combineSrcSpansA aa ab = aa <> ab

HasAnnotation

Where HasLoc provides a way of getting a SrcSpan out of a thing, HasAnnotation does the reverse, creating a thing with a SrcSpan in it.

class HasAnnotation e where
  noAnnSrcSpan :: SrcSpan -> e
noLocA :: (HasAnnotation e) => a -> GenLocated e a
noLocA = L (noAnnSrcSpan noSrcSpan)

getLocA :: (HasLoc a) => GenLocated a e -> SrcSpan
getLocA = getHasLoc

noSrcSpanA :: (HasAnnotation e) => e
noSrcSpanA = noAnnSrcSpan noSrcSpan

NoAnn class

class NoAnn a where
  -- | equivalent of `mempty`, but does not need Semigroup
  noAnn :: a

EpAnn Semigroup instance

instance (Semigroup a) => Semigroup (EpAnn a) where
  (EpAnn l1 a1 b1) <> (EpAnn l2 a2 b2) = EpAnn (l1 <> l2) (a1 <> a2) (b1 <> b2)
   -- The critical part about the anchor is its left edge, and all
   -- annotations must follow it. So we combine them which yields the
   -- largest span

Annotations inside AST elements

EpToken

-- | A token stored in the syntax tree. For example, when parsing a
-- let-expression, we store @EpToken "let"@ and @EpToken "in"@.
-- The locations of those tokens can be used to faithfully reproduce
-- (exactprint) the original program text.
data EpToken (tok :: Symbol)
  = NoEpTok
  | EpTok !EpaLocation

-- | With @UnicodeSyntax@, there might be multiple ways to write the same
-- token. For example an arrow could be either @->@ or @→@. This choice must be
-- recorded in order to exactprint such tokens, so instead of @EpToken "->"@ we
-- introduce @EpUniToken "->" "→"@.
data EpUniToken (tok :: Symbol) (utok :: Symbol)
  = NoEpUniTok
  | EpUniTok !EpaLocation !IsUnicodeSyntax

deriving instance Eq (EpToken tok)
deriving instance KnownSymbol tok => Data (EpToken tok)
deriving instance (KnownSymbol tok, KnownSymbol utok) => Data (EpUniToken tok utok)
@brandonchinn178
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment