diff options
author | Alan Zimmerman <alan.zimm@gmail.com> | 2014-11-21 11:20:13 -0600 |
---|---|---|
committer | Austin Seipp <austin@well-typed.com> | 2014-11-21 11:26:28 -0600 |
commit | 803fc5db31f084b73713342cdceaed5a9c664267 (patch) | |
tree | 176024676eb95211b2aadb43297f474983b7df75 /testsuite/tests | |
parent | 7927658ed1dcf557c7dd78e4b9844100521391c8 (diff) | |
download | haskell-803fc5db31f084b73713342cdceaed5a9c664267.tar.gz |
Add API Annotations
Summary:
The final design and discussion is captured at
https://ghc.haskell.org/trac/ghc/wiki/GhcAstAnnotations
This is a proof of concept implementation of a completely
separate annotation structure, populated in the parser,and tied to the
AST by means of a virtual "node-key" comprising the surrounding
SrcSpan and a value derived from the specific constructor used for the
node.
The key parts of the design are the following.
== The Annotations ==
In `hsSyn/ApiAnnotation.hs`
```lang=haskell
type ApiAnns = (Map.Map ApiAnnKey SrcSpan, Map.Map SrcSpan [Located Token])
type ApiAnnKey = (SrcSpan,AnnKeywordId)
-- ---------------------------------------------------------------------
-- | Retrieve an annotation based on the @SrcSpan@ of the annotated AST
-- element, and the known type of the annotation.
getAnnotation :: ApiAnns -> SrcSpan -> AnnKeywordId -> Maybe SrcSpan
getAnnotation (anns,_) span ann = Map.lookup (span,ann) anns
-- |Retrieve the comments allocated to the current @SrcSpan@
getAnnotationComments :: ApiAnns -> SrcSpan -> [Located Token]
getAnnotationComments (_,anns) span =
case Map.lookup span anns of
Just cs -> cs
Nothing -> []
-- | Note: in general the names of these are taken from the
-- corresponding token, unless otherwise noted
data AnnKeywordId
= AnnAs
| AnnBang
| AnnClass
| AnnClose -- ^ } or ] or ) or #) etc
| AnnComma
| AnnDarrow
| AnnData
| AnnDcolon
....
```
== Capturing in the lexer/parser ==
The annotations are captured in the lexer / parser by extending PState to include a field
In `parser/Lexer.x`
```lang=haskell
data PState = PState {
....
annotations :: [(ApiAnnKey,SrcSpan)]
-- Annotations giving the locations of 'noise' tokens in the
-- source, so that users of the GHC API can do source to
-- source conversions.
}
```
The lexer exposes a helper function to add an annotation
```lang=haskell
addAnnotation :: SrcSpan -> Ann -> SrcSpan -> P ()
addAnnotation l a v = P $ \s -> POk s {
annotations = ((AK l a), v) : annotations s
} ()
```
The parser also has some helper functions of the form
```lang=haskell
type MaybeAnn = Maybe (SrcSpan -> P ())
gl = getLoc
gj x = Just (gl x)
ams :: Located a -> [MaybeAnn] -> P (Located a)
ams a@(L l _) bs = (mapM_ (\a -> a l) $ catMaybes bs) >> return a
```
This allows annotations to be captured in the parser by means of
```
ctypedoc :: { LHsType RdrName }
: 'forall' tv_bndrs '.' ctypedoc {% hintExplicitForall (getLoc $1) >>
ams (LL $ mkExplicitHsForAllTy $2 (noLoc []) $4)
[mj AnnForall $1,mj AnnDot $3] }
| context '=>' ctypedoc {% ams (LL $ mkQualifiedHsForAllTy $1 $3)
[mj AnnDarrow $2] }
| ipvar '::' type {% ams (LL (HsIParamTy (unLoc $1) $3))
[mj AnnDcolon $2] }
| typedoc { $1 }
```
== Parse result ==
```lang-haskell
data HsParsedModule = HsParsedModule {
hpm_module :: Located (HsModule RdrName),
hpm_src_files :: [FilePath],
-- ^ extra source files (e.g. from #includes). The lexer collects
-- these from '# <file> <line>' pragmas, which the C preprocessor
-- leaves behind. These files and their timestamps are stored in
-- the .hi file, so that we can force recompilation if any of
-- them change (#3589)
hpm_annotations :: ApiAnns
}
-- | The result of successful parsing.
data ParsedModule =
ParsedModule { pm_mod_summary :: ModSummary
, pm_parsed_source :: ParsedSource
, pm_extra_src_files :: [FilePath]
, pm_annotations :: ApiAnns }
```
This diff depends on D426
Test Plan: sh ./validate
Reviewers: austin, simonpj, Mikolaj
Reviewed By: simonpj, Mikolaj
Subscribers: Mikolaj, goldfire, thomie, carter
Differential Revision: https://phabricator.haskell.org/D438
GHC Trac Issues: #9628
Diffstat (limited to 'testsuite/tests')
-rw-r--r-- | testsuite/tests/ghc-api/annotations/.gitignore | 7 | ||||
-rw-r--r-- | testsuite/tests/ghc-api/annotations/AnnotationLet.hs | 12 | ||||
-rw-r--r-- | testsuite/tests/ghc-api/annotations/AnnotationTuple.hs | 20 | ||||
-rw-r--r-- | testsuite/tests/ghc-api/annotations/CommentsTest.hs | 13 | ||||
-rw-r--r-- | testsuite/tests/ghc-api/annotations/Makefile | 21 | ||||
-rw-r--r-- | testsuite/tests/ghc-api/annotations/all.T | 4 | ||||
-rw-r--r-- | testsuite/tests/ghc-api/annotations/annotations.hs | 58 | ||||
-rw-r--r-- | testsuite/tests/ghc-api/annotations/annotations.stdout | 51 | ||||
-rw-r--r-- | testsuite/tests/ghc-api/annotations/comments.hs | 64 | ||||
-rw-r--r-- | testsuite/tests/ghc-api/annotations/comments.stdout | 24 | ||||
-rw-r--r-- | testsuite/tests/ghc-api/annotations/parseTree.hs | 102 | ||||
-rw-r--r-- | testsuite/tests/ghc-api/annotations/parseTree.stdout | 122 |
12 files changed, 498 insertions, 0 deletions
diff --git a/testsuite/tests/ghc-api/annotations/.gitignore b/testsuite/tests/ghc-api/annotations/.gitignore new file mode 100644 index 0000000000..61d9b24b9e --- /dev/null +++ b/testsuite/tests/ghc-api/annotations/.gitignore @@ -0,0 +1,7 @@ +annotations +parseTree +comments +*.hi +*.o +*.run.* +*.normalised diff --git a/testsuite/tests/ghc-api/annotations/AnnotationLet.hs b/testsuite/tests/ghc-api/annotations/AnnotationLet.hs new file mode 100644 index 0000000000..de30f8baaf --- /dev/null +++ b/testsuite/tests/ghc-api/annotations/AnnotationLet.hs @@ -0,0 +1,12 @@ +module AnnotationLet (foo) where + +{ +import qualified Data.List as DL +; +foo = let + a 0 = 1 + a _ = 2 + b = 2 + in a b + +} diff --git a/testsuite/tests/ghc-api/annotations/AnnotationTuple.hs b/testsuite/tests/ghc-api/annotations/AnnotationTuple.hs new file mode 100644 index 0000000000..1eced4d2cb --- /dev/null +++ b/testsuite/tests/ghc-api/annotations/AnnotationTuple.hs @@ -0,0 +1,20 @@ +{-# LANGUAGE TupleSections #-} +module AnnotationTuple (foo) where + +{ +import qualified Data.List as DL +; +foo = let + a = 1 + b = 2 + in a + b + +; +bar = print $ map (1, "hello" , 6.5,, [5, 5, 6, 7]) [Just (), Nothing] +; +baz = (1, "hello", 6.5,,,,) 'a' (Just ()) +} +-- Note: the trailing whitespace in this file is used to check that we +-- have an annotation for it. + + diff --git a/testsuite/tests/ghc-api/annotations/CommentsTest.hs b/testsuite/tests/ghc-api/annotations/CommentsTest.hs new file mode 100644 index 0000000000..ce0f336d39 --- /dev/null +++ b/testsuite/tests/ghc-api/annotations/CommentsTest.hs @@ -0,0 +1,13 @@ +{-# LANGUAGE DeriveFoldable #-} +module CommentsTest (foo) where +{- +An opening comment +-} + +import qualified Data.List as DL + +-- | The function @foo@ does blah +foo = let + a = 1 + b = 2 -- value 2 + in a + b diff --git a/testsuite/tests/ghc-api/annotations/Makefile b/testsuite/tests/ghc-api/annotations/Makefile new file mode 100644 index 0000000000..d5c7bd4973 --- /dev/null +++ b/testsuite/tests/ghc-api/annotations/Makefile @@ -0,0 +1,21 @@ +TOP=../../.. +include $(TOP)/mk/boilerplate.mk +include $(TOP)/mk/test.mk + +clean: + rm -f *.o *.hi + +annotations: clean + '$(TEST_HC)' $(TEST_HC_OPTS) --make -v0 -package ghc annotations + ./annotations "`'$(TEST_HC)' $(TEST_HC_OPTS) --print-libdir | tr -d '\r'`" + +parseTree: clean + '$(TEST_HC)' $(TEST_HC_OPTS) --make -v0 -package ghc parseTree + ./parseTree "`'$(TEST_HC)' $(TEST_HC_OPTS) --print-libdir | tr -d '\r'`" + +comments: clean + '$(TEST_HC)' $(TEST_HC_OPTS) --make -v0 -package ghc comments + ./comments "`'$(TEST_HC)' $(TEST_HC_OPTS) --print-libdir | tr -d '\r'`" + + +.PHONY: clean diff --git a/testsuite/tests/ghc-api/annotations/all.T b/testsuite/tests/ghc-api/annotations/all.T new file mode 100644 index 0000000000..54da2efda4 --- /dev/null +++ b/testsuite/tests/ghc-api/annotations/all.T @@ -0,0 +1,4 @@ +test('annotations', normal, run_command, ['$MAKE -s --no-print-directory annotations']) +test('parseTree', normal, run_command, ['$MAKE -s --no-print-directory parseTree']) +test('comments', normal, run_command, ['$MAKE -s --no-print-directory comments']) + diff --git a/testsuite/tests/ghc-api/annotations/annotations.hs b/testsuite/tests/ghc-api/annotations/annotations.hs new file mode 100644 index 0000000000..fe952601d2 --- /dev/null +++ b/testsuite/tests/ghc-api/annotations/annotations.hs @@ -0,0 +1,58 @@ +{-# LANGUAGE RankNTypes #-} + +-- This program must be called with GHC's libdir as the single command line +-- argument. +module Main where + +-- import Data.Generics +import Data.Data +import Data.List +import System.IO +import GHC +import DynFlags +import MonadUtils +import Outputable +import Bag (filterBag,isEmptyBag) +import System.Directory (removeFile) +import System.Environment( getArgs ) +import qualified Data.Map as Map +import Data.Dynamic ( fromDynamic,Dynamic ) + +main::IO() +main = do + [libdir] <- getArgs + testOneFile libdir "AnnotationLet" + +testOneFile libdir fileName = do + p <- runGhc (Just libdir) $ do + dflags <- getSessionDynFlags + setSessionDynFlags dflags + let mn =mkModuleName fileName + addTarget Target { targetId = TargetModule mn + , targetAllowObjCode = True + , targetContents = Nothing } + load LoadAllTargets + modSum <- getModSummary mn + p <- parseModule modSum + t <- typecheckModule p + d <- desugarModule t + l <- loadModule d + let ts=typecheckedSource l + r =renamedSource l + -- liftIO (putStr (showSDocDebug (ppr ts))) + return (pm_annotations p) + + let anns = p + (l,_) = fst $ head $ Map.toList (fst anns) + annModule = (getAnnotation anns l AnnModule) + annLet = (getAnnotation anns l AnnLet) + + putStrLn (intercalate "\n" [showAnns anns,pp annModule,pp annLet,pp l]) + +showAnns (anns,_) = "[\n" ++ (intercalate "\n" + $ map (\((s,k),v) + -> ("(AK " ++ pp s ++ " " ++ show k ++" = " ++ pp v ++ ")\n")) + $ Map.toList anns) + ++ "]\n" + +pp a = showPpr unsafeGlobalDynFlags a diff --git a/testsuite/tests/ghc-api/annotations/annotations.stdout b/testsuite/tests/ghc-api/annotations/annotations.stdout new file mode 100644 index 0000000000..e0c311eb18 --- /dev/null +++ b/testsuite/tests/ghc-api/annotations/annotations.stdout @@ -0,0 +1,51 @@ +[ +(AK AnnotationLet.hs:1:1 AnnClose = [AnnotationLet.hs:12:1]) + +(AK AnnotationLet.hs:1:1 AnnModule = [AnnotationLet.hs:1:1-6]) + +(AK AnnotationLet.hs:1:1 AnnOpen = [AnnotationLet.hs:3:1]) + +(AK AnnotationLet.hs:1:1 AnnSemi = [AnnotationLet.hs:5:1]) + +(AK AnnotationLet.hs:1:1 AnnWhere = [AnnotationLet.hs:1:28-32]) + +(AK AnnotationLet.hs:1:22-26 AnnClose = [AnnotationLet.hs:1:26]) + +(AK AnnotationLet.hs:1:22-26 AnnOpen = [AnnotationLet.hs:1:22]) + +(AK AnnotationLet.hs:4:1-32 AnnAs = [AnnotationLet.hs:4:28-29]) + +(AK AnnotationLet.hs:4:1-32 AnnImport = [AnnotationLet.hs:4:1-6]) + +(AK AnnotationLet.hs:4:1-32 AnnQualified = [AnnotationLet.hs:4:8-16]) + +(AK AnnotationLet.hs:(6,1)-(10,12) AnnEqual = [AnnotationLet.hs:6:5]) + +(AK AnnotationLet.hs:(6,1)-(10,12) AnnFunId = [AnnotationLet.hs:6:1-3]) + +(AK AnnotationLet.hs:(6,7)-(10,12) AnnIn = [AnnotationLet.hs:10:7-8]) + +(AK AnnotationLet.hs:(6,7)-(10,12) AnnLet = [AnnotationLet.hs:6:7-9]) + +(AK AnnotationLet.hs:7:9-15 AnnEqual = [AnnotationLet.hs:7:13]) + +(AK AnnotationLet.hs:7:9-15 AnnFunId = [AnnotationLet.hs:7:9]) + +(AK AnnotationLet.hs:8:9-15 AnnEqual = [AnnotationLet.hs:8:13]) + +(AK AnnotationLet.hs:8:9-15 AnnFunId = [AnnotationLet.hs:8:9]) + +(AK AnnotationLet.hs:8:9-15 AnnSemi = [AnnotationLet.hs:8:9]) + +(AK AnnotationLet.hs:9:9-13 AnnEqual = [AnnotationLet.hs:9:11]) + +(AK AnnotationLet.hs:9:9-13 AnnFunId = [AnnotationLet.hs:9:9]) + +(AK AnnotationLet.hs:9:9-13 AnnSemi = [AnnotationLet.hs:9:9]) + +(AK <no location info> AnnEofPos = [AnnotationLet.hs:13:1]) +] + +[AnnotationLet.hs:1:1-6] +[] +AnnotationLet.hs:1:1 diff --git a/testsuite/tests/ghc-api/annotations/comments.hs b/testsuite/tests/ghc-api/annotations/comments.hs new file mode 100644 index 0000000000..1fb1d41903 --- /dev/null +++ b/testsuite/tests/ghc-api/annotations/comments.hs @@ -0,0 +1,64 @@ +{-# LANGUAGE RankNTypes #-} + +-- This program must be called with GHC's libdir as the single command line +-- argument. +module Main where + +-- import Data.Generics +import Data.Data +import Data.List +import System.IO +import GHC +import DynFlags +import MonadUtils +import Outputable +import Bag (filterBag,isEmptyBag) +import System.Directory (removeFile) +import System.Environment( getArgs ) +import qualified Data.Map as Map +import Data.Dynamic ( fromDynamic,Dynamic ) + +main::IO() +main = do + [libdir] <- getArgs + testOneFile libdir "CommentsTest" True + testOneFile libdir "CommentsTest" False + +testOneFile libdir fileName useHaddock = do + p <- runGhc (Just libdir) $ do + dflags <- getSessionDynFlags + let dflags' = if useHaddock + then gopt_set (gopt_set dflags Opt_Haddock) + Opt_KeepRawTokenStream + else gopt_set (gopt_unset dflags Opt_Haddock) + Opt_KeepRawTokenStream + setSessionDynFlags dflags' + let mn =mkModuleName fileName + addTarget Target { targetId = TargetModule mn + , targetAllowObjCode = True + , targetContents = Nothing } + load LoadAllTargets + modSum <- getModSummary mn + p <- parseModule modSum + t <- typecheckModule p + d <- desugarModule t + l <- loadModule d + let ts=typecheckedSource l + r =renamedSource l + -- liftIO (putStr (showSDocDebug (ppr ts))) + return (pm_annotations p) + + let anns = p + + putStrLn (intercalate "\n" [showAnns anns]) + +showAnns (_,anns) = "[\n" ++ (intercalate "\n" + $ map (\(s,v) + -> ("( " ++ pp s ++" =\n[" ++ showToks v ++ "])\n")) + $ Map.toList anns) + ++ "]\n" + +showToks ts = intercalate ",\n\n" + $ map (\(L p t) -> "(" ++ pp p ++ "," ++ show t ++ ")") ts + +pp a = showPpr unsafeGlobalDynFlags a diff --git a/testsuite/tests/ghc-api/annotations/comments.stdout b/testsuite/tests/ghc-api/annotations/comments.stdout new file mode 100644 index 0000000000..82ae6e1f50 --- /dev/null +++ b/testsuite/tests/ghc-api/annotations/comments.stdout @@ -0,0 +1,24 @@ +[ +( CommentsTest.hs:(10,7)-(13,14) = +[(CommentsTest.hs:12:15-24,AnnLineComment "-- value 2")]) + +( <no location info> = +[(CommentsTest.hs:9:1-33,AnnDocCommentNext " The function @foo@ does blah"), + +(CommentsTest.hs:(3,1)-(5,2),AnnBlockComment "\nAn opening comment\n"), + +(CommentsTest.hs:1:1-31,AnnBlockComment "# LANGUAGE DeriveFoldable #")]) +] + +[ +( CommentsTest.hs:(10,7)-(13,14) = +[(CommentsTest.hs:12:15-24,AnnLineComment "-- value 2")]) + +( <no location info> = +[(CommentsTest.hs:9:1-33,AnnLineComment "-- | The function @foo@ does blah"), + +(CommentsTest.hs:(3,1)-(5,2),AnnBlockComment "\nAn opening comment\n"), + +(CommentsTest.hs:1:1-31,AnnBlockComment "# LANGUAGE DeriveFoldable #")]) +] + diff --git a/testsuite/tests/ghc-api/annotations/parseTree.hs b/testsuite/tests/ghc-api/annotations/parseTree.hs new file mode 100644 index 0000000000..2794f22607 --- /dev/null +++ b/testsuite/tests/ghc-api/annotations/parseTree.hs @@ -0,0 +1,102 @@ +{-# LANGUAGE RankNTypes #-} + +-- This program must be called with GHC's libdir as the single command line +-- argument. +module Main where + +-- import Data.Generics +import Data.Data +import Data.List +import System.IO +import GHC +import BasicTypes +import DynFlags +import MonadUtils +import Outputable +import Bag (filterBag,isEmptyBag) +import System.Directory (removeFile) +import System.Environment( getArgs ) +import qualified Data.Map as Map +import Data.Dynamic ( fromDynamic,Dynamic ) + +main::IO() +main = do + [libdir] <- getArgs + testOneFile libdir "AnnotationTuple" + +testOneFile libdir fileName = do + ((anns,cs),p) <- runGhc (Just libdir) $ do + dflags <- getSessionDynFlags + setSessionDynFlags dflags + let mn =mkModuleName fileName + addTarget Target { targetId = TargetModule mn + , targetAllowObjCode = True + , targetContents = Nothing } + load LoadAllTargets + modSum <- getModSummary mn + p <- parseModule modSum + t <- typecheckModule p + d <- desugarModule t + l <- loadModule d + let ts=typecheckedSource l + r =renamedSource l + return (pm_annotations p,p) + + let tupArgs = gq (pm_parsed_source p) + + putStrLn (pp tupArgs) + putStrLn (intercalate "\n" [showAnns anns]) + + where + gq ast = everything (++) ([] `mkQ` doLHsTupArg) ast + + doLHsTupArg :: LHsTupArg RdrName -> [(SrcSpan,String,HsExpr RdrName)] + doLHsTupArg (L l arg@(Present _)) = [(l,"p",ExplicitTuple [L l arg] Boxed)] + doLHsTupArg (L l arg@(Missing _)) = [(l,"m",ExplicitTuple [L l arg] Boxed)] + + +showAnns anns = "[\n" ++ (intercalate "\n" + $ map (\((s,k),v) + -> ("(AK " ++ pp s ++ " " ++ show k ++" = " ++ pp v ++ ")\n")) + $ Map.toList anns) + ++ "]\n" + +pp a = showPpr unsafeGlobalDynFlags a + + +-- --------------------------------------------------------------------- + +-- Copied from syb for the test + + +-- | Generic queries of type \"r\", +-- i.e., take any \"a\" and return an \"r\" +-- +type GenericQ r = forall a. Data a => a -> r + + +-- | Make a generic query; +-- start from a type-specific case; +-- return a constant otherwise +-- +mkQ :: ( Typeable a + , Typeable b + ) + => r + -> (b -> r) + -> a + -> r +(r `mkQ` br) a = case cast a of + Just b -> br b + Nothing -> r + + + +-- | Summarise all nodes in top-down, left-to-right order +everything :: (r -> r -> r) -> GenericQ r -> GenericQ r + +-- Apply f to x to summarise top-level node; +-- use gmapQ to recurse into immediate subterms; +-- use ordinary foldl to reduce list of intermediate results + +everything k f x = foldl k (f x) (gmapQ (everything k f) x) diff --git a/testsuite/tests/ghc-api/annotations/parseTree.stdout b/testsuite/tests/ghc-api/annotations/parseTree.stdout new file mode 100644 index 0000000000..b8b9aa69b7 --- /dev/null +++ b/testsuite/tests/ghc-api/annotations/parseTree.stdout @@ -0,0 +1,122 @@ +[(AnnotationTuple.hs:13:20, [p], (1)), + (AnnotationTuple.hs:13:23-29, [p], ("hello")), + (AnnotationTuple.hs:13:35-37, [p], (6.5)), + (AnnotationTuple.hs:13:38, [m], ()), + (AnnotationTuple.hs:13:41-52, [p], ([5, 5, 6, 7])), + (AnnotationTuple.hs:15:8, [p], (1)), + (AnnotationTuple.hs:15:11-17, [p], ("hello")), + (AnnotationTuple.hs:15:20-22, [p], (6.5)), + (AnnotationTuple.hs:15:23, [m], ()), + (AnnotationTuple.hs:15:24, [m], ()), + (AnnotationTuple.hs:15:25, [m], ()), + (AnnotationTuple.hs:15:26, [m], ())] +[ +(AK AnnotationTuple.hs:1:1 AnnClose = [AnnotationTuple.hs:16:1]) + +(AK AnnotationTuple.hs:1:1 AnnModule = [AnnotationTuple.hs:2:1-6]) + +(AK AnnotationTuple.hs:1:1 AnnOpen = [AnnotationTuple.hs:4:1]) + +(AK AnnotationTuple.hs:1:1 AnnSemi = [AnnotationTuple.hs:6:1]) + +(AK AnnotationTuple.hs:1:1 AnnWhere = [AnnotationTuple.hs:2:30-34]) + +(AK AnnotationTuple.hs:2:24-28 AnnClose = [AnnotationTuple.hs:2:28]) + +(AK AnnotationTuple.hs:2:24-28 AnnOpen = [AnnotationTuple.hs:2:24]) + +(AK AnnotationTuple.hs:5:1-32 AnnAs = [AnnotationTuple.hs:5:28-29]) + +(AK AnnotationTuple.hs:5:1-32 AnnImport = [AnnotationTuple.hs:5:1-6]) + +(AK AnnotationTuple.hs:5:1-32 AnnQualified = [AnnotationTuple.hs:5:8-16]) + +(AK AnnotationTuple.hs:(7,1)-(10,14) AnnEqual = [AnnotationTuple.hs:7:5]) + +(AK AnnotationTuple.hs:(7,1)-(10,14) AnnFunId = [AnnotationTuple.hs:7:1-3]) + +(AK AnnotationTuple.hs:(7,7)-(10,14) AnnIn = [AnnotationTuple.hs:10:7-8]) + +(AK AnnotationTuple.hs:(7,7)-(10,14) AnnLet = [AnnotationTuple.hs:7:7-9]) + +(AK AnnotationTuple.hs:8:9-13 AnnEqual = [AnnotationTuple.hs:8:11]) + +(AK AnnotationTuple.hs:8:9-13 AnnFunId = [AnnotationTuple.hs:8:9]) + +(AK AnnotationTuple.hs:9:9-13 AnnEqual = [AnnotationTuple.hs:9:11]) + +(AK AnnotationTuple.hs:9:9-13 AnnFunId = [AnnotationTuple.hs:9:9]) + +(AK AnnotationTuple.hs:9:9-13 AnnSemi = [AnnotationTuple.hs:9:9]) + +(AK AnnotationTuple.hs:13:1-72 AnnEqual = [AnnotationTuple.hs:13:5]) + +(AK AnnotationTuple.hs:13:1-72 AnnFunId = [AnnotationTuple.hs:13:1-3]) + +(AK AnnotationTuple.hs:13:1-72 AnnSemi = [AnnotationTuple.hs:12:1]) + +(AK AnnotationTuple.hs:13:19-53 AnnClose = [AnnotationTuple.hs:13:53]) + +(AK AnnotationTuple.hs:13:19-53 AnnOpen = [AnnotationTuple.hs:13:19]) + +(AK AnnotationTuple.hs:13:20 AnnComma = [AnnotationTuple.hs:13:21]) + +(AK AnnotationTuple.hs:13:23-29 AnnComma = [AnnotationTuple.hs:13:33]) + +(AK AnnotationTuple.hs:13:35-37 AnnComma = [AnnotationTuple.hs:13:38]) + +(AK AnnotationTuple.hs:13:39 AnnComma = [AnnotationTuple.hs:13:39]) + +(AK AnnotationTuple.hs:13:41-52 AnnClose = [AnnotationTuple.hs:13:52]) + +(AK AnnotationTuple.hs:13:41-52 AnnOpen = [AnnotationTuple.hs:13:41]) + +(AK AnnotationTuple.hs:13:42 AnnComma = [AnnotationTuple.hs:13:43]) + +(AK AnnotationTuple.hs:13:45 AnnComma = [AnnotationTuple.hs:13:46]) + +(AK AnnotationTuple.hs:13:48 AnnComma = [AnnotationTuple.hs:13:49]) + +(AK AnnotationTuple.hs:13:55-72 AnnClose = [AnnotationTuple.hs:13:72]) + +(AK AnnotationTuple.hs:13:55-72 AnnOpen = [AnnotationTuple.hs:13:55]) + +(AK AnnotationTuple.hs:13:56-62 AnnComma = [AnnotationTuple.hs:13:63]) + +(AK AnnotationTuple.hs:13:61-62 AnnClose = [AnnotationTuple.hs:13:62]) + +(AK AnnotationTuple.hs:13:61-62 AnnOpen = [AnnotationTuple.hs:13:61]) + +(AK AnnotationTuple.hs:15:1-41 AnnEqual = [AnnotationTuple.hs:15:5]) + +(AK AnnotationTuple.hs:15:1-41 AnnFunId = [AnnotationTuple.hs:15:1-3]) + +(AK AnnotationTuple.hs:15:1-41 AnnSemi = [AnnotationTuple.hs:14:1]) + +(AK AnnotationTuple.hs:15:7-27 AnnClose = [AnnotationTuple.hs:15:27]) + +(AK AnnotationTuple.hs:15:7-27 AnnOpen = [AnnotationTuple.hs:15:7]) + +(AK AnnotationTuple.hs:15:8 AnnComma = [AnnotationTuple.hs:15:9]) + +(AK AnnotationTuple.hs:15:11-17 AnnComma = [AnnotationTuple.hs:15:18]) + +(AK AnnotationTuple.hs:15:20-22 AnnComma = [AnnotationTuple.hs:15:23]) + +(AK AnnotationTuple.hs:15:24 AnnComma = [AnnotationTuple.hs:15:24]) + +(AK AnnotationTuple.hs:15:25 AnnComma = [AnnotationTuple.hs:15:25]) + +(AK AnnotationTuple.hs:15:26 AnnComma = [AnnotationTuple.hs:15:26]) + +(AK AnnotationTuple.hs:15:33-41 AnnClose = [AnnotationTuple.hs:15:41]) + +(AK AnnotationTuple.hs:15:33-41 AnnOpen = [AnnotationTuple.hs:15:33]) + +(AK AnnotationTuple.hs:15:39-40 AnnClose = [AnnotationTuple.hs:15:40]) + +(AK AnnotationTuple.hs:15:39-40 AnnOpen = [AnnotationTuple.hs:15:39]) + +(AK <no location info> AnnEofPos = [AnnotationTuple.hs:21:1]) +] + |