postgresql-syntax-0.5.0.0: library-internal/PostgresqlSyntax/Ast/InExpr.hs
module PostgresqlSyntax.Ast.InExpr
( InExpr (..),
)
where
import qualified HeadedMegaparsec as Parser
import {-# SOURCE #-} PostgresqlSyntax.Ast.AExpr (selectWithParensAExpr)
import PostgresqlSyntax.Ast.ExprList
import {-# SOURCE #-} PostgresqlSyntax.Ast.SelectWithParens (SelectWithParens, refineToSelectWithParens)
import qualified PostgresqlSyntax.Helpers.Gens as Gens
import qualified PostgresqlSyntax.Helpers.Parsers as Parsers
import qualified PostgresqlSyntax.Helpers.TextBuilders as TextBuilders
import PostgresqlSyntax.IsAst
import PostgresqlSyntax.Prelude
import qualified Test.QuickCheck as Qc
import qualified Text.Megaparsec as Megaparsec
-- |
-- ==== References
-- @
-- in_expr:
-- | select_with_parens
-- | '(' expr_list ')'
-- @
data InExpr
= SelectInExpr SelectWithParens
| ExprListInExpr ExprList
deriving (Show, Generic, Eq, Ord, Data)
instance IsAst InExpr where
toTextBuilder settings = \case
SelectInExpr a -> toTextBuilder settings a
ExprListInExpr a -> TextBuilders.renderInParens (toTextBuilder settings a)
parser settings =
(ExprListInExpr <$> Parser.parse (Megaparsec.try (Parser.toParsec (Parsers.inParens (parser settings)))))
<|> (SelectInExpr <$> Parser.wrapToHead (parser settings))
instance Qc.Arbitrary InExpr where
shrink = fmap canonicalize . Qc.genericShrink
arbitrary =
canonicalize
<$> Qc.sized
( \n ->
if n <= 1
then ExprListInExpr <$> Qc.arbitrary
else
Qc.oneof
[ SelectInExpr <$> Gens.downscale Qc.arbitrary,
ExprListInExpr <$> Qc.arbitrary
]
)
-- |
-- Collapses the non-canonical @SelectInExpr@-of-@WithParensSelectWithParens@
-- shape to the @ExprListInExpr@ shape the parser actually produces for it:
-- @in_expr@'s two productions, @select_with_parens@ and
-- @'(' expr_list ')'@, overlap whenever the parenthesised select itself
-- contains another parenthesised select, since a single-element
-- @expr_list@ can itself be that inner parenthesised select wrapped as a
-- @c_expr@. Both render to the same text; the parser's @expr_list@
-- alternative is tried first and so wins. Both 'arbitrary' and 'shrink'
-- can otherwise construct the non-canonical shape, which renders fine but
-- parses back to a different, canonical value and so breaks the
-- roundtrip property.
canonicalize :: InExpr -> InExpr
canonicalize = \case
SelectInExpr a
| Just inner <- refineToSelectWithParens a ->
ExprListInExpr (ExprList (selectWithParensAExpr inner :| []))
other -> other