packages feed

aihc-parser 4.0.0.0 → 5.0.0.0

raw patch · 17 files changed

+427/−11 lines, 17 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Aihc.Parser.Internal.Testing: TkPragmaClose :: LexTokenKind
+ Aihc.Parser.Internal.Testing: TkPragmaOpen :: Text -> LexTokenKind
+ Aihc.Parser.Syntax: DeclRules :: [RuleDecl] -> Decl
+ Aihc.Parser.Syntax: RuleActiveAfter :: Int -> RuleActivation
+ Aihc.Parser.Syntax: RuleActiveBefore :: Int -> RuleActivation
+ Aihc.Parser.Syntax: RuleBinder :: [Annotation] -> UnqualifiedName -> Maybe Type -> RuleBinder
+ Aihc.Parser.Syntax: RuleDecl :: [Annotation] -> Text -> Maybe RuleActivation -> [TyVarBinder] -> [RuleBinder] -> Expr -> Expr -> RuleDecl
+ Aihc.Parser.Syntax: RuleNeverActive :: RuleActivation
+ Aihc.Parser.Syntax: [ruleActivation] :: RuleDecl -> Maybe RuleActivation
+ Aihc.Parser.Syntax: [ruleAnns] :: RuleDecl -> [Annotation]
+ Aihc.Parser.Syntax: [ruleBinderAnns] :: RuleBinder -> [Annotation]
+ Aihc.Parser.Syntax: [ruleBinderName] :: RuleBinder -> UnqualifiedName
+ Aihc.Parser.Syntax: [ruleBinderType] :: RuleBinder -> Maybe Type
+ Aihc.Parser.Syntax: [ruleBinders] :: RuleDecl -> [RuleBinder]
+ Aihc.Parser.Syntax: [ruleLhs] :: RuleDecl -> Expr
+ Aihc.Parser.Syntax: [ruleName] :: RuleDecl -> Text
+ Aihc.Parser.Syntax: [ruleRhs] :: RuleDecl -> Expr
+ Aihc.Parser.Syntax: [ruleTypeBinders] :: RuleDecl -> [TyVarBinder]
+ Aihc.Parser.Syntax: data RuleActivation
+ Aihc.Parser.Syntax: data RuleBinder
+ Aihc.Parser.Syntax: data RuleDecl
+ Aihc.Parser.Syntax: instance Control.DeepSeq.NFData Aihc.Parser.Syntax.RuleActivation
+ Aihc.Parser.Syntax: instance Control.DeepSeq.NFData Aihc.Parser.Syntax.RuleBinder
+ Aihc.Parser.Syntax: instance Control.DeepSeq.NFData Aihc.Parser.Syntax.RuleDecl
+ Aihc.Parser.Syntax: instance Data.Data.Data Aihc.Parser.Syntax.RuleActivation
+ Aihc.Parser.Syntax: instance Data.Data.Data Aihc.Parser.Syntax.RuleBinder
+ Aihc.Parser.Syntax: instance Data.Data.Data Aihc.Parser.Syntax.RuleDecl
+ Aihc.Parser.Syntax: instance GHC.Classes.Eq Aihc.Parser.Syntax.RuleActivation
+ Aihc.Parser.Syntax: instance GHC.Classes.Eq Aihc.Parser.Syntax.RuleBinder
+ Aihc.Parser.Syntax: instance GHC.Classes.Eq Aihc.Parser.Syntax.RuleDecl
+ Aihc.Parser.Syntax: instance GHC.Generics.Generic Aihc.Parser.Syntax.RuleActivation
+ Aihc.Parser.Syntax: instance GHC.Generics.Generic Aihc.Parser.Syntax.RuleBinder
+ Aihc.Parser.Syntax: instance GHC.Generics.Generic Aihc.Parser.Syntax.RuleDecl
+ Aihc.Parser.Syntax: instance GHC.Show.Show Aihc.Parser.Syntax.RuleActivation
+ Aihc.Parser.Syntax: instance GHC.Show.Show Aihc.Parser.Syntax.RuleBinder
+ Aihc.Parser.Syntax: instance GHC.Show.Show Aihc.Parser.Syntax.RuleDecl
+ Aihc.Parser.Token: TkPragmaClose :: LexTokenKind
+ Aihc.Parser.Token: TkPragmaOpen :: Text -> LexTokenKind

Files

