aihc-parser-1.0.0.2: src/Aihc/Parser/Internal/Decl.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
module Aihc.Parser.Internal.Decl
( declParser,
fixityDeclParser,
pragmaDeclParser,
typeSigDeclParser,
)
where
import Aihc.Parser.Internal.Common
import {-# SOURCE #-} Aihc.Parser.Internal.Expr (equationRhsParser, exprParser)
import Aihc.Parser.Internal.Import (warningPragmaParser)
import Aihc.Parser.Internal.Pattern (apatParser, lpatParser, patParser, patternParser)
import Aihc.Parser.Internal.Type (arrowKindParser, forallTelescopeParser, typeAppParser, typeAtomParser, typeInfixOperatorParser, typeInfixParser, typeParser, typeSignatureParser)
import Aihc.Parser.Lex (LexTokenKind (..), lexTokenKind, pattern TkVarFamily, pattern TkVarRole)
import Aihc.Parser.Syntax
import Aihc.Parser.Types (ParserErrorComponent (..), mkFoundToken)
import Control.Monad (when)
import Data.Char (isLower)
import Data.Functor (($>))
import Data.Maybe (fromMaybe, isJust)
import Data.Text (Text)
import Data.Text qualified as T
import Text.Megaparsec (anySingle, lookAhead, (<|>))
import Text.Megaparsec qualified as MP
instanceOverlapPragmaParser :: TokParser Pragma
instanceOverlapPragmaParser =
hiddenPragma "instance overlap pragma" $ \p -> case pragmaType p of
PragmaInstanceOverlap _ -> Just p
_ -> Nothing
anyPragmaParser :: String -> TokParser Pragma
anyPragmaParser expectedLabel = hiddenPragma expectedLabel Just
-- | Report core:
--
-- > decl -> gendecl
-- > | (funlhs | pat) rhs
declParser :: TokParser Decl
declParser = pragmaDeclParser <|> ordinaryDeclParser
ordinaryDeclParser :: TokParser Decl
ordinaryDeclParser = do
(tok, nextTok) <- lookAhead ((,) <$> anySingle <*> anySingle)
exprFallback <- exprDeclEnabled
typeSigPrefix <- startsWithTypeSig
let tokKind = lexTokenKind tok
nextTokKind = lexTokenKind nextTok
valueDecl
| exprFallback = MP.try valueDeclParser <|> exprDeclParser
| otherwise = valueDeclParser
sigOrValueDecl
| typeSigPrefix = typeSigOrPatternTypeSigDeclParser
| otherwise = valueDecl
case tokKind of
TkKeywordData ->
case nextTokKind of
TkVarFamily -> dataFamilyDeclParser
TkKeywordInstance -> dataFamilyInstParser
_ -> dataDeclParser
TkKeywordClass -> classDeclParser
TkKeywordDefault -> defaultDeclParser
TkKeywordDeriving -> standaloneDerivingDeclParser
TkKeywordForeign -> foreignDeclParser
TkKeywordInfix -> fixityDeclParser
TkKeywordInfixl -> fixityDeclParser
TkKeywordInfixr -> fixityDeclParser
TkKeywordInstance -> instanceDeclParser
TkKeywordNewtype
| nextTokKind == TkKeywordInstance -> newtypeFamilyInstParser
TkKeywordNewtype -> newtypeDeclParser
TkKeywordType ->
case nextTokKind of
TkVarRole -> roleAnnotationDeclParser
TkVarFamily -> typeFamilyDeclParser
TkKeywordData -> typeDataDeclParser
TkKeywordInstance -> typeFamilyInstParser
_ -> typeDeclarationParser
TkKeywordPattern -> patternSynonymParser
TkVarId {} ->
case nextTokKind of
TkReservedDoubleColon -> sigOrValueDecl
TkSpecialComma -> sigOrValueDecl
TkReservedEquals -> valueDecl
_ -> nonBareVarPatternBindDeclParser <|> valueDecl
_ ->
(if typeSigPrefix then typeSigOrPatternTypeSigDeclParser else MP.empty)
<|> MP.try valueDeclParser
<|> patternBindDeclParser
<|> (if exprFallback then exprDeclParser else MP.empty)
-- | Like 'patternBindDeclParser' but rejects bare variable patterns.
-- When the leading token is a variable identifier, a bare @x = 5@ must be
-- parsed as a zero-argument function bind, not a pattern bind. This parser
-- detects that case early (after parsing the pattern) and fails, letting
-- 'valueDeclParser' handle it instead.
nonBareVarPatternBindDeclParser :: TokParser Decl
nonBareVarPatternBindDeclParser = MP.try $ withSpanAnn (DeclAnn . mkAnnotation) $ do
pat <- region "while parsing pattern binding" patternParser
case pat of
PVar {} -> fail "bare variable bindings are parsed as function declarations"
_ -> do
DeclValue . PatternBind NoMultiplicityTag pat <$> equationRhsParser
-- | Parse a pragma declaration (e.g. {-# INLINE f #-}, {-# SPECIALIZE ... #-})
pragmaDeclParser :: TokParser Decl
pragmaDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ DeclPragma <$> anyPragmaParser "pragma declaration"
-- | Check whether the expression-as-declaration fallback is enabled.
-- GHC allows top-level expressions under TemplateHaskell (bare splices),
-- TemplateHaskellQuotes, and QuasiQuotes (e.g. @[qq|...|]@ at the top level).
exprDeclEnabled :: TokParser Bool
exprDeclEnabled = do
th <- isExtensionEnabled TemplateHaskell
thq <- isExtensionEnabled TemplateHaskellQuotes
qq <- isExtensionEnabled QuasiQuotes
pure (th || thq || qq)
-- | Parse a top-level expression as a declaration.
--
-- GHC allows arbitrary expressions at the top level under TemplateHaskell
-- (interpreted as declaration splices). This parser wraps the expression in
-- 'DeclSplice'. The expression parser itself handles extension-specific
-- constructs (e.g. @$expr@, @$(expr)@ via TH, @[qq|...|]@ via QuasiQuotes),
-- so no special dispatch is needed here.
exprDeclParser :: TokParser Decl
exprDeclParser = DeclSplice <$> exprParser
-- | Parse a @type@ declaration after the @type@ keyword has been consumed.
-- Uses 'typeDeclHeadParser' to handle both prefix and infix type heads,
-- then dispatches based on the next token:
-- - @::@ → standalone kind signature (must have zero type parameters)
-- - @=@ → type synonym
--
-- > topdecl -> 'type' simpletype '=' type
typeDeclarationParser :: TokParser Decl
typeDeclarationParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordType
typeHead <- typeDeclHeadParser
nextTok <- anySingle
case lexTokenKind nextTok of
TkReservedDoubleColon -> do
-- Standalone kind signature: cannot have type parameters
if null (binderHeadParams typeHead)
then
DeclStandaloneKindSig (binderHeadName typeHead) <$> typeParser
else
fail "Standalone kind signatures cannot have type parameters."
TkReservedEquals -> do
body <- typeParser
pure $
DeclTypeSyn
TypeSynDecl
{ typeSynHead = typeHead,
typeSynBody = body
}
_ ->
MP.customFailure
UnexpectedTokenExpecting
{ unexpectedFound = Just (mkFoundToken nextTok),
unexpectedExpecting = "'::' or '=' after type declaration head",
unexpectedContext = []
}
roleAnnotationDeclParser :: TokParser Decl
roleAnnotationDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordType
expectedTok TkVarRole
typeName <- constructorUnqualifiedNameParser <|> parens constructorOperatorUnqualifiedNameParser
roles <- MP.many roleParser
pure $
DeclRoleAnnotation
RoleAnnotation
{ roleAnnotationName = typeName,
roleAnnotationRoles = roles
}
roleParser :: TokParser Role
roleParser =
(varIdTok "nominal" >> pure RoleNominal)
<|> (varIdTok "representational" >> pure RoleRepresentational)
<|> (varIdTok "phantom" >> pure RolePhantom)
<|> (expectedTok TkKeywordUnderscore >> pure RoleInfer)
-- ---------------------------------------------------------------------------
-- TypeFamilies: shared helpers
-- | Parse an optional declaration context, returning @[]@ on absence.
-- Uses 'MP.try' so inputs like @data T :: C => ()@ can backtrack from a
-- failed context parse and treat @::@ as an inline kind signature instead.
contextPrefixDispatchList :: TokParser [Type]
contextPrefixDispatchList = MP.option [] (MP.try (declContextParser <* expectedTok TkReservedDoubleArrow))
-- | Parse a declaration head with an optional leading context, but prefer an
-- inline @::@ result kind when the head is immediately followed by @::@.
--
-- Without this preference, inputs like @data T :: C => K@ are ambiguous:
-- @T :: C@ is also a valid context item. GHC resolves that form as a head with
-- an inline result kind, so we do the same.
declHeadPreferringInlineKind :: TokParser head -> TokParser (Maybe [Type], head, Maybe Type)
declHeadPreferringInlineKind headParser =
MP.try
( do
head' <- headParser
inlineKind <- expectedTok TkReservedDoubleColon *> typeParser
pure (Nothing, head', Just inlineKind)
)
<|> do
(context, head') <- declHeadWithOptionalContext headParser
inlineKind <- MP.optional (expectedTok TkReservedDoubleColon *> typeParser)
pure (context, head', inlineKind)
-- | Parse a declaration head that may be preceded by a context.
--
-- The @MP.try@ must cover both the context and the following head parser so
-- inputs like @data T :: C => ()@ can backtrack and treat @::@ as an inline
-- kind signature rather than committing to a datatype context too early.
declHeadWithOptionalContext :: TokParser head -> TokParser (Maybe [Type], head)
declHeadWithOptionalContext headParser =
MP.try ((,) . Just <$> (declContextParser <* expectedTok TkReservedDoubleArrow) <*> headParser)
<|> ((Nothing,) <$> headParser)
-- | Parse an explicit forall telescope: @forall a (b :: Kind).@.
-- Used for type family instances, data family instances, and instance heads.
explicitForallParser :: TokParser [TyVarBinder]
explicitForallParser = do
expectedTok TkKeywordForall
binders <- MP.some explicitForallBinderParser
expectedTok (TkVarSym ".")
pure binders
-- | Parse an optional unnamed @:: Kind@ result signature for a family head.
familyResultKindParser :: TokParser (Maybe Type)
familyResultKindParser =
MP.optional (expectedTok TkReservedDoubleColon *> typeParser)
-- | Parse an optional type family result signature. GHC admits either an unnamed
-- @:: Kind@ annotation or a named result variable with optional injectivity annotation,
-- such as @= r@, @= r | r -> a@, or @= (r :: Type) | r -> a where ...@.
--
-- The 'Bool' parameter indicates whether the @family@ keyword was explicitly
-- present. When it was /not/ present (i.e. the shorthand @type T a …@ form
-- inside a class body), @= r@ is syntactically ambiguous with a default type
-- instance (@type T a = r@). GHC resolves this by treating @= binder |@ as a
-- named result signature with injectivity, while @= expr@ without @|@ is
-- always a default type instance. We mirror that behaviour: without an
-- explicit @family@ keyword, @namedSigParser@ requires the @|@ injectivity
-- annotation that follows the binder.
typeFamilyResultSigParser :: Bool -> TokParser (Maybe TypeFamilyResultSig)
typeFamilyResultSigParser explicitFamily =
MP.optional (kindSigParser <|> namedSigParser)
where
kindSigParser =
TypeFamilyKindSig <$> (expectedTok TkReservedDoubleColon *> typeParser)
namedSigParser = do
expectedTok TkReservedEquals
result <- namedResultBinderParser
mInjectivity <- MP.optional typeFamilyInjectivityParser
case mInjectivity of
Just injectivity -> pure $ TypeFamilyInjectiveSig result injectivity
Nothing
-- With an explicit @family@ keyword, @= r@ on its own is
-- unambiguously a named result signature.
| explicitFamily -> pure $ TypeFamilyTyVarSig result
-- Without @family@, @= r@ (no injectivity @|@) is ambiguous with a
-- default type instance. Fail so the caller can backtrack and try
-- the default-instance parser instead.
| otherwise -> fail "named result sig without injectivity requires explicit 'family' keyword"
namedResultBinderParser =
withSpan $
( do
ident <- lowerIdentifierParser
pure (\span' -> TyVarBinder [mkAnnotation span'] ident Nothing TyVarBSpecified TyVarBVisible)
)
<|> ( do
expectedTok TkSpecialLParen
ident <- lowerIdentifierParser
expectedTok TkReservedDoubleColon
kind <- typeParser
expectedTok TkSpecialRParen
pure (\span' -> TyVarBinder [mkAnnotation span'] ident (Just kind) TyVarBSpecified TyVarBVisible)
)
typeFamilyInjectivityParser :: TokParser TypeFamilyInjectivity
typeFamilyInjectivityParser = withSpan $ do
expectedTok TkReservedPipe
result <- lowerIdentifierParser
expectedTok TkReservedRightArrow
determined <- MP.some lowerIdentifierParser
pure $ \span' ->
TypeFamilyInjectivity
{ typeFamilyInjectivityAnns = [mkAnnotation span'],
typeFamilyInjectivityResult = result,
typeFamilyInjectivityDetermined = determined
}
-- ---------------------------------------------------------------------------
-- TypeFamilies: top-level type family declaration
-- | Parse @type family Name params [:: Kind] [where { equations }]@
typeFamilyDeclParser :: TokParser Decl
typeFamilyDeclParser =
withSpanAnn (DeclAnn . mkAnnotation) $
DeclTypeFamilyDecl <$> typeFamilyDeclBodyParser FamilyKeywordRequired
-- | Parse the @where { eq; ... }@ block of a closed type family.
closedTypeFamilyWhereParser :: TokParser [TypeFamilyEq]
closedTypeFamilyWhereParser = whereClauseItemsParser typeFamilyEqParser
-- | Parse one closed type family equation: @[forall binders.] LhsType = RhsType@
typeFamilyEqParser :: TokParser TypeFamilyEq
typeFamilyEqParser = withSpan $ do
forallBinders <- MP.option [] explicitForallParser
(headForm, lhs) <- typeFamilyLhsParser
expectedTok TkReservedEquals
rhs <- typeParser
pure $ \span' ->
TypeFamilyEq
{ typeFamilyEqAnns = [mkAnnotation span'],
typeFamilyEqForall = forallBinders,
typeFamilyEqHeadForm = headForm,
typeFamilyEqLhs = lhs,
typeFamilyEqRhs = rhs
}
-- ---------------------------------------------------------------------------
-- TypeFamilies: top-level data family declaration
-- | Parse @data family Name params [:: Kind]@
dataFamilyDeclParser :: TokParser Decl
dataFamilyDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordData
varIdTok "family"
head' <- typeDeclHeadParser
kind <- familyResultKindParser
pure $
DeclDataFamilyDecl
DataFamilyDecl
{ dataFamilyDeclHead = head',
dataFamilyDeclKind = kind
}
-- ---------------------------------------------------------------------------
-- TypeFamilies: top-level type/data/newtype family instances
-- | Parse @type instance [forall binders.] LhsType = RhsType@
typeFamilyInstParser :: TokParser Decl
typeFamilyInstParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordType
expectedTok TkKeywordInstance
forallBinders <- MP.option [] explicitForallParser
(headForm, lhs) <- typeFamilyLhsParser
expectedTok TkReservedEquals
rhs <- typeParser
pure $
DeclTypeFamilyInst
TypeFamilyInst
{ typeFamilyInstForall = forallBinders,
typeFamilyInstHeadForm = headForm,
typeFamilyInstLhs = lhs,
typeFamilyInstRhs = rhs
}
-- | Parse @data instance [forall binders.] HeadType = Cons | ...@ (also GADT style)
dataFamilyInstParser :: TokParser Decl
dataFamilyInstParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordData
expectedTok TkKeywordInstance
forallBinders <- MP.option [] explicitForallParser
(_, head') <- typeFamilyLhsParser
kind <- familyResultKindParser
(constructors, derivingClauses) <- gadtDataDeclParser <|> traditionalDataDeclParser
pure $
DeclDataFamilyInst
DataFamilyInst
{ dataFamilyInstIsNewtype = False,
dataFamilyInstForall = forallBinders,
dataFamilyInstHead = head',
dataFamilyInstKind = kind,
dataFamilyInstConstructors = constructors,
dataFamilyInstDeriving = derivingClauses
}
-- | Parse @newtype instance [forall binders.] HeadType = Constructor@
newtypeFamilyInstParser :: TokParser Decl
newtypeFamilyInstParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordNewtype
expectedTok TkKeywordInstance
forallBinders <- MP.option [] explicitForallParser
(_, head') <- typeFamilyLhsParser
kind <- familyResultKindParser
expectedTok TkReservedEquals
constructor <- dataConDeclParser
derivingClauses <- MP.many derivingClauseParser
pure $
DeclDataFamilyInst
DataFamilyInst
{ dataFamilyInstIsNewtype = True,
dataFamilyInstForall = forallBinders,
dataFamilyInstHead = head',
dataFamilyInstKind = kind,
dataFamilyInstConstructors = [constructor],
dataFamilyInstDeriving = derivingClauses
}
-- ---------------------------------------------------------------------------
-- TypeFamilies: class body items (associated type/data families + defaults)
-- | Parse @type [family] Name params [:: Kind]@ as an associated type family in a class.
-- Callers must ensure the next token after @type@ is not @instance@
-- (which is handled by 'classDefaultTypeInstParser' via token dispatch).
classTypeFamilyDeclParser :: TokParser ClassDeclItem
classTypeFamilyDeclParser =
withSpanAnn (ClassItemAnn . mkAnnotation) $
ClassItemTypeFamilyDecl <$> typeFamilyDeclBodyParser FamilyKeywordOptional
-- | Parse @data Name params [:: Kind]@ as an associated data family in a class.
classDataFamilyDeclParser :: TokParser ClassDeclItem
classDataFamilyDeclParser = withSpanAnn (ClassItemAnn . mkAnnotation) $ do
expectedTok TkKeywordData
head' <- typeDeclHeadParser
kind <- familyResultKindParser
pure
( ClassItemDataFamilyDecl
DataFamilyDecl
{ dataFamilyDeclHead = head',
dataFamilyDeclKind = kind
}
)
-- | Parse @type instance LhsType = RhsType@ as a default type family instance in a class.
classDefaultTypeInstParser :: TokParser ClassDeclItem
classDefaultTypeInstParser = classDefaultTypeInstParser' True
-- | Parse @type [forall binders.] LhsType = RhsType@ as a shorthand default
-- associated type instance in a class body.
classDefaultTypeInstShorthandParser :: TokParser ClassDeclItem
classDefaultTypeInstShorthandParser = classDefaultTypeInstParser' False
classDefaultTypeInstParser' :: Bool -> TokParser ClassDeclItem
classDefaultTypeInstParser' requireInstance = withSpanAnn (ClassItemAnn . mkAnnotation) $ do
expectedTok TkKeywordType
when requireInstance (expectedTok TkKeywordInstance)
forallBinders <- MP.option [] explicitForallParser
(headForm, lhs) <- typeFamilyLhsParser
expectedTok TkReservedEquals
rhs <- typeParser
pure
( ClassItemDefaultTypeInst
TypeFamilyInst
{ typeFamilyInstForall = forallBinders,
typeFamilyInstHeadForm = headForm,
typeFamilyInstLhs = lhs,
typeFamilyInstRhs = rhs
}
)
-- ---------------------------------------------------------------------------
-- TypeFamilies: instance body items
-- | Parse @type [instance] LhsType = RhsType@ inside an instance body.
-- The @instance@ keyword is accepted but optional (GHC normalizes both forms
-- to the same AST, so we treat them identically).
instanceTypeFamilyInstParser :: TokParser InstanceDeclItem
instanceTypeFamilyInstParser = withSpanAnn (InstanceItemAnn . mkAnnotation) $ do
expectedTok TkKeywordType
_ <- MP.optional (expectedTok TkKeywordInstance)
forallBinders <- MP.option [] explicitForallParser
(headForm, lhs) <- typeFamilyLhsParser
expectedTok TkReservedEquals
rhs <- typeParser
pure $
InstanceItemTypeFamilyInst
TypeFamilyInst
{ typeFamilyInstForall = forallBinders,
typeFamilyInstHeadForm = headForm,
typeFamilyInstLhs = lhs,
typeFamilyInstRhs = rhs
}
-- | Parse @data [instance] HeadType = Cons | ...@ (or GADT style) inside an instance body.
-- The @instance@ keyword is accepted but optional.
instanceDataFamilyInstParser :: TokParser InstanceDeclItem
instanceDataFamilyInstParser = withSpanAnn (InstanceItemAnn . mkAnnotation) $ do
expectedTok TkKeywordData
_ <- MP.optional (expectedTok TkKeywordInstance)
(_, head') <- typeFamilyLhsParser
kind <- familyResultKindParser
(constructors, derivingClauses) <- gadtDataDeclParser <|> traditionalDataDeclParser
pure $
InstanceItemDataFamilyInst
DataFamilyInst
{ dataFamilyInstIsNewtype = False,
dataFamilyInstForall = [],
dataFamilyInstHead = head',
dataFamilyInstKind = kind,
dataFamilyInstConstructors = constructors,
dataFamilyInstDeriving = derivingClauses
}
-- | Parse @newtype [instance] HeadType = Constructor@ inside an instance body.
-- The @instance@ keyword is accepted but optional.
instanceNewtypeFamilyInstParser :: TokParser InstanceDeclItem
instanceNewtypeFamilyInstParser = withSpanAnn (InstanceItemAnn . mkAnnotation) $ do
expectedTok TkKeywordNewtype
_ <- MP.optional (expectedTok TkKeywordInstance)
(_, head') <- typeFamilyLhsParser
kind <- familyResultKindParser
expectedTok TkReservedEquals
constructor <- dataConDeclParser
derivingClauses <- MP.many derivingClauseParser
pure $
InstanceItemDataFamilyInst
DataFamilyInst
{ dataFamilyInstIsNewtype = True,
dataFamilyInstForall = [],
dataFamilyInstHead = head',
dataFamilyInstKind = kind,
dataFamilyInstConstructors = [constructor],
dataFamilyInstDeriving = derivingClauses
}
-- ---------------------------------------------------------------------------
-- | Report core general declaration:
--
-- > gendecl -> vars '::' [context '=>'] type
typeSigDeclParser :: TokParser Decl
typeSigDeclParser =
withSpanAnn (DeclAnn . mkAnnotation) $
uncurry DeclTypeSig <$> typedSignaturePrefixParser typeSignatureParser
-- | Parse a type signature or a pattern-typed equation.
--
-- With @ScopedTypeVariables@, @f :: Int = 0@ is valid Haskell meaning the same
-- as @(f :: Int) = 0@: a pattern bind whose LHS carries a type annotation.
-- GHC parses @name :: Type@ and then, if @=@ (or a guard @|@) follows,
-- reinterprets the construct as a 'PatternBind' with a 'PTypeSig' pattern.
--
-- This parser mirrors that behaviour at the top level. It first parses
-- @name(s) :: Type@ (the type-signature prefix), then peeks at the next
-- token. If the next token begins an equation RHS (@=@ or @|@), the result is
-- reinterpreted as a pattern-typed equation; otherwise a plain type signature
-- is returned.
typeSigOrPatternTypeSigDeclParser :: TokParser Decl
typeSigOrPatternTypeSigDeclParser =
withSpanAnn (DeclAnn . mkAnnotation) $
typedBindingOrSignatureParser
typeSignatureParser
DeclTypeSig
( \name ty -> do
rhs <- equationRhsParser
let pat = PTypeSig (PVar name) ty
pure (DeclValue (PatternBind NoMultiplicityTag pat rhs))
)
"typed pattern bindings with '=' require exactly one binder"
defaultDeclParser :: TokParser Decl
defaultDeclParser = do
expectedTok TkKeywordDefault
DeclDefault <$> parens (typeParser `MP.sepEndBy` expectedTok TkSpecialComma)
-- | Report core general declaration:
--
-- > gendecl -> fixity [integer] ops
fixityDeclParser :: TokParser Decl
fixityDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
(parsedAssoc, prec, mNamespace, ops) <- fixityDeclPartsParser
pure (DeclFixity parsedAssoc mNamespace prec ops)
fixityDeclPartsParser :: TokParser (FixityAssoc, Maybe Int, Maybe IEEntityNamespace, [UnqualifiedName])
fixityDeclPartsParser = do
assoc <- fixityAssocParser
prec <- MP.optional fixityPrecedenceParser
mNamespace <- MP.optional fixityNamespaceParser
ops <- fixityOperatorParser `MP.sepBy1` expectedTok TkSpecialComma
pure (assoc, prec, mNamespace, ops)
fixityNamespaceParser :: TokParser IEEntityNamespace
fixityNamespaceParser =
(expectedTok TkKeywordType >> pure IEEntityNamespaceType)
<|> (expectedTok TkKeywordData >> pure IEEntityNamespaceData)
fixityAssocParser :: TokParser FixityAssoc
fixityAssocParser =
(expectedTok TkKeywordInfix >> pure Infix)
<|> (expectedTok TkKeywordInfixl >> pure InfixL)
<|> (expectedTok TkKeywordInfixr >> pure InfixR)
fixityPrecedenceParser :: TokParser Int
fixityPrecedenceParser =
tokenSatisfy "fixity precedence" $ \tok ->
case lexTokenKind tok of
TkInteger n _
| n <= 9 -> Just (fromInteger n)
_ -> Nothing
fixityOperatorParser :: TokParser UnqualifiedName
fixityOperatorParser =
symbolicOperatorParser <|> backtickIdentifierParser
where
symbolicOperatorParser =
tokenSatisfy "fixity operator" $ \tok ->
case lexTokenKind tok of
TkVarSym op -> Just (mkUnqualifiedName NameVarSym op)
TkConSym op -> Just (mkUnqualifiedName NameConSym op)
TkReservedColon -> Just (mkUnqualifiedName NameConSym ":")
TkReservedRightArrow -> Just (mkUnqualifiedName NameVarSym "->")
_ -> Nothing
backtickIdentifierParser = do
expectedTok TkSpecialBacktick
op <- identifierUnqualifiedNameParser
expectedTok TkSpecialBacktick
pure op
-- | Report core:
--
-- > topdecl -> 'class' [scontext '=>'] tycls tyvar ['where' cdecls]
classDeclParser :: TokParser Decl
classDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordClass
(context, classHead) <- declHeadWithOptionalContext classHeadParser
classFundeps <- MP.option [] (MP.try classFundepsParser)
items <- MP.option [] classWhereClauseParser
pure $
DeclClass
ClassDecl
{ classDeclContext = context,
classDeclHead = classHead,
classDeclFundeps = classFundeps,
classDeclItems = items
}
classFundepsParser :: TokParser [FunctionalDependency]
classFundepsParser = do
expectedTok TkReservedPipe
classFundepParser `MP.sepBy1` expectedTok TkSpecialComma
classFundepParser :: TokParser FunctionalDependency
classFundepParser = withSpan $ do
determinedBy <- MP.many lowerIdentifierParser
expectedTok TkReservedRightArrow
determines <- MP.many lowerIdentifierParser
pure $ \span' ->
FunctionalDependency
{ functionalDependencyAnns = [mkAnnotation span'],
functionalDependencyDeterminers = determinedBy,
functionalDependencyDetermined = determines
}
classWhereClauseParser :: TokParser [ClassDeclItem]
classWhereClauseParser = do
expectedTok TkKeywordWhere
bracedSemiSep classDeclItemParser
<|> plainSemiSep1 classDeclItemParser
<|> pure []
whereClauseItemsParser :: TokParser a -> TokParser [a]
whereClauseItemsParser itemParser = do
expectedTok TkKeywordWhere
bracedSemiSep itemParser <|> plainSemiSep1 itemParser <|> pure []
-- | Report core:
--
-- > cdecl -> gendecl
-- > | (funlhs | var) rhs
classDeclItemParser :: TokParser ClassDeclItem
classDeclItemParser =
classPragmaItemParser
<|> do
(tok, nextTok) <- lookAhead ((,) <$> anySingle <*> anySingle)
typeSigPrefix <- startsWithTypeSig
case lexTokenKind tok of
TkKeywordInfix -> classFixityItemParser
TkKeywordInfixl -> classFixityItemParser
TkKeywordInfixr -> classFixityItemParser
TkKeywordData -> classDataFamilyDeclParser
TkKeywordDefault -> classDefaultSigItemParser
TkKeywordType
| lexTokenKind nextTok == TkKeywordInstance -> classDefaultTypeInstParser
TkKeywordType -> MP.try classTypeFamilyDeclParser <|> classDefaultTypeInstShorthandParser
_ -> (if typeSigPrefix then classTypeSigItemParser else classDefaultItemParser)
classPragmaItemParser :: TokParser ClassDeclItem
classPragmaItemParser =
withSpanAnn (ClassItemAnn . mkAnnotation) $
ClassItemPragma <$> anyPragmaParser "pragma declaration"
classTypeSigItemParser :: TokParser ClassDeclItem
classTypeSigItemParser = typeSigItemParser (ClassItemAnn . mkAnnotation) ClassItemTypeSig
classDefaultSigItemParser :: TokParser ClassDeclItem
classDefaultSigItemParser = withSpanAnn (ClassItemAnn . mkAnnotation) $ do
expectedTok TkKeywordDefault
name <- binderNameParser
expectedTok TkReservedDoubleColon
ClassItemDefaultSig name <$> typeSignatureParser
classFixityItemParser :: TokParser ClassDeclItem
classFixityItemParser = fixityItemParser (ClassItemAnn . mkAnnotation) ClassItemFixity
classDefaultItemParser :: TokParser ClassDeclItem
classDefaultItemParser = valueItemParser (ClassItemAnn . mkAnnotation) ClassItemDefault
-- | Report core:
--
-- > topdecl -> 'instance' [scontext '=>'] qtycls inst ['where' idecls]
instanceDeclParser :: TokParser Decl
instanceDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordInstance
overlapPragmas <- MP.option [] (fmap (: []) instanceOverlapPragmaParser)
warningText <- MP.optional warningPragmaParser
forallBinders <- MP.optional explicitForallParser
(context, instanceHead) <- declHeadWithOptionalContext typeInfixParser
items <- MP.option [] instanceWhereClauseParser
pure $
DeclInstance
InstanceDecl
{ instanceDeclPragmas = overlapPragmas,
instanceDeclWarning = warningText,
instanceDeclForall = fromMaybe [] forallBinders,
instanceDeclContext = fromMaybe [] context,
instanceDeclHead = instanceHead,
instanceDeclItems = items
}
standaloneDerivingDeclParser :: TokParser Decl
standaloneDerivingDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordDeriving
strategy <- MP.optional derivingStrategyParser
expectedTok TkKeywordInstance
overlapPragmas <- MP.option [] (fmap (: []) instanceOverlapPragmaParser)
warningText <- MP.optional warningPragmaParser
forallBinders <- MP.optional explicitForallParser
(context, derivingHead) <- declHeadWithOptionalContext typeInfixParser
pure $
DeclStandaloneDeriving
StandaloneDerivingDecl
{ standaloneDerivingStrategy = strategy,
standaloneDerivingPragmas = overlapPragmas,
standaloneDerivingWarning = warningText,
standaloneDerivingForall = fromMaybe [] forallBinders,
standaloneDerivingContext = fromMaybe [] context,
standaloneDerivingHead = derivingHead
}
instanceWhereClauseParser :: TokParser [InstanceDeclItem]
instanceWhereClauseParser = do
expectedTok TkKeywordWhere
bracedSemiSep instanceDeclItemParser
<|> plainSemiSep1 instanceDeclItemParser
<|> pure []
-- | Report core:
--
-- > idecl -> (funlhs | var) rhs
-- > | empty
instanceDeclItemParser :: TokParser InstanceDeclItem
instanceDeclItemParser =
instancePragmaItemParser
<|> do
tok <- lookAhead anySingle
typeSigPrefix <- startsWithTypeSig
case lexTokenKind tok of
TkKeywordInfix -> instanceFixityItemParser
TkKeywordInfixl -> instanceFixityItemParser
TkKeywordInfixr -> instanceFixityItemParser
TkKeywordData -> instanceDataFamilyInstParser
TkKeywordNewtype -> instanceNewtypeFamilyInstParser
TkKeywordType -> instanceTypeFamilyInstParser
_ -> (if typeSigPrefix then instanceTypeSigItemParser else instanceValueItemParser)
instancePragmaItemParser :: TokParser InstanceDeclItem
instancePragmaItemParser = withSpanAnn (InstanceItemAnn . mkAnnotation) $ do
pragma <- anyPragmaParser "pragma declaration"
pure (InstanceItemPragma pragma)
instanceTypeSigItemParser :: TokParser InstanceDeclItem
instanceTypeSigItemParser = typeSigItemParser (InstanceItemAnn . mkAnnotation) InstanceItemTypeSig
instanceFixityItemParser :: TokParser InstanceDeclItem
instanceFixityItemParser = fixityItemParser (InstanceItemAnn . mkAnnotation) InstanceItemFixity
instanceValueItemParser :: TokParser InstanceDeclItem
instanceValueItemParser = valueItemParser (InstanceItemAnn . mkAnnotation) InstanceItemBind
-- ---------------------------------------------------------------------------
-- Shared class/instance item helpers
typeSigItemParser :: (SourceSpan -> a -> a) -> ([UnqualifiedName] -> Type -> a) -> TokParser a
typeSigItemParser ann ctor = withSpanAnn ann $ uncurry ctor <$> typedSignaturePrefixParser typeSignatureParser
fixityItemParser :: (SourceSpan -> a -> a) -> (FixityAssoc -> Maybe IEEntityNamespace -> Maybe Int -> [UnqualifiedName] -> a) -> TokParser a
fixityItemParser ann ctor = withSpanAnn ann $ do
(assoc, prec, mNamespace, ops) <- fixityDeclPartsParser
pure (ctor assoc mNamespace prec ops)
valueItemParser :: (SourceSpan -> a -> a) -> (ValueDecl -> a) -> TokParser a
valueItemParser ann ctor = withSpanAnn ann $ do
(headForm, name, pats) <- functionHeadParserWith patParser apatParser
ctor . functionBindValue headForm name pats <$> equationRhsParser
foreignDeclParser :: TokParser Decl
foreignDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordForeign
direction <- foreignDirectionParser
callConv <- callConvParser
safety <-
case direction of
ForeignImport -> MP.optional foreignSafetyParser
ForeignExport -> pure Nothing
entity <- MP.optional foreignEntityParser
name <- binderNameParser
expectedTok TkReservedDoubleColon
ty <- typeSignatureParser
pure $
DeclForeign
ForeignDecl
{ foreignDirection = direction,
foreignCallConv = callConv,
foreignSafety = safety,
foreignEntity = fromMaybe ForeignEntityOmitted entity,
foreignName = name,
foreignType = ty
}
foreignDirectionParser :: TokParser ForeignDirection
foreignDirectionParser =
(expectedTok TkKeywordImport >> pure ForeignImport)
<|> (varIdTok "export" >> pure ForeignExport)
callConvParser :: TokParser CallConv
callConvParser =
(varIdTok "ccall" >> pure CCall)
<|> (varIdTok "stdcall" >> pure StdCall)
<|> (varIdTok "capi" >> pure CApi)
<|> (varIdTok "prim" >> pure CPrim)
<|> (varIdTok "javascript" >> pure JavaScript)
foreignSafetyParser :: TokParser ForeignSafety
foreignSafetyParser =
(varIdTok "safe" >> pure Safe)
<|> (varIdTok "unsafe" >> pure Unsafe)
<|> (varIdTok "interruptible" >> pure Interruptible)
foreignEntityParser :: TokParser ForeignEntitySpec
foreignEntityParser = foreignEntityFromString <$> stringTextParser
foreignEntityFromString :: Text -> ForeignEntitySpec
foreignEntityFromString txt
| txt == "dynamic" = ForeignEntityDynamic
| txt == "wrapper" = ForeignEntityWrapper
| txt == "static" = ForeignEntityStatic Nothing
| Just rest <- T.stripPrefix "static " txt = ForeignEntityStatic (Just rest)
| txt == "&" = ForeignEntityAddress Nothing
| Just rest <- T.stripPrefix "&" txt = ForeignEntityAddress (Just rest)
| otherwise = ForeignEntityNamed txt
traditionalDataDeclParser :: TokParser ([DataConDecl], [DerivingClause])
traditionalDataDeclParser = do
constructors <- MP.optional (expectedTok TkReservedEquals *> dataConDeclParser `MP.sepBy1` expectedTok TkReservedPipe)
derivingClauses <- MP.many derivingClauseParser
pure (fromMaybe [] constructors, derivingClauses)
gadtDataDeclParser :: TokParser ([DataConDecl], [DerivingClause])
gadtDataDeclParser = do
constructors <- gadtWhereClauseParser
derivingClauses <- MP.many derivingClauseParser
pure (constructors, derivingClauses)
-- | Report core:
--
-- > topdecl -> 'data' [context '=>'] simpletype ['=' constrs] [deriving]
dataDeclParser :: TokParser Decl
dataDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordData
ctypePragma <- optionalHiddenPragma Just
(context, typeHead, inlineKind) <- declHeadPreferringInlineKind typeDeclHeadParser
-- GADT syntax starts with `where`, traditional syntax starts with `=` or nothing
(constructors, derivingClauses) <- gadtDataDeclParser <|> traditionalDataDeclParser
pure $
DeclData
DataDecl
{ dataDeclCTypePragma = ctypePragma,
dataDeclHead = typeHead,
dataDeclContext = fromMaybe [] context,
dataDeclKind = inlineKind,
dataDeclConstructors = constructors,
dataDeclDeriving = derivingClauses
}
-- | Parse a @type data@ declaration.
-- Similar to @data@ but with restrictions:
-- - No datatype context
-- - No labelled fields in constructors
-- - No strictness annotations in constructors
-- - No deriving clause
typeDataDeclParser :: TokParser Decl
typeDataDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordType
expectedTok TkKeywordData
-- type data may not have a datatype context
typeHead <- typeDeclHeadParser
-- Parse optional inline kind signature: @:: Kind@
inlineKind <- MP.optional (expectedTok TkReservedDoubleColon *> typeParser)
-- GADT syntax starts with `where`, traditional syntax starts with `=` or nothing
constructors <- gadtStyleTypeDataDecl <|> traditionalStyleTypeDataDecl
-- type data may not have a deriving clause
pure $
DeclTypeData
DataDecl
{ dataDeclCTypePragma = Nothing,
dataDeclHead = typeHead,
dataDeclContext = [],
dataDeclKind = inlineKind,
dataDeclConstructors = constructors,
dataDeclDeriving = []
}
where
traditionalStyleTypeDataDecl =
fromMaybe [] <$> MP.optional (expectedTok TkReservedEquals *> typeDataConDeclParser `MP.sepBy1` expectedTok TkReservedPipe)
gadtStyleTypeDataDecl = gadtTypeDataWhereClauseParser
-- | Parse constructors for type data (traditional style, after `=`)
-- No labelled fields, no strictness annotations
typeDataConDeclParser :: TokParser DataConDecl
typeDataConDeclParser = withSpan $ do
(_forallVars, context) <- dataConQualifiersParser
MP.try (typeDataConPrefixParser context) <|> typeDataConInfixParser context
typeDataConPrefixParser :: [Type] -> TokParser (SourceSpan -> DataConDecl)
typeDataConPrefixParser context = do
conName <- constructorUnqualifiedNameParser <|> parens constructorOperatorUnqualifiedNameParser
-- Parse arguments (no strictness, no records).
-- Use typeAtomParser to keep adjacent atoms as separate fields instead of
-- merging them into a type application.
args <- MP.many $ BangType [] [] False False <$> typeAtomParser
-- If a constructor operator follows, this declaration is actually infix.
MP.notFollowedBy constructorOperatorParser
pure $ \span' -> DataConAnn (mkAnnotation span') (PrefixCon [] context conName args)
typeDataConInfixParser :: [Type] -> TokParser (SourceSpan -> DataConDecl)
typeDataConInfixParser context = do
lhs <- typeDataConArgParser
op <- constructorOperatorUnqualifiedNameParser <|> backtickConstructorUnqualifiedParser
rhs <- typeDataConArgParser
pure $ \span' -> DataConAnn (mkAnnotation span') (InfixCon [] context lhs op rhs)
where
backtickConstructorUnqualifiedParser = do
expectedTok TkSpecialBacktick
name <- constructorUnqualifiedNameParser
expectedTok TkSpecialBacktick
pure name
typeDataConArgParser :: TokParser BangType
typeDataConArgParser = BangType [] [] False False <$> typeAtomParser
-- | Parse GADT-style constructors for type data (after `where`)
-- No labelled fields, no strictness annotations
gadtTypeDataWhereClauseParser :: TokParser [DataConDecl]
gadtTypeDataWhereClauseParser = whereClauseItemsParser gadtTypeDataConDeclParser
-- | Parse a GADT constructor for type data
-- Only equality constraints permitted, no strictness, no records
gadtTypeDataConDeclParser :: TokParser DataConDecl
gadtTypeDataConDeclParser = withSpan $ do
-- Parse constructor names (can be multiple separated by commas)
names <- gadtConNameParser `MP.sepBy1` expectedTok TkSpecialComma
expectedTok TkReservedDoubleColon
-- Parse optional forall
forallBinders <- MP.many gadtForallParser
-- Parse context (only equality constraints permitted, but we parse generally)
context <- contextPrefixDispatchList
-- Parse the body (prefix only for type data - no record style)
body <- gadtTypeDataBodyParser
pure $ \span' -> DataConAnn (mkAnnotation span') (GadtCon forallBinders context names body)
-- | Parse the body of a GADT constructor for type data
-- Only prefix style allowed (no records), no strictness annotations
gadtTypeDataBodyParser :: TokParser GadtBody
gadtTypeDataBodyParser = do
-- Parse types separated by arrows (may be linear with LinearTypes)
firstTy <- typeAppParser
rest <- MP.many ((,) <$> arrowKindParser <*> typeAppParser)
case rest of
[] -> pure (GadtPrefixBody [] firstTy)
_ ->
let mkBang = BangType [] [] False False
allTys = firstTy : map snd rest
arrowKinds = map fst rest
argTys = init allTys
resultTy = last allTys
argsWithKinds = zip (map mkBang argTys) arrowKinds
in pure (GadtPrefixBody argsWithKinds resultTy)
dataConDeclParser :: TokParser DataConDecl
dataConDeclParser = withSpan $ do
(forallVars, context) <- dataConQualifiersParser
tok <- lookAhead anySingle
case lexTokenKind tok of
-- `(#` is either the LHS arg of an infix constructor (e.g. @(# #) :. Int@) or a
-- standalone unboxed tuple/sum constructor (e.g. @(# Int, Bool #)@).
-- Try infix first to match the original priority; fall back to unboxed.
TkSpecialUnboxedLParen ->
MP.try (dataConInfixParser forallVars context)
<|> unboxedConDeclParser forallVars context
-- `[ ]` is the list-con; any other `[…` must be a type in infix position.
TkSpecialLBracket ->
MP.try (listConDeclParser forallVars context)
<|> dataConInfixParser forallVars context
-- General case: skip unboxed/list (wrong leading token), try infix then boxed-tuple then prefix/record.
_ ->
MP.try (dataConInfixParser forallVars context)
<|> MP.try (boxedTupleConDeclParser forallVars context)
<|> dataConRecordOrPrefixParser forallVars context
listConDeclParser :: [TyVarBinder] -> [Type] -> TokParser (SourceSpan -> DataConDecl)
listConDeclParser forallVars context = do
expectedTok TkSpecialLBracket
expectedTok TkSpecialRBracket
pure $ \span' -> DataConAnn (mkAnnotation span') (ListCon forallVars context)
boxedTupleConDeclParser :: [TyVarBinder] -> [Type] -> TokParser (SourceSpan -> DataConDecl)
boxedTupleConDeclParser forallVars context = do
expectedTok TkSpecialLParen
mClose <- MP.optional (expectedTok TkSpecialRParen)
case mClose of
Just () ->
pure $ \span' -> DataConAnn (mkAnnotation span') (TupleCon forallVars context Boxed [])
Nothing -> do
firstField <- constructorArgParser
-- A comma is mandatory: boxed 1-tuples don't exist in Haskell
-- (e.g. @data C = (Int)@ is invalid). Without this, a
-- parenthesized infix constructor operand like @(?a :: Int) :+ C@
-- would be misinterpreted as a 1-tuple constructor.
expectedTok TkSpecialComma
rest <- constructorArgParser `MP.sepBy1` expectedTok TkSpecialComma
expectedTok TkSpecialRParen
pure $ \span' -> DataConAnn (mkAnnotation span') (TupleCon forallVars context Boxed (firstField : rest))
unboxedConDeclParser :: [TyVarBinder] -> [Type] -> TokParser (SourceSpan -> DataConDecl)
unboxedConDeclParser forallVars context = do
expectedTok TkSpecialUnboxedLParen
mClose <- MP.optional (expectedTok TkSpecialUnboxedRParen)
case mClose of
Just () ->
pure $ \span' -> DataConAnn (mkAnnotation span') (TupleCon forallVars context Unboxed [])
Nothing -> do
leadingPipes <- MP.many (MP.try (expectedTok TkReservedPipe))
if not (null leadingPipes)
then do
field <- constructorArgParser
trailingPipes <- MP.many (expectedTok TkReservedPipe)
expectedTok TkSpecialUnboxedRParen
let pos = length leadingPipes + 1
arity = length leadingPipes + 1 + length trailingPipes
pure $ \span' -> DataConAnn (mkAnnotation span') (UnboxedSumCon forallVars context pos arity field)
else do
firstField <- constructorArgParser
mSep <- MP.optional (MP.try ((Left () <$ expectedTok TkSpecialComma) <|> (Right () <$ expectedTok TkReservedPipe)))
case mSep of
Nothing -> do
expectedTok TkSpecialUnboxedRParen
pure $ \span' -> DataConAnn (mkAnnotation span') (TupleCon forallVars context Unboxed [firstField])
Just (Left ()) -> do
rest <- constructorArgParser `MP.sepBy1` expectedTok TkSpecialComma
expectedTok TkSpecialUnboxedRParen
pure $ \span' -> DataConAnn (mkAnnotation span') (TupleCon forallVars context Unboxed (firstField : rest))
Just (Right ()) -> do
trailingPipes <- MP.many (expectedTok TkReservedPipe)
expectedTok TkSpecialUnboxedRParen
let arity = 1 + 1 + length trailingPipes
pure $ \span' -> DataConAnn (mkAnnotation span') (UnboxedSumCon forallVars context 1 arity firstField)
-- | Report core:
--
-- > topdecl -> 'newtype' [context '=>'] simpletype '=' newconstr [deriving]
newtypeDeclParser :: TokParser Decl
newtypeDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordNewtype
ctypePragma <- optionalHiddenPragma Just
(context, typeHead, inlineKind) <- declHeadPreferringInlineKind typeDeclHeadParser
-- GADT syntax starts with `where`, traditional syntax starts with `=` or nothing
(constructor, derivingClauses) <- gadtStyleNewtypeDecl <|> traditionalStyleNewtypeDecl
pure $
DeclNewtype
NewtypeDecl
{ newtypeDeclCTypePragma = ctypePragma,
newtypeDeclHead = typeHead,
newtypeDeclContext = fromMaybe [] context,
newtypeDeclKind = inlineKind,
newtypeDeclConstructor = constructor,
newtypeDeclDeriving = derivingClauses
}
where
traditionalStyleNewtypeDecl = do
constructor <- MP.optional (expectedTok TkReservedEquals *> dataConDeclParser)
derivingClauses <- MP.many derivingClauseParser
pure (constructor, derivingClauses)
gadtStyleNewtypeDecl = do
constructors <- gadtWhereClauseParser
-- newtype can only have one constructor
case constructors of
[ctor] -> do
derivingClauses <- MP.many derivingClauseParser
pure (Just ctor, derivingClauses)
_ -> fail "newtype must have exactly one constructor"
-- | Parse GADT-style constructors after 'where'
gadtWhereClauseParser :: TokParser [DataConDecl]
gadtWhereClauseParser = whereClauseItemsParser gadtConDeclParser
-- | Parse a GADT constructor declaration: @Con1, Con2 :: forall a. Ctx => Type@
gadtConDeclParser :: TokParser DataConDecl
gadtConDeclParser = withSpan $ do
-- Parse constructor names (can be multiple separated by commas)
names <- gadtConNameParser `MP.sepBy1` expectedTok TkSpecialComma
expectedTok TkReservedDoubleColon
-- Parse optional forall
forallBinders <- MP.many gadtForallParser
-- Parse optional context
context <- contextPrefixDispatchList
-- Parse the body (record or prefix style)
body <- gadtBodyParser
pure $ \span' -> DataConAnn (mkAnnotation span') (GadtCon forallBinders context names body)
-- | Parse constructor name for GADT - can be regular or operator in parens
gadtConNameParser :: TokParser UnqualifiedName
gadtConNameParser =
constructorUnqualifiedNameParser
<|> parens constructorOperatorUnqualifiedNameParser
-- | Parse forall in GADT context: @forall a b.@
gadtForallParser :: TokParser ForallTelescope
gadtForallParser = forallTelescopeParser
-- | Parse the body of a GADT constructor (after :: and optional forall/context)
-- Can be either prefix style: @a -> b -> T a@
-- Or record style: @{ field :: Type } -> T a@
-- Record style is distinguished by the leading @{@ token.
gadtBodyParser :: TokParser GadtBody
gadtBodyParser = gadtRecordBodyParser <|> gadtPrefixBodyParser
-- | Parse record-style GADT body: @{ field :: Type, ... } -> ResultType@
gadtRecordBodyParser :: TokParser GadtBody
gadtRecordBodyParser = do
fields <- recordFieldsParser
expectedTok TkReservedRightArrow
GadtRecordBody fields <$> gadtResultTypeParser
-- | Parse prefix-style GADT body: @!Type1 -> Type2 -> ... -> ResultType@
-- Each argument can have an optional strictness annotation (!).
-- The result type is the final type in a chain of arrows.
gadtPrefixBodyParser :: TokParser GadtBody
gadtPrefixBodyParser = do
firstBang <- gadtBangTypeParser
rest <- MP.many ((,) <$> gadtArrowParser <*> gadtBangTypeParser)
case rest of
[] -> pure (GadtPrefixBody [] (bangType firstBang))
_ ->
let allBangs = firstBang : map snd rest
arrowKinds = map fst rest
args = init allBangs
result = last allBangs
argsWithKinds = zip args arrowKinds
in pure (GadtPrefixBody argsWithKinds (bangType result))
-- | Parse the arrow between GADT constructor arguments.
-- Supports unrestricted @->@ and multiplicity-annotated arrows when LinearTypes is enabled.
gadtArrowParser :: TokParser ArrowKind
gadtArrowParser = arrowKindParser
-- | Parse a potentially strict type for GADT prefix body.
-- Uses 'typeInfixParser' so infix type operators (e.g. @key := v@) are
-- accepted as argument types without requiring parentheses.
gadtBangTypeParser :: TokParser BangType
gadtBangTypeParser = bangTypeParserWith typeInfixParser
-- | Parse the result type of a GADT constructor
-- This is a simple type application like @T a b@
gadtResultTypeParser :: TokParser Type
gadtResultTypeParser = typeParser
declContextParser :: TokParser [Type]
declContextParser = contextParserWith typeParser typeAtomParser
-- | Parse a type/class declaration head, parameterised by the infix operator parser.
-- Handles prefix (@T a b@), infix (@a op b@), parenthesised-infix (@(a op b) c@),
-- and parenthesised-prefix (@(T a b) c@) forms.
--
-- > simpletype -> tycon tyvar_1 ... tyvar_k
declHeadParserWith :: TokParser UnqualifiedName -> TokParser (BinderHead UnqualifiedName)
declHeadParserWith opParser =
MP.try parenthesizedInfixDeclHeadParser
<|> MP.try parenthesizedPrefixDeclHeadParser
<|> MP.try infixDeclHeadParser
<|> prefixDeclHeadParser
where
prefixDeclHeadParser = do
name <- constructorUnqualifiedNameParser <|> parens operatorUnqualifiedNameParser
params <- MP.many declTypeParamParser
pure (PrefixBinderHead name params)
infixDeclHeadParser = do
lhs <- declTypeParamParser
op <- opParser
rhs <- declTypeParamParser
pure (InfixBinderHead lhs op rhs [])
parenthesizedInfixDeclHeadParser = do
expectedTok TkSpecialLParen
lhs <- declTypeParamParser
op <- opParser
rhs <- declTypeParamParser
expectedTok TkSpecialRParen
tailParams <- MP.many declTypeParamParser
pure (InfixBinderHead lhs op rhs tailParams)
parenthesizedPrefixDeclHeadParser = do
expectedTok TkSpecialLParen
name <- constructorUnqualifiedNameParser
params <- MP.some declTypeParamParser
expectedTok TkSpecialRParen
tailParams <- MP.many declTypeParamParser
pure (PrefixBinderHead name (params <> tailParams))
typeDeclHeadParser :: TokParser (BinderHead UnqualifiedName)
typeDeclHeadParser = declHeadParserWith (unqualifiedNameFromText <$> typeSynonymOperatorParser)
typeSynonymOperatorParser :: TokParser Text
typeSynonymOperatorParser =
operatorTextParser <|> backtickTypeSynonymIdentifierParser
where
backtickTypeSynonymIdentifierParser = do
expectedTok TkSpecialBacktick
op <- identifierTextParser
expectedTok TkSpecialBacktick
pure op
typeFamilyHeadParser :: TokParser (TypeHeadForm, Type, [TyVarBinder])
typeFamilyHeadParser = withSpan $ do
binderHead <- declHeadParserWith (nameToUnqualified <$> typeFamilyOperatorParser)
pure (`binderHeadToTypeFamilyHead` binderHead)
-- | Parse an operator for type family declarations.
-- Unlike 'constructorOperatorParser', this accepts both constructor symbols (@:+:@)
-- and variable symbols (@**@), since type families can use either.
typeFamilyOperatorParser :: TokParser Name
typeFamilyOperatorParser =
operatorNameParser <|> backtickTypeFamilyIdentifierParser
where
backtickTypeFamilyIdentifierParser = do
expectedTok TkSpecialBacktick
op <- constructorNameParser
expectedTok TkSpecialBacktick
pure op
typeFamilyLhsParser :: TokParser (TypeHeadForm, Type)
typeFamilyLhsParser = do
lhs <- typeAppParser
hasInfixTail <- MP.optional (lookAhead typeInfixOperatorParser)
case hasInfixTail of
Just _ -> do
rest <- typeHeadInfixTailParser
pure (TypeHeadInfix, foldInfixR buildInfixType lhs rest)
Nothing ->
pure (TypeHeadPrefix, lhs)
where
typeHeadInfixTailParser :: TokParser [((Name, TypePromotion), Type)]
typeHeadInfixTailParser = MP.many $ MP.try $ do
op <- typeInfixOperatorParser
rhs <- typeAppParser
pure (op, rhs)
buildInfixType left ((op, promoted), right) = TInfix left op promoted right
classHeadParser :: TokParser (BinderHead UnqualifiedName)
classHeadParser = declHeadParserWith (nameToUnqualified <$> typeFamilyOperatorParser)
nameToUnqualified :: Name -> UnqualifiedName
nameToUnqualified name = mkUnqualifiedName (nameType name) (nameText name)
binderHeadToTypeFamilyHead :: SourceSpan -> BinderHead UnqualifiedName -> (TypeHeadForm, Type, [TyVarBinder])
binderHeadToTypeFamilyHead span' binderHead =
case binderHead of
PrefixBinderHead name params ->
( TypeHeadPrefix,
typeAnnSpan span' (TCon (qualifyName Nothing name) Unpromoted),
params
)
InfixBinderHead lhs op rhs tailParams ->
( TypeHeadInfix,
typeAnnSpan span' (TInfix lhsType opName Unpromoted rhsType),
[lhs, rhs] <> tailParams
)
where
lhsType = TVar (mkUnqualifiedName NameVarId (tyVarBinderName lhs))
rhsType = TVar (mkUnqualifiedName NameVarId (tyVarBinderName rhs))
opName = qualifyName Nothing op
explicitForallBinderParser :: TokParser TyVarBinder
explicitForallBinderParser =
withSpan $
( do
ident <- typeParamBinderNameParser
pure (\span' -> TyVarBinder [mkAnnotation span'] ident Nothing TyVarBSpecified TyVarBVisible)
)
<|> ( do
expectedTok TkSpecialLParen
ident <- typeParamBinderNameParser
expectedTok TkReservedDoubleColon
kind <- typeParser
expectedTok TkSpecialRParen
pure (\span' -> TyVarBinder [mkAnnotation span'] ident (Just kind) TyVarBSpecified TyVarBVisible)
)
declTypeParamParser :: TokParser TyVarBinder
declTypeParamParser = MP.try invisibleDeclTypeParamParser <|> explicitForallBinderParser
invisibleDeclTypeParamParser :: TokParser TyVarBinder
invisibleDeclTypeParamParser = withSpan $ do
expectedTok TkTypeApp
( do
ident <- lowerIdentifierParser <|> (expectedTok TkKeywordUnderscore $> "_")
pure (\span' -> TyVarBinder [mkAnnotation span'] ident Nothing TyVarBSpecified TyVarBInvisible)
)
<|> do
expectedTok TkSpecialLParen
ident <- lowerIdentifierParser <|> (expectedTok TkKeywordUnderscore $> "_")
expectedTok TkReservedDoubleColon
kind <- typeParser
expectedTok TkSpecialRParen
pure (\span' -> TyVarBinder [mkAnnotation span'] ident (Just kind) TyVarBSpecified TyVarBInvisible)
isTypeVarName :: Text -> Bool
isTypeVarName name =
case T.uncons name of
Just (c, _) -> c == '_' || isLower c
Nothing -> False
typeParamBinderNameParser :: TokParser Text
typeParamBinderNameParser =
tokenSatisfy "type parameter binder" $ \tok ->
case lexTokenKind tok of
TkVarId name
| isTypeVarName name -> Just name
TkKeywordUnderscore -> Just "_"
_ -> Nothing
derivingClauseParser :: TokParser DerivingClause
derivingClauseParser = do
expectedTok TkKeywordDeriving
strategy <- MP.optional derivingStrategyWithoutViaParser
classes <- parenClasses <|> singleClass
viaStrategy <- MP.optional (DerivingVia <$> derivingViaTypeParser)
case (strategy, viaStrategy) of
(Just _, Just _) -> fail "deriving via cannot be combined with another deriving strategy"
_ -> pure (DerivingClause (viaStrategy <|> strategy) classes)
where
singleClass = Left <$> constructorNameParser
parenClasses = Right <$> parens (typeParser `MP.sepEndBy` expectedTok TkSpecialComma)
derivingViaTypeParser :: TokParser Type
derivingViaTypeParser = do
varIdTok "via"
typeParser
derivingStrategyParser :: TokParser DerivingStrategy
derivingStrategyParser =
derivingStrategyWithoutViaParser
<|> (DerivingVia <$> MP.try derivingViaTypeParser)
derivingStrategyWithoutViaParser :: TokParser DerivingStrategy
derivingStrategyWithoutViaParser =
(varIdTok "stock" >> pure DerivingStock)
<|> (expectedTok TkKeywordNewtype >> pure DerivingNewtype)
<|> (varIdTok "anyclass" >> pure DerivingAnyclass)
dataConQualifiersParser :: TokParser ([TyVarBinder], [Type])
dataConQualifiersParser = do
foralls <- MP.option [] forallBindersParser
context <- contextPrefixDispatchList
pure (foralls, context)
data FamilyKeywordMode
= FamilyKeywordRequired
| FamilyKeywordOptional
typeFamilyDeclBodyParser :: FamilyKeywordMode -> TokParser TypeFamilyDecl
typeFamilyDeclBodyParser familyKeywordMode = do
expectedTok TkKeywordType
explicitFamilyKeyword <- case familyKeywordMode of
FamilyKeywordRequired -> expectedTok TkVarFamily $> True
FamilyKeywordOptional -> isJust <$> MP.optional (expectedTok TkVarFamily)
(headForm, headType, params) <- typeFamilyHeadParser
resultSig <- typeFamilyResultSigParser explicitFamilyKeyword
equations <-
case familyKeywordMode of
FamilyKeywordRequired -> MP.optional (MP.try closedTypeFamilyWhereParser)
FamilyKeywordOptional -> pure Nothing
pure
TypeFamilyDecl
{ typeFamilyDeclHeadForm = headForm,
typeFamilyDeclExplicitFamilyKeyword = explicitFamilyKeyword,
typeFamilyDeclHead = headType,
typeFamilyDeclParams = params,
typeFamilyDeclResultSig = resultSig,
typeFamilyDeclEquations = equations
}
forallBindersParser :: TokParser [TyVarBinder]
forallBindersParser = do
expectedTok TkKeywordForall
binders <- MP.some explicitForallBinderParser
expectedTok (TkVarSym ".")
pure binders
dataConRecordOrPrefixParser :: [TyVarBinder] -> [Type] -> TokParser (SourceSpan -> DataConDecl)
dataConRecordOrPrefixParser forallVars context = do
name <- constructorUnqualifiedNameParser <|> parens operatorUnqualifiedNameParser
mRecordFields <- MP.optional (MP.try recordFieldsParserAfterLayoutSemicolon)
case mRecordFields of
Just fields -> pure (\span' -> DataConAnn (mkAnnotation span') (RecordCon forallVars context name fields))
Nothing -> do
args <- MP.many constructorArgParser
-- Ensure we're not leaving a constructor operator unconsumed.
-- If there's a constructor operator next, this is actually an infix form
-- and we should backtrack to let dataConInfixParser handle it.
MP.notFollowedBy constructorOperatorParser
pure (\span' -> DataConAnn (mkAnnotation span') (PrefixCon forallVars context name args))
where
-- Layout may inject a virtual ';' before a newline-started record field block.
-- Accept it as part of the constructor declaration.
recordFieldsParserAfterLayoutSemicolon =
recordFieldsParser
<|> (expectedTok TkSpecialSemicolon *> recordFieldsParser)
dataConInfixParser :: [TyVarBinder] -> [Type] -> TokParser (SourceSpan -> DataConDecl)
dataConInfixParser forallVars context = do
lhs <- infixConstructorArgParser
op <- constructorOperatorUnqualifiedNameParser <|> backtickConstructorUnqualifiedParser
rhs <- infixConstructorArgParser
pure (\span' -> DataConAnn (mkAnnotation span') (InfixCon forallVars context lhs op rhs))
where
backtickConstructorUnqualifiedParser = do
expectedTok TkSpecialBacktick
name <- constructorUnqualifiedNameParser
expectedTok TkSpecialBacktick
pure name
recordFieldsParser :: TokParser [FieldDecl]
recordFieldsParser = braces (recordFieldDeclParser `MP.sepEndBy` expectedTok TkSpecialComma)
recordFieldDeclParser :: TokParser FieldDecl
recordFieldDeclParser = withSpan $ do
names <- binderNameParser `MP.sepBy1` expectedTok TkSpecialComma
linearEnabled <- isExtensionEnabled LinearTypes
mMult <- if linearEnabled then MP.optional fieldMultiplicityParser else pure Nothing
expectedTok TkReservedDoubleColon
fieldTy <- recordFieldBangTypeParser
pure $ \span' ->
FieldDecl
{ fieldAnns = [mkAnnotation span'],
fieldNames = names,
fieldMultiplicity = mMult,
fieldType = fieldTy
}
fieldMultiplicityParser :: TokParser Type
fieldMultiplicityParser = do
expectedTok TkPrefixPercent
typeAtomParser
constructorArgParser :: TokParser BangType
constructorArgParser = MP.try $ MP.notFollowedBy derivingKeywordParser *> bangTypeParser
infixConstructorArgParser :: TokParser BangType
infixConstructorArgParser = MP.try $ MP.notFollowedBy derivingKeywordParser *> bangTypeParserWith typeAppParser
derivingKeywordParser :: TokParser ()
derivingKeywordParser =
tokenSatisfy "identifier \"deriving\"" $ \tok ->
case lexTokenKind tok of
TkKeywordDeriving -> Just ()
_ -> Nothing
bangTypeParserWith :: TokParser Type -> TokParser BangType
bangTypeParserWith typeP = withSpan $ do
pragmas <- MP.option [] (fmap (: []) unpackPragmaParser)
strict <- MP.option False (expectedTok TkPrefixBang >> pure True)
lazy <- MP.option False (expectedTok TkPrefixTilde >> pure True)
ty <- typeP
pure $ \span' ->
BangType
{ bangAnns = [mkAnnotation span'],
bangPragmas = pragmas,
bangStrict = strict,
bangLazy = lazy,
bangType = ty
}
bangTypeParser :: TokParser BangType
bangTypeParser = bangTypeParserWith typeAtomParser
recordFieldBangTypeParser :: TokParser BangType
recordFieldBangTypeParser = bangTypeParserWith typeParser
unpackPragmaParser :: TokParser Pragma
unpackPragmaParser =
hiddenPragma "source unpack pragma" $ \p -> case pragmaType p of
PragmaUnpack _ -> Just p
_ -> Nothing
constructorOperatorParser :: TokParser Name
constructorOperatorParser =
symbolicConstructorOperatorParser <|> backtickConstructorIdentifierParser
where
symbolicConstructorOperatorParser =
tokenSatisfy "constructor operator" $ \tok ->
case lexTokenKind tok of
TkConSym op -> Just (qualifyName Nothing (mkUnqualifiedName NameConSym op))
TkQConSym modName op -> Just (mkName (Just modName) NameConSym op)
TkReservedColon -> Just (qualifyName Nothing (mkUnqualifiedName NameConSym ":"))
_ -> Nothing
backtickConstructorIdentifierParser = do
expectedTok TkSpecialBacktick
op <- constructorNameParser
expectedTok TkSpecialBacktick
pure op
-- | Parse a pattern binding declaration like @(x, y) = (1, 2)@.
-- This handles bindings where the LHS is a pattern rather than a function name.
patternBindDeclParser :: TokParser Decl
patternBindDeclParser = MP.try $ withSpanAnn (DeclAnn . mkAnnotation) $ do
pat <- region "while parsing pattern binding" patternParser
DeclValue . PatternBind NoMultiplicityTag pat <$> equationRhsParser
valueDeclParser :: TokParser Decl
valueDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
(headForm, name, pats) <- functionHeadParserWith patParser apatParser
functionBindDecl headForm name pats <$> equationRhsParser
-- ---------------------------------------------------------------------------
-- Pattern synonyms
-- | Parse a pattern synonym declaration or signature.
-- Dispatches between @pattern Name :: Type@ (signature) and
-- @pattern Name args = pat@ / @pattern Name args <- pat [where ...]@ (declaration).
-- Uses a forward scan for @name(s) ::@ to avoid backtracking over a large parse.
patternSynonymParser :: TokParser Decl
patternSynonymParser = MP.try patternSynonymSigDeclParser <|> patternSynonymDeclParser
-- | Parse a pattern synonym type signature: @pattern Name1, Name2 :: Type@
patternSynonymSigDeclParser :: TokParser Decl
patternSynonymSigDeclParser = do
expectedTok TkKeywordPattern
names <- patSynNameParser `MP.sepBy1` expectedTok TkSpecialComma
expectedTok TkReservedDoubleColon
DeclPatSynSig names <$> typeSignatureParser
patSynNameParser :: TokParser UnqualifiedName
patSynNameParser =
constructorUnqualifiedNameParser
<|> do
op <- parens constructorOperatorParser
pure (mkUnqualifiedName (nameType op) (nameText op))
-- | Parse a pattern synonym declaration.
-- Handles prefix, infix, and record forms with all three directionalities.
patternSynonymDeclParser :: TokParser Decl
patternSynonymDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ do
expectedTok TkKeywordPattern
(name, args) <- patSynLhsParser
(dir, pat) <- patSynDirAndPatParser name
pure $
DeclPatSyn
PatSynDecl
{ patSynDeclName = name,
patSynDeclArgs = args,
patSynDeclPat = pat,
patSynDeclDir = dir
}
-- | Parse the LHS of a pattern synonym declaration.
-- Returns the name and the argument form.
patSynLhsParser :: TokParser (UnqualifiedName, PatSynArgs)
patSynLhsParser =
MP.try patSynInfixLhsParser <|> patSynRecordOrPrefixLhsParser
-- | Parse an infix pattern synonym LHS: @var ConOp var@ or @var \`Con\` var@
patSynInfixLhsParser :: TokParser (UnqualifiedName, PatSynArgs)
patSynInfixLhsParser = do
lhs <- lowerIdentifierParser
op <- constructorOperatorParser
rhs <- lowerIdentifierParser
pure (mkUnqualifiedName (nameType op) (nameText op), PatSynInfixArgs lhs rhs)
-- | Parse a record or prefix pattern synonym LHS.
-- Record: @Con {field1, field2, ...}@
-- Prefix: @Con var1 var2 ...@
patSynRecordOrPrefixLhsParser :: TokParser (UnqualifiedName, PatSynArgs)
patSynRecordOrPrefixLhsParser = do
name <- patSynNameParser
mFields <- MP.optional (MP.try patSynRecordFieldsParser)
case mFields of
Just fields -> pure (name, PatSynRecordArgs fields)
Nothing -> do
args <- MP.many lowerIdentifierParser
pure (name, PatSynPrefixArgs args)
-- | Parse the record fields of a pattern synonym: @{field1, field2, ...}@
patSynRecordFieldsParser :: TokParser [Text]
patSynRecordFieldsParser = braces (lowerIdentifierParser `MP.sepEndBy` expectedTok TkSpecialComma)
-- | Parse the direction marker and RHS pattern of a pattern synonym.
patSynDirAndPatParser :: UnqualifiedName -> TokParser (PatSynDir, Pattern)
patSynDirAndPatParser name =
( do
expectedTok TkReservedEquals
pat <- patternParser
pure (PatSynBidirectional, pat)
)
<|> ( do
expectedTok TkReservedLeftArrow
pat <- patternParser
mMatches <- MP.optional (patSynWhereClauseParser (renderUnqualifiedName name))
case mMatches of
Nothing -> pure (PatSynUnidirectional, pat)
Just matches -> pure (PatSynExplicitBidirectional matches, pat)
)
<|> do
mTok <- MP.optional (lookAhead anySingle)
MP.customFailure
UnexpectedTokenExpecting
{ unexpectedFound = mkFoundToken <$> mTok,
unexpectedExpecting = "'=' or '<-' in pattern synonym declaration",
unexpectedContext = []
}
-- | Parse the where clause of an explicitly bidirectional pattern synonym.
-- @where { Name pats = expr; ... }@
patSynWhereClauseParser :: Text -> TokParser [Match]
patSynWhereClauseParser _name = whereClauseItemsParser patSynWhereMatch
-- | Parse one equation in a pattern synonym where clause.
-- Uses 'lpatParser' (not 'patternParser') for the infix head patterns
-- because 'patternParser' would greedily consume the constructor operator
-- that serves as the function head — both 'patParser' and the infix
-- head parser compete for the same constructor operators.
patSynWhereMatch :: TokParser Match
patSynWhereMatch = withSpan $ do
(headForm, _name, pats) <- patSynWhereHeadParser
rhs <- equationRhsParser
pure $ \span' ->
Match
{ matchAnns = [mkAnnotation span'],
matchHeadForm = headForm,
matchPats = pats,
matchRhs = rhs
}
patSynWhereHeadParser :: TokParser (MatchHeadForm, UnqualifiedName, [Pattern])
patSynWhereHeadParser =
MP.try infixHeadParser
<|> MP.try parenthesizedInfixHeadParser
<|> prefixHeadParser
where
prefixHeadParser = do
name <- patSynNameParser
pats <- MP.many apatParser
pure (MatchHeadPrefix, name, pats)
infixHeadParser = do
lhsPat <- lpatParser
op <- constructorInfixOperatorNameParser
rhsPat <- lpatParser
pure (MatchHeadInfix, op, [lhsPat, rhsPat])
-- Prefer the plain infix form above so a parenthesized infix sub-pattern on
-- the left-hand side, e.g. @(a :<| b) :> c = ...@, is not mistaken for the
-- entire function head.
parenthesizedInfixHeadParser = do
expectedTok TkSpecialLParen
lhsPat <- lpatParser
op <- constructorInfixOperatorNameParser
rhsPat <- lpatParser
expectedTok TkSpecialRParen
tailPats <- MP.many apatParser
pure (MatchHeadInfix, op, [lhsPat, rhsPat] <> tailPats)