Agda-2.8.0.2: src/full/Agda/Syntax/Parser/Helpers.hs
-- | Utility functions used in the Happy parser.
module Agda.Syntax.Parser.Helpers where
import Prelude hiding (null)
import Control.Applicative ( (<|>) )
import Control.Monad.State ( modify' )
import Data.Bifunctor (first, second)
import Data.Char
import qualified Data.List as List
import Data.Maybe
import Data.Semigroup ((<>), sconcat)
import Data.Text (Text)
import qualified Data.Text as T
import Agda.Syntax.Position
import Agda.Syntax.Parser.Monad
import Agda.Syntax.Parser.Lexer
import Agda.Syntax.Parser.Tokens
import Agda.Syntax.Concrete as C
import Agda.Syntax.Concrete.Attribute as CA
import Agda.Syntax.Concrete.Pattern
import Agda.Syntax.Common
import Agda.Syntax.Notation
import Agda.Syntax.Literal
import Agda.TypeChecking.Positivity.Occurrence
import Agda.Utils.Either
import Agda.Utils.Functor
import Agda.Utils.Hash
import Agda.Utils.List ( spanJust, chopWhen, initLast )
import Agda.Utils.List1 ( List1, pattern (:|), (<|) )
import Agda.Utils.Monad
import Agda.Utils.Null
import Agda.Syntax.Common.Pretty hiding ((<>))
import Agda.Utils.Singleton
import qualified Agda.Utils.Maybe.Strict as Strict
import qualified Agda.Utils.List1 as List1
import qualified Agda.Utils.List2 as List2
import Agda.Utils.Impossible
-- | Grab leading OPTIONS pragmas.
takeOptionsPragmas :: [Declaration] -> Module
takeOptionsPragmas = uncurry Mod . spanJust (\ d -> case d of
Pragma p@OptionsPragma{} -> Just p
_ -> Nothing)
-- | Insert a top-level module if there is none.
-- Also fix-up for the case the declarations in the top-level module
-- are not indented (this is allowed as a special case).
figureOutTopLevelModule :: [Declaration] -> [Declaration]
figureOutTopLevelModule ds =
case spanAllowedBeforeModule ds of
-- Andreas 2016-02-01, issue #1388.
-- We need to distinguish two additional cases.
-- Case 1: Regular file layout: imports followed by one module. Nothing to do.
(ds0, [ Module{} ]) -> ds
-- Case 2: The declarations in the module are not indented.
-- This is allowed for the top level module, and thus rectified here.
(ds0, Module r erased m tel [] : ds2) ->
ds0 ++ [Module r erased m tel ds2]
-- Case 3: There is a module with indented declarations,
-- followed by non-indented declarations. This should be a
-- parse error and be reported later (see @toAbstract TopLevel{}@),
-- thus, we do not do anything here.
(ds0, Module r _ m tel ds1 : ds2) -> ds -- Gives parse error in scope checker.
-- OLD code causing issue 1388:
-- (ds0, Module r m tel ds1 : ds2) -> ds0 ++ [Module r m tel $ ds1 ++ ds2]
-- Case 4: a top-level module declaration is missing.
-- Andreas, 2017-01-01, issue #2229:
-- Put everything (except OPTIONS pragmas) into an anonymous module.
_ -> ds0 ++ [Module r defaultErased (QName $ noName r) [] ds1]
where
(ds0, ds1) = (`span` ds) $ \case
Pragma OptionsPragma{} -> True
_ -> False
-- Andreas, 2017-05-17, issue #2574.
-- Since the module noName will act as jump target, it needs a range.
-- We use the beginning of the file as beginning of the top level module.
r = beginningOfFile $ getRange ds1
-- | Create a name from a string. The boolean indicates whether a part
-- of the name can be token 'constructor'.
mkName' :: Bool -> (Interval, String) -> Parser Name
mkName' constructor (i, s) =
either parseError return $ mkValidName constructor (getRange i) s
-- | Create a name from a string. The boolean indicates whether a part
-- of the name can be token 'constructor'.
mkValidName :: Bool -> Range -> String -> Either String Name
mkValidName constructor' r s = do
let
xs = C.stringNameParts s
-- The keyword constructor can appear as the only NamePart in the
-- last segment of a qualified name --- Foo.constructor refers to
-- the constructor of the record Foo.
constructor = case xs of
_ :| [] -> constructor'
_ -> False
-- The constructor' argument to mkName' determines whether this
-- is the last segment of a QName, the local variable constructor
-- additionally takes whether it's the only NamePart into
-- consideration.
mapM_ (isValidId constructor) xs
unless (alternating xs) $ parseError $ "a name cannot contain two consecutive underscores"
return $ Name r InScope xs
where
parseError = Left
isValidId _ Hole = return ()
isValidId con (Id y) = do
let x = rawNameToString y
err = "in the name " ++ s ++ ", the part " ++ x ++ " is not valid"
case parse defaultParseFlags [0] (lexer return) x of
ParseOk _ TokId{} -> return ()
ParseFailed{} -> parseError err
ParseOk _ TokEOF{} -> parseError err
ParseOk _ (TokKeyword KwConstructor _) | con -> pure ()
ParseOk _ t -> parseError . ((err ++ " because it is ") ++) $ case t of
TokQId{} -> "qualified"
TokKeyword{} -> "a keyword"
TokLiteral{} -> "a literal"
TokSymbol s _ -> case s of
SymDot -> __IMPOSSIBLE__ -- "reserved"
SymSemi -> "used to separate declarations"
SymVirtualSemi -> __IMPOSSIBLE__
SymBar -> "used for with-arguments"
SymColon -> "part of declaration syntax"
SymArrow -> "the function arrow"
SymEqual -> "part of declaration syntax"
SymLambda -> "used for lambda-abstraction"
SymUnderscore -> "used for anonymous identifiers"
SymQuestionMark -> "a meta variable"
SymAs -> "used for as-patterns"
SymOpenParen -> "used to parenthesize expressions"
SymCloseParen -> "used to parenthesize expressions"
SymOpenIdiomBracket -> "an idiom bracket"
SymCloseIdiomBracket -> "an idiom bracket"
SymEmptyIdiomBracket -> "an empty idiom bracket"
SymDoubleOpenBrace -> "used for instance arguments"
SymDoubleCloseBrace -> "used for instance arguments"
SymOpenBrace -> "used for hidden arguments"
SymCloseBrace -> "used for hidden arguments"
SymOpenVirtualBrace -> __IMPOSSIBLE__
SymCloseVirtualBrace -> __IMPOSSIBLE__
SymOpenPragma -> "used for pragmas"
SymClosePragma -> "used for pragmas"
SymEllipsis -> "used for function clauses"
SymDotDot -> "a modality"
SymEndComment -> "the end-of-comment brace"
TokString{} -> __IMPOSSIBLE__
TokTeX{} -> __IMPOSSIBLE__ -- used by the LaTeX backend only
TokMarkup{} -> __IMPOSSIBLE__ -- ditto
TokComment{} -> __IMPOSSIBLE__
TokDummy{} -> __IMPOSSIBLE__
-- we know that there are no two Ids in a row
alternating (Hole :| Hole : _) = False
alternating (_ :| x : xs) = alternating $ x :| xs
alternating (_ :| []) = True
-- | Create a name from a string
mkName :: (Interval, String) -> Parser Name
mkName = mkName' False
-- | Create a qualified name from a list of strings
mkQName :: [(Interval, String)] -> Parser QName
mkQName ss | Just (ss0, ss1) <- initLast ss = do
xs0 <- mapM mkName ss0
xs1 <- mkName' True ss1
return $ foldr Qual (QName xs1) xs0
mkQName _ = __IMPOSSIBLE__ -- The lexer never gives us an empty list of parts
mkDomainFree_ :: (NamedArg Binder -> NamedArg Binder) -> Maybe Pattern -> Name -> NamedArg Binder
mkDomainFree_ f p n = f $ defaultNamedArg $ Binder p UserBinderName $ mkBoundName_ n
mkRString :: (Interval, String) -> RString
mkRString (i, s) = Ranged (getRange i) s
mkRText :: (Interval, String) -> Ranged Text
mkRText (i, s) = Ranged (getRange i) $ T.pack s
-- | Create a qualified name from a string (used in pragmas).
-- Range of each name component is range of whole string.
-- TODO: precise ranges!
pragmaQName :: (Interval, String) -> Parser QName
pragmaQName (r, s) = do
let ss = chopWhen (== '.') s
mkQName $ map (r,) ss
mkNamedArg :: Maybe QName -> Either QName Range -> Parser (NamedArg BoundName)
mkNamedArg x y = do
lbl <- case x of
Nothing -> return $ Just $ WithOrigin UserWritten $ unranged "_"
Just (QName x) -> return $ Just $ WithOrigin UserWritten $ Ranged (getRange x) $ prettyShow x
_ -> parseError "expected unqualified variable name"
var <- case y of
Left (QName y) -> return $ mkBoundName y noFixity'
Right r -> return $ mkBoundName (noName r) noFixity'
_ -> parseError "expected unqualified variable name"
return $ defaultArg $ Named lbl var
-- | Polarity parser.
--
--- Unknown polarities are replaced with the default polarity.
parsePolarity :: (Interval, String) -> Parser (Ranged Occurrence)
parsePolarity (i, s) =
case s of
"_" -> ret Unused
"++" -> ret StrictPos
"+" -> ret JustPos
"-" -> ret JustNeg
"*" -> ret Mixed
_ -> do
parseWarning (UnknownPolarity r s)
ret Mixed
where
r = getRange i
ret = return . Ranged r
recoverLayout :: [(Interval, String)] -> String
recoverLayout [] = ""
recoverLayout xs@((i, _) : _) = go (iStart i) xs
where
c0 = posCol (iStart i)
go cur [] = ""
go cur ((i, s) : xs) = padding cur (iStart i) ++ s ++ go (iEnd i) xs
padding Pn{ posLine = l1, posCol = c1 } Pn{ posLine = l2, posCol = c2 }
| l1 < l2 = List.genericReplicate (l2 - l1) '\n' ++ List.genericReplicate (max 0 (c2 - c0)) ' '
| l1 == l2 = List.genericReplicate (c2 - c1) ' '
| otherwise = __IMPOSSIBLE__
ensureUnqual :: QName -> Parser Name
ensureUnqual (QName x) = return x
ensureUnqual q@Qual{} = parseError' (rStart' $ getRange q) "Qualified name not allowed here"
------------------------------------------------------------------------
-- Lambinds
-- | Result of parsing @LamBinds@.
data LamBinds' a = LamBinds
{ lamBindings :: a -- ^ A number of domain-free or typed bindings or record patterns.
, absurdBinding :: Maybe Hiding -- ^ Followed by possibly a final absurd pattern.
} deriving (Functor)
type LamBinds = LamBinds' [LamBinding]
mkAbsurdBinding :: Hiding -> LamBinds
mkAbsurdBinding = LamBinds [] . Just
mkLamBinds :: a -> LamBinds' a
mkLamBinds bs = LamBinds bs Nothing
-- | Build a forall pi (forall x y z -> ...)
forallPi :: List1 LamBinding -> Expr -> Expr
forallPi bs e = Pi (fmap addType bs) e
-- | Converts lambda bindings to typed bindings.
addType :: LamBinding -> TypedBinding
addType (DomainFull b) = b
addType (DomainFree x) = TBind r (singleton x) $ Underscore r Nothing
where r = getRange x
-- | Returns the value of the first erasure attribute, if any, or else
-- the default value of type 'Erased'.
--
-- Raises warnings for all attributes except for erasure attributes,
-- and for multiple erasure attributes.
onlyErased
:: [Attr] -- ^ The attributes, in reverse order.
-> Parser Erased
onlyErased as = do
es <- catMaybes <$> mapM onlyErased' (reverse as)
case es of
[] -> return defaultErased
[e] -> return e
e : es -> do
parseWarning $ MultipleAttributes (getRange es) (Just "erasure")
return e
where
onlyErased' a = case theAttr a of
RelevanceAttribute{} -> unsup "Relevance"
CohesionAttribute{} -> unsup "Cohesion"
LockAttribute{} -> unsup "Lock"
CA.TacticAttribute{} -> unsup "Tactic"
PolarityAttribute{} -> unsup "Polarity"
QuantityAttribute q -> maybe (unsup "Linearity") (return . Just) $ erasedFromQuantity q
where
unsup s = do
parseWarning $ UnsupportedAttribute (attrRange a) (Just s)
return Nothing
-- | Constructs extended lambdas.
extLam
:: Range -- ^ The range of the lambda symbol and @where@ or
-- the braces.
-> [Attr] -- ^ The attributes in reverse order.
-> List1 LamClause -- ^ The clauses in reverse order.
-> Parser Expr
extLam symbolRange attrs cs = do
e <- onlyErased attrs
let cs' = List1.reverse cs
return $ ExtendedLam (getRange (symbolRange, e, cs')) e cs'
-- | Constructs extended or absurd lambdas.
extOrAbsLam
:: Range -- ^ The range of the lambda symbol.
-> [Attr] -- ^ The attributes, in reverse order.
-> Either ([LamBinding], Hiding) (List1 Expr)
-> Parser Expr
extOrAbsLam lambdaRange attrs cs = case cs of
Right es -> do
-- It is of the form @\ { p1 ... () }@.
e <- onlyErased attrs
cl <- mkAbsurdLamClause empty es
return $ ExtendedLam (getRange (lambdaRange, e, es)) e $ singleton cl
Left (bs, h) -> do
mapM_ (\a -> parseWarning $
UnsupportedAttribute (attrRange a) Nothing)
(reverse attrs)
List1.ifNull bs
{-then-} (return $ AbsurdLam r h)
{-else-} $ \ bs -> return $ Lam r bs (AbsurdLam r h)
where
r = fuseRange lambdaRange bs
-- | Interpret an expression as a list of names and (not parsed yet) as-patterns
exprAsNamesAndPatterns :: Expr -> Maybe (List1 (Name, Maybe Expr))
exprAsNamesAndPatterns = mapM exprAsNameAndPattern . exprAsTele
where
exprAsTele :: Expr -> List1 Expr
exprAsTele (RawApp _ es) = List2.toList1 es
exprAsTele e = singleton e
exprAsNameAndPattern :: Expr -> Maybe (Name, Maybe Expr)
exprAsNameAndPattern (Ident (QName x)) = Just (x, Nothing)
exprAsNameAndPattern (Underscore r _) = Just (setRange r simpleHole, Nothing)
exprAsNameAndPattern (As _ n e) = Just (n, Just e)
exprAsNameAndPattern (Paren r e) = Just (setRange r simpleHole, Just e)
exprAsNameAndPattern _ = Nothing
-- interpret an expression as name or list of hidden / instance names
exprAsNameOrHiddenNames :: Expr -> Maybe (List1 (NamedArg (Name, Maybe Expr)))
exprAsNameOrHiddenNames = \case
HiddenArg _ (Named Nothing e) ->
fmap (hide . defaultNamedArg) <$> exprAsNamesAndPatterns e
InstanceArg _ (Named Nothing e) ->
fmap (makeInstance . defaultNamedArg) <$> exprAsNamesAndPatterns e
e ->
singleton . defaultNamedArg <$> exprAsNameAndPattern e
boundNamesOrAbsurd :: List1 Expr -> Parser (Either (List1 (NamedArg Binder)) (List1 Expr))
boundNamesOrAbsurd es
| any isAbsurd es = return $ Right es
| otherwise =
case mapM exprAsNameAndPattern es of
Nothing -> parseError $ "expected sequence of bound identifiers"
Just good -> fmap Left $ forM good $ \ (n, me) -> do
p <- traverse exprToPattern me
return (defaultNamedArg (Binder p UserBinderName (mkBoundName_ n)))
where
isAbsurd :: Expr -> Bool
isAbsurd (Absurd _) = True
isAbsurd (HiddenArg _ (Named _ e)) = isAbsurd e
isAbsurd (InstanceArg _ (Named _ e)) = isAbsurd e
isAbsurd (Paren _ e) = isAbsurd e
isAbsurd (As _ _ e) = isAbsurd e
isAbsurd (RawApp _ es) = any isAbsurd es
isAbsurd _ = False
-- | Match a pattern-matching "assignment" statement @p <- e@
exprToAssignment :: Expr -> Parser (Maybe (Pattern, Range, Expr))
exprToAssignment e@(RawApp r es)
| (es1, arr : es2) <- List2.break isLeftArrow es =
case filter isLeftArrow es2 of
arr : _ -> parseError' (rStart' $ getRange arr) $ "Unexpected " ++ prettyShow arr
[] ->
-- Andreas, 2021-05-06, issue #5365
-- Handle pathological cases like @do <-@ and @do x <-@.
case (es1, es2) of
(e1:rest1, e2:rest2) -> do
p <- exprToPattern $ rawApp $ e1 :| rest1
pure $ Just (p, getRange arr, rawApp (e2 :| rest2))
_ -> parseError' (rStart' $ getRange e) $ "Incomplete binding " ++ prettyShow e
where
isLeftArrow (Ident (QName (Name _ _ (Id arr :| [])))) =
arr `elem` ["<-", "\x2190"] -- \leftarrow [issue #5465, unicode might crash happy]
isLeftArrow _ = False
exprToAssignment _ = pure Nothing
-- | Build a with-block
buildWithBlock ::
[Either RewriteEqn (List1 (Named Name Expr))] ->
Parser ([RewriteEqn], [Named Name Expr])
buildWithBlock rees = case groupByEither rees of
(Left rs : rest) -> (List1.toList rs,) <$> finalWith rest
rest -> ([],) <$> finalWith rest
where
finalWith :: (HasRange a, HasRange b) =>
[Either (List1 a) (List1 (List1 b))] -> Parser [b]
finalWith [] = pure $ []
finalWith [Right ees] = pure $ List1.toList $ sconcat ees
finalWith (Right{} : tl) = parseError' (rStart' $ getRange tl)
"Cannot use rewrite / pattern-matching with after a with-abstraction."
finalWith (Left{} : _) = __IMPOSSIBLE__
-- | Build a with-statement
buildWithStmt :: List1 (Named Name Expr) ->
Parser [Either RewriteEqn (List1 (Named Name Expr))]
buildWithStmt nes = do
ws <- mapM buildSingleWithStmt (List1.toList nes)
let rws = groupByEither ws
pure $ map (first (Invert ())) rws
buildUsingStmt :: List1 Expr -> Parser RewriteEqn
buildUsingStmt es = do
mpatexprs <- mapM exprToAssignment es
case mapM (fmap $ \(pat, _, expr) -> (pat, expr)) mpatexprs of
Nothing -> parseError' (rStart' $ getRange es) "Expected assignments"
Just assignments -> pure $ LeftLet assignments
buildSingleWithStmt ::
Named Name Expr ->
Parser (Either (Named Name (Pattern, Expr)) (Named Name Expr))
buildSingleWithStmt e = do
mpatexpr <- exprToAssignment (namedThing e)
pure $ case mpatexpr of
Just (pat, _, expr) -> Left ((pat, expr) <$ e)
Nothing -> Right e
-- | Build a do-statement
defaultBuildDoStmt :: Expr -> [LamClause] -> Parser DoStmt
defaultBuildDoStmt e (_ : _) = parseError' (rStart' $ getRange e) "Only pattern matching do-statements can have where clauses."
defaultBuildDoStmt e [] = pure $ DoThen e
buildDoStmt :: Expr -> [LamClause] -> Parser DoStmt
buildDoStmt (Let r ds Nothing) [] = return $ DoLet r ds
buildDoStmt e@(RawApp r _) cs = do
mpatexpr <- exprToAssignment e
case mpatexpr of
Just (pat, r, expr) -> pure $ DoBind r pat expr cs
Nothing -> defaultBuildDoStmt e cs
buildDoStmt e cs = defaultBuildDoStmt e cs
{--------------------------------------------------------------------------
Patterns
--------------------------------------------------------------------------}
-- | Turn an expression into a left hand side.
exprToLHS :: Expr -> Parser ([RewriteEqn] -> [WithExpr] -> LHS)
exprToLHS e = LHS <$> exprToPattern e
-- | Turn an expression into a pattern. Fails if the expression is not a
-- valid pattern.
exprToPattern :: Expr -> Parser Pattern
exprToPattern e = case C.isPattern e of
Nothing -> parseErrorRange e $ "Not a valid pattern: " ++ prettyShow e
Just p -> pure p
-- | Turn an expression into a name. Fails if the expression is not a
-- valid identifier.
exprToName :: Expr -> Parser Name
exprToName (Ident (QName x)) = return x
exprToName e = parseErrorRange e $ "Not a valid identifier: " ++ prettyShow e
-- | When given expression is @e1 = e2@, turn it into a named expression.
-- Call this inside an implicit argument @{e}@ or @{{e}}@, where
-- an equality must be a named argument (rather than a cubical partial match).
maybeNamed :: Expr -> Parser (Named_ Expr)
maybeNamed e =
case e of
Equal _ e1 e2 -> do
let succeed x = return $ named (WithOrigin UserWritten $ Ranged (getRange e1) x) e2
case e1 of
Ident (QName x) -> succeed $ nameToRawName x
-- We could have the following, but names of arguments cannot be _.
-- Underscore{} -> succeed $ "_"
_ -> parseErrorRange e $ "Not a valid named argument: " ++ prettyShow e
_ -> return $ unnamed e
-- Andreas, 2024-02-20, issue #7136:
-- The following function has been rewritten to a defensive pattern matching style
-- to be robust against future parser changes.
patternSynArgs :: [NamedArg Binder] -> Parser [WithHiding Name]
patternSynArgs = mapM \ x -> do
let
abort s = parseError $
"Illegal pattern synonym argument " ++ prettyShow x ++ "\n" ++
"(" ++ s ++ ".)"
noAnn s = s ++ " annotations not allowed in pattern synonym arguments"
case x of
-- Invariant: fixity is not used here, and neither finiteness
Arg ai (Named mn (Binder mp _ (BName n fix mtac fin)))
| not $ null fix -> __IMPOSSIBLE__
| fin -> __IMPOSSIBLE__
-- Error cases:
Arg _ (Named _ (Binder (Just _) _ _)) ->
abort "Arguments to pattern synonyms cannot be patterns themselves"
Arg _ (Named _ (Binder _ _ (BName _ _ tac _))) | not (null tac) ->
abort $ noAnn "Tactic"
-- Benign case:
Arg ai (Named mn (Binder Nothing _ (BName n _ _ _)))
-- allow {n = n} for backwards compat with Agda 2.6
| maybe True ((C.nameToRawName n ==) . rangedThing . woThing) mn ->
case ai of
-- Benign case:
ArgInfo h (Modality Relevant{} (Quantityω _) Continuous (PolarityModality { modPolarityAnn = MixedPolarity })) UserWritten UnknownFVs (Annotation IsNotLock) ->
return $ WithHiding h n
-- Error cases:
ArgInfo _ _ _ _ (Annotation (IsLock _)) ->
abort $ noAnn "Lock"
ArgInfo h (Modality r q c p) _ _ _
| not (isRelevant r) ->
abort "Arguments to pattern synonyms must be relevant"
| not (isQuantityω q) ->
abort $ noAnn "Quantity"
| modPolarityAnn p /= MixedPolarity ->
abort $ noAnn "Polarity"
| c /= Continuous ->
abort $ noAnn "Cohesion"
-- Invariant: origin and fvs not used.
ArgInfo _ _ _ (KnownFVs _) _ -> __IMPOSSIBLE__
ArgInfo _ _ o _ _ | o /= UserWritten -> __IMPOSSIBLE__
ArgInfo _ _ _ _ _ -> __IMPOSSIBLE__
-- Error case: other named args are unsupported (issue #7136)
| otherwise ->
abort "Arguments to pattern synonyms cannot be named"
mkLamClause
:: Catchall
-> [Expr] -- ^ Possibly empty list of patterns.
-> RHS
-> Parser LamClause
mkLamClause catchall es rhs = mapM exprToPattern es <&> \ ps ->
LamClause{ lamLHS = ps, lamRHS = rhs, lamCatchall = catchall }
mkAbsurdLamClause :: Catchall -> List1 Expr -> Parser LamClause
mkAbsurdLamClause catchall es = mkLamClause catchall (List1.toList es) AbsurdRHS
{- RHS or type signature -}
data RHSOrTypeSigs
= JustRHS RHS
| TypeSigsRHS Expr
deriving Show
patternToNames :: Pattern -> Parser (List1 (ArgInfo, Name))
patternToNames = \case
IdentP _ (QName i) -> return $ singleton (defaultArgInfo, i)
WildP r -> return $ singleton (defaultArgInfo, C.noName r)
DotP kwr _ (Ident (QName i)) -> return $ singleton (makeIrrelevant kwr defaultArgInfo, i)
RawAppP _ ps -> sconcat . List2.toList1 <$> mapM patternToNames ps
p -> parseError $
"Illegal name in type signature: " ++ prettyShow p
funClauseOrTypeSigs :: [Attr] -> ([RewriteEqn] -> [WithExpr] -> LHS)
-> [Either RewriteEqn (List1 (Named Name Expr))]
-> RHSOrTypeSigs
-> WhereClause -> Parser (List1 Declaration)
funClauseOrTypeSigs attrs lhs' with mrhs wh = do
(rs , es) <- buildWithBlock with
let lhs = lhs' rs (map (fmap observeModifiers) es)
-- traceShowM lhs
case mrhs of
JustRHS rhs -> do
unless (null attrs) $ parseWarning $ MisplacedAttributes (getRange attrs) "A function clause cannot have attributes"
return $ singleton $ FunClause lhs rhs wh empty
TypeSigsRHS e -> case wh of
NoWhere -> case lhs of
LHS p _ _ | hasEllipsis p -> parseError "The ellipsis ... cannot have a type signature"
LHS _ _ (_:_) -> parseError "Illegal: with in type signature"
LHS _ (_:_) _ -> parseError "Illegal: rewrite in type signature"
LHS p _ _ | hasWithPatterns p -> parseError "Illegal: with patterns in type signature"
LHS p [] [] -> forMM (patternToNames p) $ \ (info, x) -> do
info <- applyAttrs attrs info
return $ typeSig info (getTacticAttr attrs) x e
_ -> parseError "A type signature cannot have a where clause"
typeSig :: ArgInfo -> TacticAttribute -> Name -> Expr -> Declaration
typeSig i tac n e = TypeSig i tac n (Generalized e)
------------------------------------------------------------------------
-- * Relevance
makeIrrelevant :: (HasRange a, LensRelevance b) => a -> b -> b
makeIrrelevant = setRelevance . Irrelevant . OIrrDot . getRange
makeShapeIrrelevant :: (HasRange a, LensRelevance b) => a -> b -> b
makeShapeIrrelevant = setRelevance . ShapeIrrelevant . OShIrrDotDot . getRange
defaultIrrelevantArg :: HasRange a => a -> b -> Arg b
defaultIrrelevantArg a = makeIrrelevant a . defaultArg
defaultShapeIrrelevantArg :: HasRange a => a -> b -> Arg b
defaultShapeIrrelevantArg a = makeShapeIrrelevant a . defaultArg
makeIrrelevantM :: (HasRange a, LensRelevance b) => a -> b -> Parser b
makeIrrelevantM r x = do
assertPristineRelevance r x
return $ makeIrrelevant r x
makeShapeIrrelevantM :: (HasRange a, LensRelevance b) => a -> b -> Parser b
makeShapeIrrelevantM r x = do
assertPristineRelevance r x
return $ makeShapeIrrelevant r x
assertPristineRelevance :: (HasRange a, LensRelevance b) => a -> b -> Parser ()
assertPristineRelevance r x = unless (null $ getRelevance x) $
parseErrorRange r $ "Conflicting relevance information"
------------------------------------------------------------------------
-- * Attributes
-- | Parse an attribute.
toAttribute :: Range -> Expr -> Parser (Maybe Attr)
toAttribute r e = do
case exprToAttribute r e of
Nothing -> Nothing <$ parseWarning (UnknownAttribute r s)
Just a -> do
let attr = Attr r s a
modify' \ st -> st{ parseAttributes = attr : parseAttributes st }
return $ Just attr
where
s = prettyShow e
-- | Apply an attribute to thing (usually `Arg`).
-- This will fail if one of the attributes is already set
-- in the thing to something else than the default value.
applyAttr :: (LensAttribute a) => Attr -> a -> Parser a
applyAttr attr@(Attr _ _ a) = maybe failure return . setPristineAttribute a
where
failure = errorConflictingAttribute attr
-- | Apply attributes to thing (usually `Arg`).
-- Expects a reversed list of attributes.
-- This will fail if one of the attributes is already set
-- in the thing to something else than the default value.
applyAttrs :: LensAttribute a => [Attr] -> a -> Parser a
applyAttrs rattrs arg = do
let attrs = reverse rattrs
checkForUniqueAttribute (isJust . isQuantityAttribute ) attrs
checkForUniqueAttribute (isJust . isRelevanceAttribute) attrs
checkForUniqueAttribute (not . null . isTacticAttribute) attrs
foldM (flip applyAttr) arg attrs
applyAttrs1 :: LensAttribute a => List1 Attr -> a -> Parser a
applyAttrs1 = applyAttrs . List1.toList
-- | Set the tactic attribute of a binder
setTacticAttr :: [Attr] -> NamedArg Binder -> NamedArg Binder
setTacticAttr [] = id
setTacticAttr as = updateNamedArg $ fmap $ \ b ->
case getTacticAttr as of
t | null t -> b
| otherwise -> b { bnameTactic = t }
-- | Get the tactic attribute if present.
getTacticAttr :: [Attr] -> TacticAttribute
getTacticAttr as = C.TacticAttribute $
case tacticAttributes [ a | Attr _ _ a <- as ] of
[CA.TacticAttribute e] -> Just e
[] -> Nothing
_ -> __IMPOSSIBLE__
-- | Report a parse error if two attributes in the list are of the same kind,
-- thus, present conflicting information.
checkForUniqueAttribute :: (Attribute -> Bool) -> [Attr] -> Parser ()
checkForUniqueAttribute p attrs = do
let pAttrs = filter (p . theAttr) attrs
when (length pAttrs >= 2) $
errorConflictingAttributes pAttrs
-- | Report an attribute as conflicting (e.g., with an already set value).
errorConflictingAttribute :: Attr -> Parser a
errorConflictingAttribute a = parseErrorRange a $
"Conflicting attribute: " ++ prettyAttr a
-- | Report attributes as conflicting (e.g., with each other).
-- Precondition: List not emtpy.
errorConflictingAttributes :: [Attr] -> Parser a
errorConflictingAttributes [a] = errorConflictingAttribute a
errorConflictingAttributes as = parseErrorRange as $
"Conflicting attributes: " ++ unwords (map prettyAttr as)
prettyAttr :: Attr -> String
prettyAttr = ("@" ++) . attrName
-- | Apply some attributes to some binders.
applyAttributes :: Functor f
=> [Attr]
-- ^ Can contain @tactic@ attribute.
-> ArgInfo
-- ^ If the attributes to be set are not at default value here, crash.
-> f (NamedArg Binder)
-- ^ Binders to apply attributes to.
-> Parser (f (NamedArg Binder))
-- ^ Binders with attributes applied.
applyAttributes attrs ai bs = do
applyAttrs attrs ai <&> \ ai' ->
fmap (setTacticAttr attrs . setArgInfo ai') bs