CHANGELOG.md view
@@ -6,6 +6,21 @@  ## [Unreleased] +## [5.0.0.0] - 2026-09-30++### Added++- `RULES` pragmas are parsed. A `{-# RULES ... #-}` pragma at the top level+  is a `DeclRules` declaration that holds one `RuleDecl` per rule: the name,+  the phase control (`[n]`, `[~n]` or `[~]`), the type variables of a leading+  `forall` that a second `forall` follows, the pattern variables with their+  optional types, and the two sides as expressions. The lexer opens the+  pragma with a `TkPragmaOpen "RULES"` token and closes it with+  `TkPragmaClose`, and lexes the rules between them as ordinary tokens, so+  the rules take part in layout the way GHC reads them: a rule that starts+  in the column of the enclosing declarations begins a new rule. Before, a+  `RULES` pragma was a `DeclPragma` with a `PragmaUnknown` body.+ ## [4.0.0.0] - 2026-09-17  ### Fixed
aihc-parser.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.8 name: aihc-parser-version: 4.0.0.0+version: 5.0.0.0 build-type: Simple license: Unlicense license-file: LICENSE
docs/aihc-parser-supported-extensions.md view
@@ -68,7 +68,7 @@ | PatternSynonyms           |   🟢    | 34/34         | | PolyKinds                 |   🟢    | 14/14         | | QualifiedDo               |   🟢    | 4/4           |-| QuantifiedConstraints     |   🟢    | 2/2           |+| QuantifiedConstraints     |   🟢    | 6/6           | | QuasiQuotes               |   🟢    | 11/11         | | RankNTypes                |   🟢    | 5/5           | | RecordWildCards           |   🟢    | 10/10         |@@ -76,7 +76,7 @@ | RequiredTypeArguments     |   🟢    | 4/4           | | RoleAnnotations           |   🟢    | 7/7           | | Safe                      |   🟢    | 2/2           |-| ScopedTypeVariables       |   🟢    | 18/18         |+| ScopedTypeVariables       |   🟢    | 19/19         | | StandaloneDeriving        |   🟢    | 18/18         | | StandaloneKindSignatures  |   🟢    | 12/12         | | StarIsType                |   🟢    | 4/4           |@@ -85,7 +85,7 @@ | TransformListComp         |   🟢    | 18/18         | | TupleSections             |   🟢    | 4/4           | | TypeAbstractions          |   🟢    | 5/5           |-| TypeApplications          |   🟢    | 14/14         |+| TypeApplications          |   🟢    | 15/15         | | TypeData                  |   🟢    | 1/1           | | TypeFamilies              |   🟢    | 61/61         | | TypeFamilyDependencies    |   🟢    | 4/4           |
src/Aihc/Parser/Internal/Decl.hs view
@@ -97,6 +97,7 @@         TkKeywordInstance -> typeFamilyInstParser         _ -> typeDeclarationParser     TkKeywordPattern -> patternSynonymParser+    TkPragmaOpen "RULES" -> rulesDeclParser     TkVarId {} ->       case nextTokKind of         TkReservedDoubleColon -> sigOrValueDecl@@ -144,6 +145,118 @@ -- | Parse a pragma declaration (e.g. {-# INLINE f #-}, {-# SPECIALIZE ... #-}) pragmaDeclParser :: TokParser Decl pragmaDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ DeclPragma <$> anyPragmaParser "pragma declaration"++-- | Parse a @RULES@ pragma:+--+-- > {-# RULES "map/map" [2] forall f g xs. map f (map g xs) = map (f . g) xs #-}+--+-- The lexer opens the pragma with 'TkPragmaOpen' and closes it with+-- 'TkPragmaClose', and lexes the rules between them as ordinary tokens. The+-- rules are separated by semicolons, which the layout rule supplies for a+-- rule that starts in the column of the enclosing declarations.+rulesDeclParser :: TokParser Decl+rulesDeclParser = withSpanAnn (DeclAnn . mkAnnotation) $ do+  expectedTok (TkPragmaOpen "RULES")+  rules <- region "while parsing RULES pragma" (plainSemiSep ruleDeclParser)+  expectedTok TkPragmaClose+  pure (DeclRules rules)++-- | One rule:+--+-- > "name" [2] forall a. forall (x :: a) y. lhs = rhs+--+-- With one @forall@, every binder is a pattern variable. With two, the+-- first binds type variables and the second the pattern variables.+ruleDeclParser :: TokParser RuleDecl+ruleDeclParser = withSpan $ do+  name <- stringTextParser+  activation <- MP.optional ruleActivationParser+  binders <- MP.option [] ruleForallParser+  (typeBinders, termBinders) <-+    MP.option ([], binders) $ do+      termBinders <- ruleForallParser+      pure (map ruleBinderToTyVarBinder binders, termBinders)+  lhs <- region "while parsing rule left-hand side" exprParser+  expectedTok TkReservedEquals+  rhs <- region "while parsing rule right-hand side" exprParser+  pure $ \span' ->+    RuleDecl+      { ruleAnns = [mkAnnotation span'],+        ruleName = name,+        ruleActivation = activation,+        ruleTypeBinders = typeBinders,+        ruleBinders = termBinders,+        ruleLhs = lhs,+        ruleRhs = rhs+      }++-- | The phase control of a rule: @[n]@, @[~n]@ or @[~]@.+ruleActivationParser :: TokParser RuleActivation+ruleActivationParser = do+  expectedTok TkSpecialLBracket+  activation <-+    (ruleTildeParser *> (RuleActiveBefore <$> rulePhaseParser <|> pure RuleNeverActive))+      <|> (RuleActiveAfter <$> rulePhaseParser)+  expectedTok TkSpecialRBracket+  pure activation++ruleTildeParser :: TokParser ()+ruleTildeParser =+  tokenSatisfy "~" $ \tok ->+    case lexTokenKind tok of+      TkPrefixTilde -> Just ()+      TkVarSym "~" -> Just ()+      _ -> Nothing++rulePhaseParser :: TokParser Int+rulePhaseParser =+  tokenSatisfy "phase number" $ \tok ->+    case lexTokenKind tok of+      TkInteger n _ | n >= 0 && n <= fromIntegral (maxBound :: Int) -> Just (fromInteger n)+      _ -> Nothing++-- | @forall x (y :: t).@+ruleForallParser :: TokParser [RuleBinder]+ruleForallParser = do+  expectedTok TkKeywordForall+  binders <- MP.many ruleBinderParser+  expectedTok (TkVarSym ".")+  pure binders++-- | @x@ or @(x :: t)@.+ruleBinderParser :: TokParser RuleBinder+ruleBinderParser =+  withSpan $+    ( do+        name <- ruleBinderNameParser+        pure (\span' -> RuleBinder [mkAnnotation span'] name Nothing)+    )+      <|> ( do+              expectedTok TkSpecialLParen+              name <- ruleBinderNameParser+              expectedTok TkReservedDoubleColon+              ty <- typeParser+              expectedTok TkSpecialRParen+              pure (\span' -> RuleBinder [mkAnnotation span'] name (Just ty))+          )++ruleBinderNameParser :: TokParser UnqualifiedName+ruleBinderNameParser =+  tokenSatisfy "rule variable" $ \tok ->+    case lexTokenKind tok of+      TkVarId ident -> Just (mkUnqualifiedNameAt tok NameVarId ident)+      _ -> Nothing++-- | A binder of the first of two @forall@s binds a type variable.+ruleBinderToTyVarBinder :: RuleBinder -> TyVarBinder+ruleBinderToTyVarBinder binder =+  TyVarBinder+    { tyVarBinderAnns = ruleBinderAnns binder,+      tyVarBinderName = unqualifiedNameText (ruleBinderName binder),+      tyVarBinderKind = ruleBinderType binder,+      tyVarBinderSpecificity = TyVarBSpecified,+      tyVarBinderVisibility = TyVarBVisible+    }  -- | Check whether the expression-as-declaration fallback is enabled. -- GHC allows top-level expressions under TemplateHaskell (bare splices),
src/Aihc/Parser/Lex.hs view
@@ -52,7 +52,7 @@     lexIntBase,     withOptionalMagicHashSuffix,   )-import Aihc.Parser.Lex.Pragmas (tryParsePragma)+import Aihc.Parser.Lex.Pragmas (tryParsePragma, tryParsePragmaClose) import Aihc.Parser.Lex.Quoted   ( decodeStringBody,     processMultilineString,@@ -231,6 +231,7 @@   -- (<|>) for Maybe short-circuits on the first Just without allocating.   fromMaybe (lexErrorToken st "unexpected character") $     lexPragma st+      <|> tryParsePragmaClose st       <|> lexTHQuoteBracket env st       <|> lexQuasiQuote st       <|> lexHexFloat env st@@ -519,6 +520,8 @@     TkReservedBackslash -> True     TkTypeApp -> True     TkPragma _ -> True+    TkPragmaOpen _ -> True+    TkPragmaClose -> True     _ -> False  -- | Returns True for tokens after which a '.' can begin a record field access
src/Aihc/Parser/Lex/Pragmas.hs view
@@ -3,6 +3,7 @@  module Aihc.Parser.Lex.Pragmas   ( tryParsePragma,+    tryParsePragmaClose,     parsePragmaType,     parseControlPragma,   )@@ -25,13 +26,48 @@  -- | Single entry point for pragma parsing. -- Scans "{-# ... #-}" once, then parses the body to determine which Pragma it is.+--+-- A pragma whose body is Haskell syntax, such as @RULES@, does not become one+-- token. The lexer emits a 'TkPragmaOpen' token for its opening and keyword,+-- lexes the body as ordinary tokens, and emits 'TkPragmaClose' at its @#-}@+-- (see 'tryParsePragmaClose'). The body then takes part in layout like the+-- code around it, which is how GHC reads it. tryParsePragma :: LexerState -> Maybe (LexToken, LexerState) tryParsePragma st = do   (rawBody, consumedLen) <- extractPragmaBody (lexerInput st)-  let fullText = "{-#" <> rawBody <> "#-}"-      pragma = Pragma {pragmaType = parsePragmaType rawBody, pragmaRawText = fullText}-      st' = advanceN consumedLen st-  Just (mkToken st st' fullText (TkPragma pragma), st')+  case openPragmaKeyword rawBody of+    Just (keyword, openLen) ->+      let st' = (advanceN openLen st) {lexerInPragma = True}+       in Just (mkToken st st' (T.take openLen (lexerInput st)) (TkPragmaOpen keyword), st')+    Nothing ->+      let fullText = "{-#" <> rawBody <> "#-}"+          pragma = Pragma {pragmaType = parsePragmaType rawBody, pragmaRawText = fullText}+          st' = advanceN consumedLen st+       in Just (mkToken st st' fullText (TkPragma pragma), st')++-- | The keyword of a pragma whose body is lexed as ordinary tokens, and the+-- length of the opening up to and including the keyword.+openPragmaKeyword :: Text -> Maybe (Text, Int)+openPragmaKeyword rawBody =+  let leading = T.takeWhile isSpace rawBody+      keyword = T.takeWhile (not . isSpace) (T.drop (T.length leading) rawBody)+      upper = T.toUpper keyword+   in if upper `elem` openPragmaKeywords+        then Just (upper, 3 + T.length leading + T.length keyword)+        else Nothing++-- | The pragmas that 'tryParsePragma' opens rather than lexes as one token.+openPragmaKeywords :: [Text]+openPragmaKeywords = ["RULES"]++-- | The @#-}@ that closes a pragma opened by 'tryParsePragma'.+tryParsePragmaClose :: LexerState -> Maybe (LexToken, LexerState)+tryParsePragmaClose st+  | lexerInPragma st,+    "#-}" `T.isPrefixOf` lexerInput st =+      let st' = (advanceN 3 st) {lexerInPragma = False}+       in Just (mkToken st st' "#-}" TkPragmaClose, st')+  | otherwise = Nothing  -- | Extract the raw body text between "{-#" and "#-}". -- Returns (body_text, total_consumed_length) where total includes "{-#" and "#-}".
src/Aihc/Parser/Lex/Types.hs view
@@ -162,6 +162,11 @@     TkTypeApp   | -- Pragmas     TkPragma Pragma+  | -- | The opening of a pragma whose body is lexed as ordinary tokens,+    -- such as @{-# RULES@. The text is the pragma keyword in upper case.+    TkPragmaOpen Text+  | -- | The @#-}@ that closes a pragma opened by 'TkPragmaOpen'.+    TkPragmaClose   | -- TemplateHaskellQuotes bracket tokens     TkTHExpQuoteOpen   | TkTHExpQuoteClose@@ -232,7 +237,10 @@     lexerByteOffset :: !Int,     lexerAtLineStart :: !Bool,     lexerPrevTokenKind :: !(Maybe LexTokenKind),-    lexerHadTrivia :: !Bool+    lexerHadTrivia :: !Bool,+    -- | Whether the lexer is inside a pragma opened by 'TkPragmaOpen', so+    -- that the next @#-}@ closes it.+    lexerInPragma :: !Bool   }   deriving (Eq, Show) @@ -331,7 +339,8 @@         lexerByteOffset = 0,         lexerAtLineStart = True,         lexerPrevTokenKind = Nothing,-        lexerHadTrivia = True+        lexerHadTrivia = True,+        lexerInPragma = False       }   ) 
src/Aihc/Parser/Parens.hs view
@@ -630,6 +630,23 @@     DeclTypeFamilyInst tfi -> DeclTypeFamilyInst (addTypeFamilyInstParens tfi)     DeclDataFamilyInst dfi -> DeclDataFamilyInst (addDataFamilyInstParens dfi)     DeclPragma {} -> decl+    DeclRules rules -> DeclRules (map addRuleDeclParens rules)++-- | Parenthesize the sides and binder types of a rewrite rule. The+-- left-hand side is followed by @=@ on the same line, so a side whose+-- rightmost part would take the @=@ into itself is wrapped.+addRuleDeclParens :: RuleDecl -> RuleDecl+addRuleDeclParens rule =+  rule+    { ruleTypeBinders = map addTyVarBinderParens (ruleTypeBinders rule),+      ruleBinders = map addRuleBinderParens (ruleBinders rule),+      ruleLhs = wrapExpr (lhsNeedsParens (ruleLhs rule)) (addExprParens (ruleLhs rule)),+      ruleRhs = addExprParens (ruleRhs rule)+    }+  where+    lhsNeedsParens expr = isGreedyExpr expr || isOpenEnded expr || endsWithTypeSig expr+    addRuleBinderParens binder =+      binder {ruleBinderType = fmap addSignatureTypeParens (ruleBinderType binder)}  addDeclSpliceParens :: Expr -> Expr addDeclSpliceParens = addExprParens
src/Aihc/Parser/Pretty.hs view
@@ -278,6 +278,51 @@     DeclTypeFamilyInst tfi -> [prettyTopTypeFamilyInst tfi]     DeclDataFamilyInst dfi -> [prettyTopDataFamilyInst dfi]     DeclPragma pragma -> [prettyPragma pragma]+    DeclRules rules -> prettyRulesLines rules++-- | A @RULES@ pragma, one rule per line. Every rule after the first starts+-- with an explicit semicolon in the first column, so the rules are separated+-- whether or not an enclosing layout context supplies semicolons, and the+-- closing @#-}@ stands in the first column so that it ends any layout+-- context a rule opened.+prettyRulesLines :: [RuleDecl] -> [Doc ann]+prettyRulesLines rules =+  ["{-# RULES"]+    <> zipWith prettyRuleLine [0 :: Int ..] rules+    <> ["#-}"]+  where+    prettyRuleLine index rule+      | index == 0 = nest 2 (prettyRuleDecl rule)+      | otherwise = nest 2 (";" <+> prettyRuleDecl rule)++prettyRuleDecl :: RuleDecl -> Doc ann+prettyRuleDecl rule =+  hsep+    ( [pretty (show (ruleName rule))]+        <> maybe [] (pure . prettyRuleActivation) (ruleActivation rule)+        <> foralls+        <> [prettyExpr (ruleLhs rule), "=", prettyExpr (ruleRhs rule)]+    )+  where+    foralls =+      case (ruleTypeBinders rule, ruleBinders rule) of+        ([], []) -> []+        ([], binders) -> [ruleForall (map prettyRuleBinder binders)]+        (typeBinders, binders) -> [ruleForall (map prettyTyVarBinder typeBinders), ruleForall (map prettyRuleBinder binders)]+    ruleForall binders = hsep ("forall" : binders) <> "."++prettyRuleActivation :: RuleActivation -> Doc ann+prettyRuleActivation activation =+  case activation of+    RuleActiveAfter phase -> brackets (pretty phase)+    RuleActiveBefore phase -> brackets ("~" <> pretty phase)+    RuleNeverActive -> brackets "~"++prettyRuleBinder :: RuleBinder -> Doc ann+prettyRuleBinder binder =+  case ruleBinderType binder of+    Nothing -> prettyBinderName (ruleBinderName binder)+    Just ty -> parens (prettyBinderName (ruleBinderName binder) <+> "::" <+> prettyType ty)  prettyRoleAnnotation :: RoleAnnotation -> Doc ann prettyRoleAnnotation ann =
src/Aihc/Parser/Shorthand.hs view
@@ -236,7 +236,39 @@     DeclTypeFamilyInst tfi -> "DeclTypeFamilyInst" <+> parens (docTypeFamilyInst tfi)     DeclDataFamilyInst dfi -> "DeclDataFamilyInst" <+> parens (docDataFamilyInst dfi)     DeclPragma pragma -> "DeclPragma" <+> docPragma pragma+    DeclRules rules -> "DeclRules" <+> brackets (hsep (punctuate comma (map docRuleDecl rules))) +docRuleDecl :: RuleDecl -> Doc ann+docRuleDecl rule =+  "RuleDecl" <+> braces (hsep (punctuate comma fields))+  where+    fields =+      [docText (ruleName rule)]+        <> optionalField docRuleActivation (ruleActivation rule)+        <> listField docTyVarBinder (ruleTypeBinders rule)+        <> listField docRuleBinder (ruleBinders rule)+        <> [docExpr (ruleLhs rule), docExpr (ruleRhs rule)]++docRuleActivation :: RuleActivation -> Doc ann+docRuleActivation activation =+  case activation of+    RuleActiveAfter phase -> "RuleActiveAfter" <+> pretty phase+    RuleActiveBefore phase -> "RuleActiveBefore" <+> pretty phase+    RuleNeverActive -> "RuleNeverActive"++docRuleBinder :: RuleBinder -> Doc ann+docRuleBinder binder =+  "RuleBinder"+    <+> braces+      ( hsep+          ( punctuate+              comma+              ( [docUnqualifiedNameText (ruleBinderName binder)]+                  <> optionalField (\ty -> "Just" <+> parens (docType ty)) (ruleBinderType binder)+              )+          )+      )+ docValueDecl :: ValueDecl -> Doc ann docValueDecl vdecl =   case vdecl of@@ -1112,6 +1144,8 @@     TkPrefixTilde -> "TkPrefixTilde"     TkRecordDot -> "TkRecordDot"     TkPragma pragma' -> "TkPragma" <+> docPragmaType (pragmaType pragma')+    TkPragmaOpen keyword -> "TkPragmaOpen" <+> docText keyword+    TkPragmaClose -> "TkPragmaClose"     TkQuasiQuote quoter body -> "TkQuasiQuote" <+> docText quoter <+> docText body     TkLineComment -> "TkLineComment"     TkBlockComment -> "TkBlockComment"
src/Aihc/Parser/Syntax.hs view
@@ -87,6 +87,9 @@     Role (..),     RoleAnnotation (..),     Rhs (..),+    RuleActivation (..),+    RuleBinder (..),+    RuleDecl (..),     SourceSpan,     pattern SourceSpan,     sourceSpanSourceName,@@ -1203,6 +1206,48 @@     DeclDataFamilyInst DataFamilyInst   | -- | A standalone pragma declaration such as @{-# INLINE f #-}@.     DeclPragma Pragma+  | -- | @{-# RULES "map/map" forall f g xs. map f (map g xs) = map (f . g) xs #-}@+    DeclRules [RuleDecl]+  deriving (Data, Eq, Show, Generic, NFData)++-- | One rewrite rule of a @RULES@ pragma.+-- Example: @"map/map" [2] forall f g xs. map f (map g xs) = map (f . g) xs@.+data RuleDecl = RuleDecl+  { ruleAnns :: [Annotation],+    -- | The name in double quotes. It only identifies the rule in reports.+    ruleName :: Text,+    -- | The phase control after the name, such as @[2]@ or @[~2]@.+    ruleActivation :: Maybe RuleActivation,+    -- | The type variables of a leading @forall a b.@ that a second+    -- @forall@ follows. A rule with one @forall@ binds only term variables.+    ruleTypeBinders :: [TyVarBinder],+    -- | The pattern variables bound by @forall@.+    ruleBinders :: [RuleBinder],+    -- | The left-hand side, matched against the program.+    ruleLhs :: Expr,+    -- | The right-hand side, put in place of a match.+    ruleRhs :: Expr+  }+  deriving (Data, Eq, Show, Generic, NFData)++-- | The phases in which a rule is active.+-- Examples: @[2]@, @[~2]@, and @[~]@.+data RuleActivation+  = -- | @[n]@: active in phase @n@ and later phases.+    RuleActiveAfter Int+  | -- | @[~n]@: active before phase @n@.+    RuleActiveBefore Int+  | -- | @[~]@: never active.+    RuleNeverActive+  deriving (Data, Eq, Show, Generic, NFData)++-- | One pattern variable of a rule, bound by @forall@.+-- Examples: @xs@ and @(g :: forall b. (a -> b -> b) -> b -> b)@.+data RuleBinder = RuleBinder+  { ruleBinderAnns :: [Annotation],+    ruleBinderName :: UnqualifiedName,+    ruleBinderType :: Maybe Type+  }   deriving (Data, Eq, Show, Generic, NFData)  -- | Peel nested 'DeclAnn' wrappers.
test/Spec.hs view
@@ -648,6 +648,7 @@             "type instance F Int = Bool",             "data instance DF Int = DFInt",             "{-# INLINE f #-}",+            "{-# RULES \"f/g\" forall x. f (g x) = x #-}",             "pattern Q :: Int -> T Int",             "pattern Q x = MkT x",             "$(pure [])"
+ test/Test/Fixtures/golden/pragma/rules-layout.yaml view
@@ -0,0 +1,12 @@+extensions: []+input: |+  module Demo where+  {-# RULES+  "map/map" [2] forall f g xs. map f (map g xs) = map (f . g) xs+  "map/id" [~1] forall xs. map id xs = xs+  "fold/build" forall k z (g :: forall b. (a -> b -> b) -> b -> b) . foldr k z (build g) = g k z+    #-}+  x = 1+ast: |-+  Module {ModuleHead {"Demo"}, [DeclRules [RuleDecl {"map/map", RuleActiveAfter 2, [RuleBinder {"f"}, RuleBinder {"g"}, RuleBinder {"xs"}], EApp (EApp (EVar "map") (EVar "f")) (EParen (EApp (EApp (EVar "map") (EVar "g")) (EVar "xs"))), EApp (EApp (EVar "map") (EParen (EInfix (EVar "f") "." (EVar "g")))) (EVar "xs")}, RuleDecl {"map/id", RuleActiveBefore 1, [RuleBinder {"xs"}], EApp (EApp (EVar "map") (EVar "id")) (EVar "xs"), EVar "xs"}, RuleDecl {"fold/build", [RuleBinder {"k"}, RuleBinder {"z"}, RuleBinder {"g", Just (TForall [TyVarBinder {"b"}] (TFun (TParen (TFun (TVar "a") (TFun (TVar "b") (TVar "b")))) (TFun (TVar "b") (TVar "b"))))}], EApp (EApp (EApp (EVar "foldr") (EVar "k")) (EVar "z")) (EParen (EApp (EVar "build") (EVar "g"))), EApp (EApp (EVar "g") (EVar "k")) (EVar "z")}], DeclValue (PatternBind (PVar "x") (EInt 1 TInteger))]}+status: pass
+ test/Test/Fixtures/golden/pragma/rules-type-binders.yaml view
@@ -0,0 +1,7 @@+extensions: [TypeApplications]+input: |+  {-# RULES "id" forall a. forall (x :: a). id @a x = x; "never" [~] forall. f = g #-}+  {-# RULES #-}+ast: |-+  Module {[DeclRules [RuleDecl {"id", [TyVarBinder {"a"}], [RuleBinder {"x", Just (TVar "a")}], EApp (ETypeApp (EVar "id") (TVar "a")) (EVar "x"), EVar "x"}, RuleDecl {"never", RuleNeverActive, EVar "f", EVar "g"}], DeclRules []]}+status: pass
+ test/Test/Fixtures/oracle/pragma/RulesPragmaForms.hs view
@@ -0,0 +1,20 @@+{- ORACLE_TEST pass -}+{-# LANGUAGE ScopedTypeVariables, TypeApplications #-}+module RulesPragmaForms where++{-# RULES+"map/map" [2] forall f g xs. map f (map g xs) = map (f . g) xs+"map/id"  [~1] forall xs. map id xs = xs+"fold/build" forall k z (g :: forall b. (a -> b -> b) -> b -> b) . foldr k z (build g) = g k z+"id/type" forall a. forall (x :: a). id @a x = x+"never" [~] forall (x :: Int). negate (negate x) = x+  #-}++{-# RULES "one" forall x. one x = x ; "two" forall y. two y = y #-}++build :: (forall b. (a -> b -> b) -> b -> b) -> [a]+build g = g (:) []++one, two :: Int -> Int+one = id+two = id
test/Test/Properties/Arb/Decl.hs view
@@ -24,6 +24,7 @@     genConName,     genConSym,     genConUnqualifiedName,+    genStringValue,     genVarId,     genVarIdNoHash,     genVarName,@@ -69,6 +70,7 @@         genDeclTypeFamilyInst,         genDeclDataFamilyInst,         genDeclPragma,+        genDeclRules,         genDeclPatSyn,         genDeclPatSynSig,         genDeclStandaloneKindSig@@ -1109,6 +1111,59 @@ mkPragma :: PragmaType -> Pragma mkPragma pt = Pragma {pragmaType = pt, pragmaRawText = ""} +genDeclRules :: Gen Decl+genDeclRules = DeclRules <$> smallList0 genRuleDecl++genRuleDecl :: Gen RuleDecl+genRuleDecl = do+  name <- genStringValue+  activation <- optional genRuleActivation+  typeBinders <- smallList0 genSimpleTyVarBinder+  binders <- smallList0 genRuleBinder+  lhs <- genRuleLhs+  rhs <- genExpr+  pure+    RuleDecl+      { ruleAnns = [],+        ruleName = name,+        ruleActivation = activation,+        ruleTypeBinders = typeBinders,+        ruleBinders = binders,+        ruleLhs = lhs,+        ruleRhs = rhs+      }++genRuleActivation :: Gen RuleActivation+genRuleActivation =+  oneof+    [ RuleActiveAfter <$> chooseInt (0, 3),+      RuleActiveBefore <$> chooseInt (0, 3),+      pure RuleNeverActive+    ]++genRuleBinder :: Gen RuleBinder+genRuleBinder = RuleBinder [] . mkUnqualifiedName NameVarId <$> genVarId <*> optional genType++-- | A left-hand side is a variable applied to arguments, as GHC requires.+genRuleLhs :: Gen Expr+genRuleLhs = do+  headName <- genVarName+  args <- smallList0 genExpr+  pure (foldl EApp (EVar headName) args)++shrinkRuleDecl :: RuleDecl -> [RuleDecl]+shrinkRuleDecl rule =+  [rule {ruleActivation = Nothing} | isJust (ruleActivation rule)]+    <> [rule {ruleTypeBinders = binders'} | binders' <- shrinkTyVarBinders (ruleTypeBinders rule)]+    <> [rule {ruleBinders = binders'} | binders' <- shrinkList shrinkRuleBinder (ruleBinders rule)]+    <> [rule {ruleLhs = lhs'} | lhs' <- shrinkExpr (ruleLhs rule)]+    <> [rule {ruleRhs = rhs'} | rhs' <- shrinkExpr (ruleRhs rule)]++shrinkRuleBinder :: RuleBinder -> [RuleBinder]+shrinkRuleBinder binder =+  [binder {ruleBinderType = Nothing} | isJust (ruleBinderType binder)]+    <> [binder {ruleBinderType = Just ty'} | Just ty <- [ruleBinderType binder], ty' <- shrinkType ty]+ genDeclPatSyn :: Gen Decl genDeclPatSyn = do   synName <- genConUnqualifiedName@@ -1235,6 +1290,8 @@     DeclDataFamilyInst dfi ->       [DeclDataFamilyInst dfi' | dfi' <- shrinkDataFamilyInst dfi]     DeclPragma _ -> []+    DeclRules rules ->+      [DeclRules rules' | rules' <- shrinkList shrinkRuleDecl rules]  -- --------------------------------------------------------------------------- -- Value declarations (function binds and pattern binds)
test/Test/Properties/NoExceptions.hs view
@@ -194,6 +194,8 @@       TkPragma . (\pt -> Syntax.Pragma {Syntax.pragmaType = pt, Syntax.pragmaRawText = ""}) <$> (Syntax.PragmaSource <$> genTokenText <*> genTokenText),       TkPragma . (\pt -> Syntax.Pragma {Syntax.pragmaType = pt, Syntax.pragmaRawText = ""}) . Syntax.PragmaSCC <$> genTokenText,       TkPragma . (\pt -> Syntax.Pragma {Syntax.pragmaType = pt, Syntax.pragmaRawText = ""}) . Syntax.PragmaUnknown <$> genTokenText,+      TkPragmaOpen <$> genTokenText,+      pure TkPragmaClose,       TkVarId <$> genIdentifierText,       TkConId <$> genConstructorText,       TkQVarId <$> genModuleText <*> genIdentifierText,