postgresql-syntax-0.5.0.2: library-internal/PostgresqlSyntax/Ast/SelectWithParens.hs
module PostgresqlSyntax.Ast.SelectWithParens
( SelectWithParens (..),
)
where
import PostgresqlSyntax.Algebra
import {-# SOURCE #-} PostgresqlSyntax.Ast.SelectNoParens (SelectNoParens)
import qualified PostgresqlSyntax.Helpers.Gens as Gens
import qualified PostgresqlSyntax.Helpers.Parsers as Parsers
import qualified PostgresqlSyntax.Helpers.TextBuilders as TextBuilders
import PostgresqlSyntax.Prelude
import qualified Test.QuickCheck as Qc
-- |
-- ==== References
-- @
-- select_with_parens:
-- | '(' select_no_parens ')'
-- | '(' select_with_parens ')'
-- @
data SelectWithParens
= NoParensSelectWithParens SelectNoParens
| WithParensSelectWithParens SelectWithParens
deriving (Show, Generic, Eq, Ord, Data)
instance IsAst SelectWithParens where
toTextBuilder settings =
TextBuilders.renderInParens . \case
NoParensSelectWithParens a -> toTextBuilder settings a
WithParensSelectWithParens a -> toTextBuilder settings a
-- ==== Canonical shape
--
-- Because @select_with_parens@ (@'(' select_with_parens ')'@) and
-- @'(' select_no_parens ')'@ both parse @((select 1))@, there are two
-- possible representations: @WithParensSelectWithParens@ wrapping the
-- inner parenthesised select, or @NoParensSelectWithParens@ of a
-- @SelectNoParens@ whose clause is that inner select. Both render back
-- to the same text. __The first (@WithParensSelectWithParens@) is
-- canonical.__ The 'Canonicalizes' instance normalizes shrunk or generated
-- values to this shape.
parser settings = Parsers.inParens (canonicalize . NoParensSelectWithParens <$> parser settings)
instance Qc.Arbitrary SelectWithParens where
shrink = fmap canonicalize . Qc.genericShrink
arbitrary =
canonicalize
<$> Qc.frequency
[ (3, NoParensSelectWithParens <$> Gens.downscale Qc.arbitrary),
(1, WithParensSelectWithParens <$> Gens.downscale Qc.arbitrary)
]
-- |
-- Collapses the non-canonical @NoParensSelectWithParens@ shape described
-- above to the @WithParensSelectWithParens@ shape the parser actually
-- produces for it. Both 'arbitrary' and 'shrink' can otherwise construct
-- the non-canonical shape (shrinking the inner 'SelectNoParens' toward
-- @Nothing@s is exactly how it arises), which renders fine but parses back
-- to a different, canonical value and so breaks the roundtrip property.
instance Canonicalizes SelectWithParens where
canonicalize = \case
NoParensSelectWithParens a
| Just c <- project @SelectWithParens a -> WithParensSelectWithParens c
other -> other
instance Refines SelectWithParens SelectWithParens where
embed = WithParensSelectWithParens
project = \case
WithParensSelectWithParens a -> Just a
_ -> Nothing