packages feed

css-parser (empty) → 0.0.1

raw patch · 50 files changed

+6405/−0 lines, 50 filesdep +QuickCheckdep +arraydep +base

Dependencies added: QuickCheck, array, base, bytestring, containers, css-parser, either, generic-arbitrary, generic-deriving, quickcheck-instances, reorder-expression, tasty, tasty-discover, tasty-hunit, tasty-quickcheck, text, these, unordered-containers

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# `css-parser` Changelog++## Version 0.0.1 2026-04-18++* init
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Willem Van Onsem (c) 2019++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Willem Van Onsem nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,38 @@+# css-parser++This is a modern CSS parser and printer written in pure Haskell with Alex/Happy stack.+CSS libraries test set includes: bootstrap, carbon, patternfly, uikit, primer, uswds, etc.++css-parser is based on [css-selectors](https://hackage.haskell.org/package/css-selectors).++The work is started to provide info about style sheets structure to+[miso-css](https://github.com/yaitskov/miso-css) library.++## Dev++``` shell+$ nix develop+$ emacs src/CssParser.hs &+$ cabal test+$ cabal repl+ghci> import CssParser+ghci> parseCss "p {x: 1px;}"+CssFile {rules = [CssRule (Selector Nothing (TagSelector {tagNs = NoBar, tagName = TagName "p", tagSubSelectors = []}) [] :| []) [CssLeafRule (KnownDescriptor XT) (PropVals (IntVal (TypedNum "1" Px) :| []) Nothing)]]}+```++``` shell+nix build+./result file.css+./result < file.css+```++### Integration tests++``` shell+cd itest+nix develop+intest+```++Links to successfully parsed CSS files are stored in `.css-hashes`+folder to exclude them from consequent `intest` reruns.
+ app/Main.hs view
@@ -0,0 +1,17 @@+module Main where++import Control.Monad ( forM_ )+import CssParser ( parseCss )+import CssParser.Prelude+import System.Environment ( getArgs )++main :: IO ()+main =+  getArgs >>= \case+    [] -> do+      ast <- parseCss <$> getContents+      print ast+    cssFiles ->+      forM_ cssFiles $ \cssFile -> do+        ast <- parseCss <$> readFile cssFile+        print ast
+ css-parser.cabal view
@@ -0,0 +1,128 @@+cabal-version:       3.0+name:                css-parser+version:             0.0.1+synopsis:            pure CSS parser/printer+description:         Modern CSS parser in pure Haskell built on Alex+Happy stack+homepage:            https://github.com/yaitskov/css-parser+license:             BSD-3-Clause+license-file:        LICENSE+author:              Daniil Iaitskov+maintainer:          dyaitskov@gmail.com+copyright:           2026+category:            web+build-type:          Simple+extra-doc-files:+  , CHANGELOG.md+  , README.md++common base+  default-language: GHC2024+  ghc-options: -Wall+  default-extensions:+    DefaultSignatures+    NoImplicitPrelude+    OverloadedStrings+    DuplicateRecordFields+    OverloadedRecordDot+    PatternSynonyms+  build-depends:+    , base >=4.7 && < 5+    , text >=2 && < 3++library+  import: base+  hs-source-dirs: src+  exposed-modules:+    , CssParser+    , CssParser.At.Container+    , CssParser.At.CustomMedia+    , CssParser.At.FontFeatureValues+    , CssParser.At.FontPaletteValues+    , CssParser.At.Function+    , CssParser.At.Import+    , CssParser.At.Keyframe+    , CssParser.At.MediaQuery+    , CssParser.At.Page+    , CssParser.At.Supports+    , CssParser.Descriptor+    , CssParser.File+    , CssParser.FixRule+    , CssParser.Ident+    , CssParser.List+    , CssParser.MonoPair+    , CssParser.Norm+    , CssParser.Parser.Monad+    , CssParser.Prelude+    , CssParser.Rule+    , CssParser.Rule.Pseudo+    , CssParser.Rule.Show+    , CssParser.Rule.Type+    , CssParser.Rule.TypedNum+    , CssParser.Rule.Value+    , CssParser.Show+    , CssParser.TextMarshal+    , CssParser.Utils+  other-modules:+    , CssParser.Lexer+    , CssParser.Lexer.Token+    , CssParser.Parser+  build-depends:+    , array >=0.5.2 && < 1+    , bytestring >=0.9 && < 1+    , either < 6+    , generic-deriving < 2+    , reorder-expression < 1+    , these < 2+    , unordered-containers < 1+  build-tool-depends:+    , alex:alex+    , happy:happy++executable css-parser+  import: base+  ghc-options: -threaded+  main-is: Main.hs+  hs-source-dirs: app+  build-depends:+    , css-parser++test-suite test+  import: base+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  default-extensions:+    DerivingVia+    StandaloneDeriving+  other-modules:+    , CssParser.Test.Arbitrary+    , CssParser.Test.Arbitrary.At+    , CssParser.Test.Arbitrary.Container+    , CssParser.Test.Arbitrary.Ident+    , CssParser.Test.Arbitrary.File+    , CssParser.Test.Arbitrary.FontFeatureValues+    , CssParser.Test.Arbitrary.FontPaletteValues+    , CssParser.Test.Arbitrary.Function+    , CssParser.Test.Arbitrary.Media+    , CssParser.Test.Arbitrary.MonoPair+    , CssParser.Test.Arbitrary.Rule+    , CssParser.Test.Arbitrary.Value++  build-depends:+    , css-parser+    , containers+    , generic-arbitrary             >1 && <2+    , QuickCheck                    < 3+    , quickcheck-instances+    , tasty+    , tasty-discover+    , tasty-hunit+    , tasty-quickcheck+    , unordered-containers+++  ghc-options:         -threaded -rtsopts -with-rtsopts=-N++source-repository head+  type:     git+  location: https://github.com/yaitskov/css-parser
+ src/CssParser.hs view
@@ -0,0 +1,38 @@+module CssParser+  ( parseCss+  , parseCssP+  , alex+  , getToken+  , module X+  , NonEmpty ((:|))+  ) where++import CssParser.At.MediaQuery as X+import CssParser.At.Supports as X hiding (FeatureQuery)+import CssParser.Rule.Value as X+import CssParser.File as X+import CssParser.Ident as X+import CssParser.Lexer (TokenLoc, alexScanTokens, getToken)+import CssParser.Norm as X (Norm (..))+import CssParser.Parser.Monad as X+import CssParser.Parser as X (cssParser)+import CssParser.Prelude+import CssParser.Rule as X hiding (Namespace)+import CssParser.Rule.TypedNum as X+import CssParser.Show as X++alex :: String -> Either String [TokenLoc]+alex = alexScanTokens++parseCssP :: String -> P CssFile+parseCssP st = al (alex st')+  where+    st' = filter ('\r' /=) st+    al (Left er) = error er+    al (Right val) = cssParser val++parseCss :: String -> CssFile+parseCss s =+  case parseCssP s of+    Ok v -> v+    Failed e -> error e
+ src/CssParser/At/Container.hs view
@@ -0,0 +1,50 @@+module CssParser.At.Container where++import CssParser.At.MediaQuery+import CssParser.Ident+import CssParser.Prelude+import CssParser.Show++instance ShowSpaceBetween Ident ContainerQuery where+  cssSpace _ _ = " "++instance ShowSpaceBetween (These Ident ContainerQuery) (These Ident ContainerQuery) where+  cssSpace _ _ = ", "++newtype ContainerQueryMap+  = ContainerQueryMap (NonEmpty (These Ident ContainerQuery))+  deriving (Show, Eq, Ord, Generic)++instance CssShow ContainerQueryMap where+  toCssText (ContainerQueryMap cqm) = toCssText cqm+++data CqOp+  = CqOpFeature MediaFeature+  | CqApp Ident ContainerQuery+  deriving (Show, Eq, Ord, Generic)++instance CssShow CqOp where+  toCssText = \case+    CqOpFeature f -> "(" <> toCssText f <> ")"+    CqApp f (CqFeature (AsIs x@CqOpFeature {})) ->+      toCssText f <> toCssText x+    CqApp f (CqFeature x) ->+      toCssText f <> "(" <> toCssText x <> ")"+    CqApp f args ->+      toCssText f <> "(" <> toCssText args <> ")"++instance ShowParenthesis ContainerQuery CqOp where+  left _ _ = ""+  right _ _ = ""++data ContainerQuery+  = CqBin BinOp (Not ContainerQuery CqOp) ContainerQuery+  | CqFeature (Not ContainerQuery CqOp)+  deriving (Show, Eq, Ord, Generic)++instance CssShow ContainerQuery where+  toCssText = \case+    CqBin bop x l ->+      toCssText (CqFeature x) <> toCssText bop  <> toCssText l+    CqFeature mf -> toCssText mf
+ src/CssParser/At/CustomMedia.hs view
@@ -0,0 +1,16 @@+module CssParser.At.CustomMedia where++import CssParser.At.MediaQuery ( MediaQueryList )+import CssParser.Prelude ( Eq, Ord, Show, Generic, Bool )+import CssParser.Show ( CssShow(..) )+++data CustomMediaQuery+  = CustomMediaFlag Bool+  | CustomMediaQuery MediaQueryList+  deriving (Eq, Show, Ord, Generic)++instance CssShow CustomMediaQuery where+  toCssText = \case+    CustomMediaFlag f ->  toCssText f+    CustomMediaQuery mq -> toCssText mq
+ src/CssParser/At/FontFeatureValues.hs view
@@ -0,0 +1,41 @@+module CssParser.At.FontFeatureValues where++import CssParser.Ident+import CssParser.Rule.Value+import CssParser.At.Keyframe+import CssParser.Prelude+import CssParser.Show++newtype IdentList = IdentList (NonEmpty Ident) deriving newtype (Eq, Ord, Show) deriving (Generic)+instance CssShow IdentList where+  toCssText (IdentList l) = unwords (toCssText <$> toList l)++data FontFeatureEntry = FontFeatureEntry Ident (SslNe Unsigned)+  deriving (Eq, Ord, Show, Generic)+instance CssShow FontFeatureEntry where+  toCssText (FontFeatureEntry i l)+    = toCssText i <> ": " <> toCssText l+instance ShowSpaceBetween FontFeatureEntry FontFeatureEntry where+  cssSpace _ _ = ";"+data FontFeatureValuesSubBlock+  = FontFeatureValuesSubBlock BrowserPrefix Ident [FontFeatureEntry]+  deriving (Eq, Ord, Show, Generic)++instance CssShow FontFeatureValuesSubBlock where+  toCssText (FontFeatureValuesSubBlock bp i ps) =+    "@" <> toCssText bp <> toCssText i <> " {" <> toCssText ps <> "}"++instance ShowSpaceBetween FontFeatureValuesSubBlock FontFeatureValuesSubBlock where+  cssSpace _ _ = ""+data FontFeatureValues+  = FontFeatureValues+  { name :: Either LiteralString IdentList+  , props :: [PropEntry]+  , blocks :: [FontFeatureValuesSubBlock]+  }+  deriving (Show, Eq, Ord, Generic)++instance CssShow FontFeatureValues where+  toCssText ff =+    "font-feature-values " <> toCssText ff.name <> " {" <>+    toCssText ff.props  <> toCssText ff.blocks <> "}"
+ src/CssParser/At/FontPaletteValues.hs view
@@ -0,0 +1,15 @@+module CssParser.At.FontPaletteValues where++import CssParser.At.Keyframe ( PropEntry )+import CssParser.Ident ( Var )+import CssParser.Prelude+import CssParser.Show ( CssShow(..) )+++data FontPaletteValues+  = FontPaletteValues Var [PropEntry]+  deriving (Show, Eq, Ord, Generic)++instance CssShow FontPaletteValues where+  toCssText (FontPaletteValues v ps) =+    "font-palette-values " <> toCssText v <> " {" <> toCssText ps <> "}"
+ src/CssParser/At/Function.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE UndecidableInstances #-}+module CssParser.At.Function where++import CssParser.Ident ( Var )+import CssParser.Rule.Type+import CssParser.Rule.Value ( PropVal, PropVals, PropValsList )+import CssParser.Prelude+import CssParser.Show+    ( CssShow(..), Embraced(Embraced), ShowSpaceBetween(..), Csl(Csl) )++data FunArg = FunArg+  { argName :: Var+  , argType :: Maybe CssType+  , defaultValue :: Maybe PropVal+  }+  deriving (Eq, Ord, Show, Generic)++instance CssShow FunArg where+  toCssText fa =+    toCssText fa.argName <>+    maybe "" ((" type(" <> ) . (<> ")") . toCssText) fa.argType <>+    maybe "" ((" : " <> ) . toCssText) fa.defaultValue++data ConstEntry = ConstEntry Var PropValsList deriving (Show, Eq, Ord, Generic)+instance ShowSpaceBetween ConstEntry ConstEntry where+  cssSpace _ _ = ""+instance CssShow ConstEntry where+  toCssText (ConstEntry pn pv) =+    toCssText pn <> ": " <>  toCssText pv <> ";"++data Function r+  = Function+  { name :: Var+  , args :: [FunArg]+  , returns :: Maybe CssType+  , localConsts :: [ ConstEntry ]+  , result :: NonEmpty PropVals+  , atRules :: [r]+  } deriving (Eq, Ord, Show, Generic)++instance (ShowSpaceBetween r r, CssShow r) => CssShow (Function r) where+  toCssText p =+    toCssText p.name <> toCssText (Embraced (Csl p.args)) <>+    maybe "" ((" returns type(" <>) . (<> ")"). toCssText) p.returns <>+    "{" <> toCssText p.localConsts <> " result: " <> toCssText p.result <> ";" <>+    toCssText p.atRules <> "}"
+ src/CssParser/At/Import.hs view
@@ -0,0 +1,43 @@+module CssParser.At.Import where++import CssParser.At.MediaQuery ( MediaQuery )+import CssParser.At.Supports ( FeatureQuery, hasOuterParens )+import CssParser.Ident+import CssParser.Prelude+import CssParser.Rule.Value ( Source )+import CssParser.Show ( CssShow(..) )++{-+@import url;+@import url layer;+@import url layer(layer-name);+@import url layer(layer-name) supports(supports-condition);+@import url layer(layer-name) supports(supports-condition) list-of-media-queries;+@import url layer(layer-name) list-of-media-queries;+@import url supports(supports-condition);+@import url supports(supports-condition) list-of-media-queries;+@import url list-of-media-queries;+-}++data Import sl+  = ImportDefaultLayer Source+  | ImportUrlLayer     Source (Maybe LayerName) (Maybe (FeatureQuery sl)) [MediaQuery]+  | ImportUrlSupports  Source                   (Maybe (FeatureQuery sl)) [MediaQuery]+  deriving (Show, Ord, Eq, Generic)++instance CssShow sl => CssShow (Import sl) where+  toCssText = go+    where+      showMq = \case+        [] -> ""+        o -> " " <> toCssText o+      embrace v+        | hasOuterParens v = toCssText v+        | otherwise = "(" <> toCssText v <> ")"+      showSup = maybe "" ((" supports" <>) . embrace)+      go = \case+        ImportDefaultLayer u -> toCssText u <> " layer"+        ImportUrlLayer u l sup mq ->+          toCssText u <> " layer(" <> toCssText l <> ")" <> showSup sup <> showMq mq+        ImportUrlSupports u sup mq ->+          toCssText u <> showSup sup <> showMq mq
+ src/CssParser/At/Keyframe.hs view
@@ -0,0 +1,44 @@+module CssParser.At.Keyframe where++import CssParser.Descriptor (Descriptor)+import CssParser.Ident ( Ident(..) )+import CssParser.Rule.Value ( PropVals )+import CssParser.Rule.TypedNum+import CssParser.Prelude+import CssParser.Show ( CssShow(..), ShowSpaceBetween(..), CslNe )++data KeyframeAdr+  = KeyframePercentAdr RawNum+  | KeyframeStart+  | KeyframeEnd+  deriving (Show, Eq, Ord, Generic)++instance CssShow KeyframeAdr where+  toCssText = \case+    KeyframePercentAdr p -> toCssText $ TypedNum p Percent+    KeyframeStart -> "from"+    KeyframeEnd -> "to"++data PropEntry = PropEntry Descriptor PropVals deriving (Show, Eq, Ord, Generic)+instance ShowSpaceBetween PropEntry PropEntry where+  cssSpace _ _ = ""+instance CssShow PropEntry where+  toCssText (PropEntry pn pv) =+    toCssText pn <> toCssText pv <> ";"++data Keyframe = Keyframe (CslNe KeyframeAdr) [PropEntry] deriving (Show, Eq, Ord, Generic)++instance CssShow Keyframe where+  toCssText (Keyframe kfa ps) =+    toCssText kfa <> " {" <> unwords (toCssText <$> ps) <> "}"+instance ShowSpaceBetween Keyframe Keyframe where+  cssSpace _ _ = " "+newtype KeyframeSetName = KeyframeSetName Ident deriving newtype (Show, Eq, Ord, CssShow, IsString) deriving (Generic)++data KeyframeSet+  = KeyframeSet KeyframeSetName [Keyframe]+  deriving (Show, Eq, Ord, Generic)++instance CssShow KeyframeSet where+  toCssText (KeyframeSet kfsn frames) =+    toCssText kfsn <> " {" <> toCssText frames <> "}"
+ src/CssParser/At/MediaQuery.hs view
@@ -0,0 +1,182 @@+module CssParser.At.MediaQuery where++import CssParser.Ident ( PropertyName )+import CssParser.Prelude+import CssParser.Rule.Value+import CssParser.Show+    ( CssShow(..), ShowParenthesis(..), ShowSpaceBetween(..) )+import Expression.Reorder++data MediaType+  = AllMt+  | Print+  | Screen+  | Tty+  | Tv+  | Projection+  | Handheld+  | Braille+  | Embossed+  | Aural+  | Speech+  deriving (Eq, Show, Ord, Enum, Bounded, Generic)++instance CssShow MediaType where+  toCssText = \case+    AllMt -> "all"+    Print -> "print"+    Screen -> "screen"+    Tty -> "tty"+    Tv -> "tv"+    Projection -> "projection"+    Handheld -> "handheld"+    Braille -> "braille"+    Embossed -> "embossed"+    Aural -> "aural"+    Speech -> "speech"++data MtModifier = MtNot | MtOnly deriving (Show, Eq, Ord, Enum, Bounded, Generic)++instance CssShow MtModifier where+  toCssText = \case+    MtNot -> "not "+    MtOnly -> "only "++newtype MediaQueryList = MediaQueryList [MediaQuery] deriving (Show, Eq, Ord, Generic)++instance ShowSpaceBetween MediaQuery MediaQuery where+  cssSpace _ _ = ", "++instance CssShow MediaQueryList where+  toCssText (MediaQueryList mqs) = toCssText mqs++data MediaQuery+  = MediaQueryConditionOnly MediaCondition+  | MediaQueryWithMt (Maybe MtModifier) MediaType (Maybe MediaCondition)+  deriving (Show, Eq, Ord, Generic)++instance CssShow MediaQuery where+  toCssText = \case+    MediaQueryConditionOnly mbe -> toCssText mbe+    MediaQueryWithMt mtMod mt mbe ->+      toCssText mtMod <>+      toCssText mt <>+      maybe "" ((" and " <>) . toCssText) mbe++data BinOp = Or | And deriving (Show, Eq, Ord, Generic)++instance CssShow BinOp where+  toCssText = \case+    And -> " and "+    Or -> " or "++data Not p a = Not a | AsIs a deriving (Show, Eq, Ord, Generic)++instance (ShowParenthesis p a, CssShow a) => CssShow (Not p a) where+  toCssText = \case+    Not x -> "not " <> left p a <> toCssText x <> right p a+    AsIs x -> left p a <> toCssText x <> right p a++data MediaCondition where+  NotMc :: MediaCondition -> MediaCondition+  BopMc :: MediaCondition ->+             BinOp ->+             MediaCondition ->+             MediaCondition+  ParenMc :: MediaCondition -> MediaCondition+  FeatureMc :: MediaFeature -> MediaCondition+  deriving (Show, Eq, Ord, Generic)++mkBopMcTree :: BinOp -> NonEmpty MediaCondition -> MediaCondition+mkBopMcTree bop = \case+  x :| [] -> x+  x :| [y] -> BopMc x bop y+  x :| y : l -> BopMc x bop (mkBopMcTree bop $ y :| l)++instance HasFixity BinOp where+  fixityOf = \case+    Or -> Fixity AssocLeft 1+    And -> Fixity AssocLeft 2++instance SyntaxTree MediaCondition String where+  reorderChildren = \case+    BopMc l op r -> BopMc <$> reorder l <*> pure op <*> reorder r+    ParenMc x -> ParenMc <$> reorder x+    NotMc x -> NotMc <$> reorder x+    o -> pure o+  structureOf = \case+    BopMc l op r -> NodeInfix (fixityOf op) l r (`BopMc` op)+    NotMc x -> NodePrefix 3 x NotMc+    _ -> NodeLeaf+  makeError err _ = show err++instance CssShow MediaCondition where+  toCssText = \case+    NotMc mc@FeatureMc {} -> "not " <> toCssText mc+    NotMc mc@ParenMc {} -> "not " <> toCssText mc+    NotMc mc -> "not (" <> toCssText mc <> ")"+    BopMc l op r -> showBopOperand op l <> toCssText op <> showBopOperandR r+    ParenMc mc -> "(" <> toCssText mc <> ")"+    FeatureMc mf -> "(" <> toCssText mf <> ")"++showBopOperand :: BinOp -> MediaCondition -> LText+showBopOperand pop = \case+  BopMc l cop r+    | cop /= pop -> "(" <> showBopOperand cop l <> toCssText cop <> showBopOperandR r <> ")"+    | otherwise -> showBopOperand cop l <> toCssText cop <> showBopOperandR r+  o -> toCssText o++showBopOperandR :: MediaCondition -> LText+showBopOperandR = \case+  BopMc l cop r -> "(" <> showBopOperand cop l <> toCssText cop <> showBopOperandR r <> ")"+  o -> toCssText o++instance HasParens MediaCondition where+  stripParens = \case+    x@FeatureMc {} -> x+    ParenMc x -> stripParens x+    BopMc l op r -> BopMc (stripParens l) op (stripParens r)+    NotMc x -> NotMc $ stripParens x++data MediaFeature+  = PlainMf PropertyName PropVals+  | BooleanMf PropertyName+  | OpenRangeFeature PropertyName MfRelation PropVal+  | OpenRangeFeatureFlipped PropVal MfRelation PropertyName+  | MfClosedRange PropVal MfRelation PropertyName MfRelation PropVal+  deriving (Show, Eq, Ord, Generic)++toPlainMf :: PropVals -> MediaFeature -> MediaFeature+toPlainMf pvs = \case+  BooleanMf pn -> PlainMf pn pvs+  OpenRangeFeature pn _ pv -> PlainMf pn $ PropVals (pure pv) Nothing+  OpenRangeFeatureFlipped pv _ pn -> PlainMf pn $ PropVals (pure pv) Nothing+  MfClosedRange pv _  pn _ _ -> PlainMf pn $ PropVals (pure pv) Nothing+  o -> o++instance CssShow MediaFeature where+  toCssText = \case+    PlainMf i v -> toCssText i <> ": " <> toCssText v+    BooleanMf i -> toCssText i+    OpenRangeFeature i r v ->+      toCssText i <> toCssText r <> toCssText v+    OpenRangeFeatureFlipped v r i ->+      toCssText v <> toCssText r <> toCssText i+    MfClosedRange lv lr i rr rv ->+      toCssText lv <> toCssText lr <> toCssText i <> toCssText rr <> toCssText rv++data MfRelation+  = MfEq+  | MfGt+  | MfLt+  | MfGe+  | MfLe+  deriving (Show, Eq, Ord, Enum, Bounded, Generic)++instance CssShow MfRelation where+  toCssText = \case+    MfEq -> " = "+    MfGt -> " > "+    MfLt -> " < "+    MfGe -> " >= "+    MfLe -> " <= "
+ src/CssParser/At/Page.hs view
@@ -0,0 +1,60 @@+module CssParser.At.Page where++import CssParser.Ident ( Ident(..) )+import CssParser.Prelude+import CssParser.Rule.Pseudo ( AtomicPseudoClass )+import CssParser.Show ( CssShow(..) )++data PageMargin+  = TopLeftCorner+  | TopLeft+  | TopCenter+  | TopRight+  | TopRightCorner+  | BottomLeftCorner+  | BottomLeft+  | BottomCenter+  | BottomRight+  | BottomRightCorner+  | LeftTop+  | LeftMiddle+  | LeftBottom+  | RightTop+  | RightMiddle+  | RightBottom+  deriving (Eq, Show, Ord, Generic)++instance CssShow PageMargin where+  toCssText = \case+    TopLeftCorner ->      "top-left-corner"+    TopLeft ->            "top-left"+    TopCenter ->          "top-center"+    TopRight ->           "top-right"+    TopRightCorner ->     "top-right-corner"+    BottomLeftCorner ->   "bottom-left-corner"+    BottomLeft ->         "bottom-left"+    BottomCenter ->       "bottom-center"+    BottomRight ->        "bottom-right"+    BottomRightCorner ->  "bottom-right-corner"+    LeftTop ->            "left-top"+    LeftMiddle ->         "left-middle"+    LeftBottom ->         "left-bottom"+    RightTop ->           "right-top"+    RightMiddle ->        "right-middle"+    RightBottom ->        "right-bottom"++newtype PageName = PageName Ident deriving newtype (Show, Eq, Ord, CssShow, IsString) deriving (Generic)++data PageSelector = PageSelector (Maybe PageName) [AtomicPseudoClass]+  deriving (Show, Eq, Ord, Generic)++instance CssShow PageSelector where+  toCssText (PageSelector mpn pps) =+    maybe "" toCssText mpn <> toCssText pps++newtype PageSelectorList = PageSelectorList [PageSelector]+  deriving newtype (Show, Eq, Ord) deriving (Generic)++instance CssShow PageSelectorList where+  toCssText (PageSelectorList pps) =+    unwords (toCssText <$> pps)
+ src/CssParser/At/Supports.hs view
@@ -0,0 +1,63 @@+module CssParser.At.Supports where++import CssParser.At.MediaQuery ( MediaFeature, BinOp, toPlainMf )+import CssParser.Ident ( Ident )+import CssParser.Norm ( Norm(..) )+import CssParser.Prelude+import CssParser.Rule.Value ( PropVal(IntVal), PropVals(..) )+import CssParser.Rule.TypedNum+    ( TypedNum(TypedNum), RawNum(RawNum), PropValType(K) )+import CssParser.Show ( CssShow(..) )++data FqFun s+  = FqSelectorFun s+  | FqSomeFun Ident PropVals+  deriving (Show, Eq, Ord, Generic)++instance CssShow s => CssShow (FqFun s) where+  toCssText = \case+    FqSelectorFun sl -> "selector(" <> toCssText sl <> ")"+    FqSomeFun fn args -> toCssText fn <> "(" <> toCssText args <> ")"++data FeatureQuery s+  = FqMediaFeature MediaFeature+  | FqParen (FeatureQuery s)+  | FqBop BinOp (FeatureQuery s) (FeatureQuery s)+  | FqNot (FeatureQuery s)+  | FqApp (FqFun s)+  deriving (Show, Eq, Ord, Generic)++hasOuterParens :: FeatureQuery s -> Bool+hasOuterParens = \case+    FqMediaFeature _ -> True+    FqParen _ -> True+    _ -> False++instance CssShow s => CssShow (FeatureQuery s) where+  toCssText x = case x of+    FqMediaFeature mf -> "(" <> toCssText mf <> ")"+    FqParen fq -> "(" <> toCssText fq <> ")"+    FqBop bop fql@FqBop{} fqr ->+      "(" <> toCssText fql <> ")" <> toCssText bop <> toCssText fqr+    FqBop bop fql fqr ->+      toCssText fql <> toCssText bop <> toCssText fqr+    FqNot fq@FqNot {} -> "not (" <> toCssText fq <> ")"+    FqNot fq@FqBop {} -> "not (" <> toCssText fq <> ")"+    FqNot fq -> "not " <> toCssText fq+    FqApp fn -> toCssText fn++instance Norm (FeatureQuery s) where+  normalize = \case+    FqMediaFeature mf ->+      let one = IntVal (TypedNum (RawNum "1") K) in+        FqMediaFeature $ toPlainMf (PropVals (pure one) Nothing) mf+    FqParen mf@FqMediaFeature {} -> mf+    FqParen fn@FqApp {} -> fn+    FqParen p@FqParen {} -> normalize p+    FqParen p -> FqParen $ normalize p+    FqBop bop (FqParen l) r -> FqBop bop (normalize l) (normalize r)+    FqBop bop l r -> FqBop bop (normalize l) (normalize r)+    FqNot (FqNot fq) -> normalize fq+    FqNot (FqParen fq) -> FqNot $ normalize fq+    FqNot o -> FqNot $ normalize o+    FqApp f -> FqApp f
+ src/CssParser/Descriptor.hs view
@@ -0,0 +1,1181 @@+module CssParser.Descriptor where++import CssParser.Ident ( BrowserPrefix, Ident (..), PropertyName(..), Var (..) )+import CssParser.Prelude+    ( ($),+      Bounded(..),+      Enum,+      Eq,+      Ord,+      Show,+      Generic,+      Semigroup((<>)),+      (.),+      Text )+import CssParser.Show ( CssShow(..), mkDecodingMap)+import Data.HashMap.Strict qualified as HM+import Data.Text.Lazy (dropEnd, toStrict)++data Descriptor+  = CustomDescriptor Ident+  | BrowserSpecificDescriptor BrowserPrefix Ident+  | KnownDescriptor KnownDescriptor+  deriving (Eq, Show, Ord, Generic)++data KnownDescriptor+  = AccentColorT+  | AlignContentT+  | AlignItemsT+  | AlignmentBaselineT+  | AlignSelfT+  | AllT+  | AnchorNameT+  | AnchorScopeT+  | AnimationCompositionT+  | AnimationDelayT+  | AnimationDirectionT+  | AnimationDurationT+  | AnimationFillModeT+  | AnimationIterationCountT+  | AnimationNameT+  | AnimationPlayStateT+  | AnimationRangeEndT+  | AnimationRangeStartT+  | AnimationRangeT+  | AnimationT+  | AnimationTimelineT+  | AnimationTimingFunctionT+  | AnyHoverT+  | AnyPointerT+  | AppearanceT+  | AspectRatioT+  | BackdropFilterT+  | BackfaceVisibilityT+  | BackgroundAttachmentT+  | BackgroundBlendModeT+  | BackgroundClipT+  | BackgroundColorT+  | BackgroundImageT+  | BackgroundOriginT+  | BackgroundPositionT+  | BackgroundPositionXT+  | BackgroundPositionYT+  | BackgroundRepeatT+  | BackgroundRepeatXT+  | BackgroundRepeatYT+  | BackgroundSizeT+  | BackgroundT+  | BaselineShiftT+  | BaselineSourceT+  | BasePaletteT+  | BlockSizeT+  | BorderBlockColorT+  | BorderBlockEndColorT+  | BorderBlockEndStyleT+  | BorderBlockEndT+  | BorderBlockEndWidthT+  | BorderBlockStartColorT+  | BorderBlockStartStyleT+  | BorderBlockStartT+  | BorderBlockStartWidthT+  | BorderBlockStyleT+  | BorderBlockT+  | BorderBlockWidthT+  | BorderBottomColorT+  | BorderBottomLeftRadiusT+  | BorderBottomRightRadiusT+  | BorderBottomStyleT+  | BorderBottomT+  | BorderBottomWidthT+  | BorderCollapseT+  | BorderColorT+  | BorderEndEndRadiusT+  | BorderEndStartRadiusT+  | BorderImageOutsetT+  | BorderImageRepeatT+  | BorderImageSliceT+  | BorderImageSourceT+  | BorderImageT+  | BorderImageWidthT+  | BorderInlineColorT+  | BorderInlineEndColorT+  | BorderInlineEndStyleT+  | BorderInlineEndT+  | BorderInlineEndWidthT+  | BorderInlineStartColorT+  | BorderInlineStartStyleT+  | BorderInlineStartT+  | BorderInlineStartWidthT+  | BorderInlineStyleT+  | BorderInlineT+  | BorderInlineWidthT+  | BorderLeftColorT+  | BorderLeftStyleT+  | BorderLeftT+  | BorderLeftWidthT+  | BorderRadiusT+  | BorderRightColorT+  | BorderRightStyleT+  | BorderRightT+  | BorderRightWidthT+  | BorderSpacingT+  | BorderStartEndRadiusT+  | BorderStartStartRadiusT+  | BorderStyleT+  | BorderT+  | BorderTopColorT+  | BorderTopLeftRadiusT+  | BorderTopRightRadiusT+  | BorderTopStyleT+  | BorderTopT+  | BorderTopWidthT+  | BorderWidthT+  | BottomT+  | BoxAlignT+  | BoxDecorationBreakT+  | BoxDirectionT+  | BoxFlexGroupT+  | BoxFlexT+  | BoxLinesT+  | BoxOrdinalGroupT+  | BoxOrientT+  | BoxPackT+  | BoxShadowT+  | BoxSizingT+  | BreakAfterT+  | BreakBeforeT+  | BreakInsideT+  | CaptionSideT+  | CaretAnimationT+  | CaretColorT+  | CaretShapeT+  | CaretT+  | ClearT+  | ClipPathT+  | ClipRuleT+  | ClipT+  | ColorAdjustT+  | ColorGamutT+  | ColorIndexT+  | ColorInterpolationFiltersT+  | ColorInterpolationT+  | ColorSchemeT+  | ColorT+  | ColumnCountT+  | ColumnFillT+  | ColumnGapT+  | ColumnHeightT+  | ColumnRuleColorT+  | ColumnRuleStyleT+  | ColumnRuleT+  | ColumnRuleWidthT+  | ColumnSpanT+  | ColumnsT+  | ColumnWidthT+  | ColumnWrapT+  | ContainerD+  | ContainerNameT+  | ContainerTypeT+  | ContainIntrinsicBlockSizeT+  | ContainIntrinsicHeightT+  | ContainIntrinsicInlineSizeT+  | ContainIntrinsicSizeT+  | ContainIntrinsicWidthT+  | ContainT+  | ContentT+  | ContentVisibilityT+  | CornerBlockEndShapeT+  | CornerBlockStartShapeT+  | CornerBottomLeftShapeT+  | CornerBottomRightShapeT+  | CornerBottomShapeT+  | CornerEndEndShapeT+  | CornerEndStartShapeT+  | CornerInlineEndShapeT+  | CornerInlineStartShapeT+  | CornerLeftShapeT+  | CornerRightShapeT+  | CornerShapeT+  | CornerStartEndShapeT+  | CornerStartStartShapeT+  | CornerTopLeftShapeT+  | CornerTopRightShapeT+  | CornerTopShapeT+  | CounterIncrementT+  | CounterResetT+  | CounterSetT+  | CursorT+  | CxT+  | CyT+  | DeviceAspectRatioT+  | DeviceHeightT+  | DevicePostureT+  | DeviceWidthT+  | DirectionT+  | DisplayModeT+  | DisplayT+  | DominantBaselineT+  | DT+  | DynamicRangeLimitT+  | DynamicRangeT+  | EmptyCellsT+  | FieldSizingT+  | FillOpacityT+  | FillRuleT+  | FillT+  | FilterT+  | FlexBasisT+  | FlexDirectionT+  | FlexFlowT+  | FlexGrowT+  | FlexShrinkT+  | FlexT+  | FlexWrapT+  | FloatT+  | FloodColorT+  | FloodOpacityT+  | FontDisplayT+  | FontFamilyT+  | FontFeatureSettingsT+  | FontKerningT+  | FontLanguageOverrideT+  | FontOpticalSizingT+  | FontPaletteT+  | FontSizeAdjustT+  | FontSizeT+  | FontSmoothT+  | FontStretchT+  | FontStyleT+  | FontSynthesisPositionT+  | FontSynthesisSmallCapsT+  | FontSynthesisStyleT+  | FontSynthesisT+  | FontSynthesisWeightT+  | FontT+  | FontVariantAlternatesT+  | FontVariantCapsT+  | FontVariantEastAsianT+  | FontVariantEmojiT+  | FontVariantLigaturesT+  | FontVariantNumericT+  | FontVariantPositionT+  | FontVariantT+  | FontVariationSettingsT+  | FontWeightT+  | FontWidthT+  | ForcedColorAdjustT+  | ForcedColorsT+  | GapT+  | GridAreaT+  | GridAutoColumnsT+  | GridAutoFlowT+  | GridAutoRowsT+  | GridColumnEndT+  | GridColumnGapT+  | GridColumnStartT+  | GridColumnT+  | GridGapT+  | GridRowEndT+  | GridRowGapT+  | GridRowStartT+  | GridRowT+  | GridT+  | GridTemplateAreasT+  | GridTemplateColumnsT+  | GridTemplateRowsT+  | GridTemplateT+  | HangingPunctuationT+  | HeightT+  | HorizontalViewportSegmentsT+  | HoverT+  | HyphenateCharacterT+  | HyphenateLimitCharsT+  | HyphensT+  | ImageOrientationT+  | ImageRenderingT+  | ImageResolutionT+  | InheritsT+  | InitialLetterT+  | InitialValueT+  | InlineSizeT+  | InsetBlockEndT+  | InsetBlockStartT+  | InsetBlockT+  | InsetInlineEndT+  | InsetInlineStartT+  | InsetInlineT+  | InsetT+  | InteractivityT+  | InterestDelayEndT+  | InterestDelayStartT+  | InterestDelayT+  | InterpolateSizeT+  | InvertedColorsT+  | IsolationT+  | JustifyContentT+  | JustifyItemsT+  | JustifySelfT+  | LeftT+  | LetterSpacingT+  | LightingColorT+  | LineBreakT+  | LineClampT+  | LineHeightStepT+  | LineHeightT+  | ListStyleImageT+  | ListStylePositionT+  | ListStyleT+  | ListStyleTypeT+  | MarginBlockEndT+  | MarginBlockStartT+  | MarginBlockT+  | MarginBottomT+  | MarginInlineEndT+  | MarginInlineStartT+  | MarginInlineT+  | MarginLeftT+  | MarginRightT+  | MarginT+  | MarginTopT+  | MarginTrimT+  | MarkerEndT+  | MarkerMidT+  | MarkerStartT+  | MarkerT+  | MaskBorderModeT+  | MaskBorderOutsetT+  | MaskBorderRepeatT+  | MaskBorderSliceT+  | MaskBorderSourceT+  | MaskBorderT+  | MaskBorderWidthT+  | MaskClipT+  | MaskCompositeT+  | MaskImageT+  | MaskModeT+  | MaskOriginT+  | MaskPositionT+  | MaskRepeatT+  | MaskSizeT+  | MaskT+  | MaskTypeT+  | MathDepthT+  | MathShiftT+  | MathStyleT+  | MaxBlockSizeT+  | MaxHeightT+  | MaxInlineSizeT+  | MaxWidthT+  | MinBlockSizeT+  | MinHeightT+  | MinInlineSizeT+  | MinWidthT+  | MixBlendModeT+  | MonochromeT+  | ObjectFitT+  | ObjectPositionT+  | ObjectViewBoxT+  | OffsetAnchorT+  | OffsetDistanceT+  | OffsetPathT+  | OffsetPositionT+  | OffsetRotateT+  | OffsetT+  | OpacityT+  | OrderT+  | OrientationT+  | OrphansT+  | OutlineColorT+  | OutlineOffsetT+  | OutlineStyleT+  | OutlineT+  | OutlineWidthT+  | OverflowAnchorT+  | OverflowBlockT+  | OverflowClipMarginT+  | OverflowInlineT+  | OverflowT+  | OverflowWrapT+  | OverflowXT+  | OverflowYT+  | OverlayT+  | OverrideColorsT+  | OverscrollBehaviorBlockT+  | OverscrollBehaviorInlineT+  | OverscrollBehaviorT+  | OverscrollBehaviorXT+  | OverscrollBehaviorYT+  | PaddingBlockEndT+  | PaddingBlockStartT+  | PaddingBlockT+  | PaddingBottomT+  | PaddingInlineEndT+  | PaddingInlineStartT+  | PaddingInlineT+  | PaddingLeftT+  | PaddingRightT+  | PaddingT+  | PaddingTopT+  | PageBreakAfterT+  | PageBreakBeforeT+  | PageBreakInsideT+  | PageD+  | PaintOrderT+  | PerspectiveOriginT+  | PerspectiveT+  | PlaceContentT+  | PlaceItemsT+  | PlaceSelfT+  | PointerEventsT+  | PointerT+  | PositionAnchorT+  | PositionAreaT+  | PositionT+  | PositionTryD+  | PositionTryFallbacksT+  | PositionTryOrderT+  | PositionVisibilityT+  | PrefersColorSchemeT+  | PrefersContrastT+  | PrefersReducedDataT+  | PrefersReducedMotionT+  | PrefersReducedTransparencyT+  | PrintColorAdjustT+  | QuotesT+  | ReadingFlowT+  | ReadingOrderT+  | ResizeT+  | ResolutionT+  | ResultT+  | RightT+  | RotateT+  | RowGapT+  | RT+  | RubyAlignT+  | RubyOverhangT+  | RubyPositionT+  | RxT+  | RyT+  | ScaleT+  | ScanT+  | ScriptingT+  | ScrollbarColorT+  | ScrollbarGutterT+  | ScrollbarWidthT+  | ScrollBehaviorT+  | ScrollInitialTargetT+  | ScrollMarginBlockEndT+  | ScrollMarginBlockStartT+  | ScrollMarginBlockT+  | ScrollMarginBottomT+  | ScrollMarginInlineEndT+  | ScrollMarginInlineStartT+  | ScrollMarginInlineT+  | ScrollMarginLeftT+  | ScrollMarginRightT+  | ScrollMarginT+  | ScrollMarginTopT+  | ScrollMarkerGroupT+  | ScrollPaddingBlockEndT+  | ScrollPaddingBlockStartT+  | ScrollPaddingBlockT+  | ScrollPaddingBottomT+  | ScrollPaddingInlineEndT+  | ScrollPaddingInlineStartT+  | ScrollPaddingInlineT+  | ScrollPaddingLeftT+  | ScrollPaddingRightT+  | ScrollPaddingT+  | ScrollPaddingTopT+  | ScrollSnapAlignT+  | ScrollSnapStopT+  | ScrollSnapTypeT+  | ScrollTargetGroupT+  | ScrollTimelineAxisT+  | ScrollTimelineNameT+  | ScrollTimelineT+  | ShapeImageThresholdT+  | ShapeMarginT+  | ShapeOutsideT+  | ShapeRenderingT+  | ShapeT+  | SpeakAsT+  | SrcT+  | StopColorT+  | StopOpacityT+  | StrokeDasharrayT+  | StrokeDashoffsetT+  | StrokeLinecapT+  | StrokeLinejoinT+  | StrokeMiterlimitT+  | StrokeOpacityT+  | StrokeT+  | StrokeWidthT+  | SyntaxT+  | TableLayoutT+  | TabSizeT+  | TextAlignLastT+  | TextAlignT+  | TextAnchorT+  | TextAutospaceT+  | TextBoxEdgeT+  | TextBoxT+  | TextBoxTrimT+  | TextCombineUprightT+  | TextDecorationColorT+  | TextDecorationInsetT+  | TextDecorationLineT+  | TextDecorationSkipInkT+  | TextDecorationSkipT+  | TextDecorationStyleT+  | TextDecorationT+  | TextDecorationThicknessT+  | TextEmphasisColorT+  | TextEmphasisPositionT+  | TextEmphasisStyleT+  | TextEmphasisT+  | TextIndentT+  | TextJustifyT+  | TextOrientationT+  | TextOverflowT+  | TextRenderingT+  | TextShadowT+  | TextSizeAdjustT+  | TextSpacingTrimT+  | TextTransformT+  | TextUnderlineOffsetT+  | TextUnderlinePositionT+  | TextWrapModeT+  | TextWrapStyleT+  | TextWrapT+  | TimelineScopeT+  | TopT+  | TouchActionT+  | TransformBoxT+  | TransformOriginT+  | TransformStyleT+  | TransformT+  | TransitionBehaviorT+  | TransitionDelayT+  | TransitionDurationT+  | TransitionPropertyT+  | TransitionT+  | TransitionTimingFunctionT+  | TranslateT+  | UnicodeBidiT+  | UnicodeRangeT+  | UpdateT+  | UserModifyT+  | UserSelectT+  | VectorEffectT+  | VerticalAlignT+  | VerticalViewportSegmentsT+  | VideoDynamicRangeT+  | ViewTimelineAxisT+  | ViewTimelineInsetT+  | ViewTimelineNameT+  | ViewTimelineT+  | ViewTransitionClassT+  | ViewTransitionNameT+  | VisibilityT+  | WhiteSpaceCollapseT+  | WhiteSpaceT+  | WidowsT+  | WidthT+  | WillChangeT+  | WordBreakT+  | WordSpacingT+  | WordWrapT+  | WritingModeT+  | XT+  | YT+  | ZIndexT+  | ZoomT+  deriving (Eq, Show, Ord, Bounded, Enum, Generic)++instance CssShow Descriptor where+  toCssText = \case+    CustomDescriptor i              -> "--" <> toCssText i <> ":"+    BrowserSpecificDescriptor bp i  -> toCssText bp <> toCssText i <> ":"+    KnownDescriptor kd              -> toCssText kd <> ":"++knownDescriptorMap :: HM.HashMap Text KnownDescriptor+knownDescriptorMap = mkDecodingMap++instance CssShow KnownDescriptor where+  toCssText = \case+    AccentColorT                    -> "accent-color"+    AlignContentT                   -> "align-content"+    AlignItemsT                     -> "align-items"+    AlignmentBaselineT              -> "alignment-baseline"+    AlignSelfT                      -> "align-self"+    AllT                            -> "all"+    AnchorNameT                     -> "anchor-name"+    AnchorScopeT                    -> "anchor-scope"+    AnimationCompositionT           -> "animation-composition"+    AnimationDelayT                 -> "animation-delay"+    AnimationDirectionT             -> "animation-direction"+    AnimationDurationT              -> "animation-duration"+    AnimationFillModeT              -> "animation-fill-mode"+    AnimationIterationCountT        -> "animation-iteration-count"+    AnimationNameT                  -> "animation-name"+    AnimationPlayStateT             -> "animation-play-state"+    AnimationRangeEndT              -> "animation-range-end"+    AnimationRangeStartT            -> "animation-range-start"+    AnimationRangeT                 -> "animation-range"+    AnimationT                      -> "animation"+    AnimationTimelineT              -> "animation-timeline"+    AnimationTimingFunctionT        -> "animation-timing-function"+    AnyHoverT                       -> "any-hover"+    AnyPointerT                     -> "any-pointer"+    AppearanceT                     -> "appearance"+    AspectRatioT                    -> "aspect-ratio"+    BackdropFilterT                 -> "backdrop-filter"+    BackfaceVisibilityT             -> "backface-visibility"+    BackgroundAttachmentT           -> "background-attachment"+    BackgroundBlendModeT            -> "background-blend-mode"+    BackgroundClipT                 -> "background-clip"+    BackgroundColorT                -> "background-color"+    BackgroundImageT                -> "background-image"+    BackgroundOriginT               -> "background-origin"+    BackgroundPositionT             -> "background-position"+    BackgroundPositionXT            -> "background-position-x"+    BackgroundPositionYT            -> "background-position-y"+    BackgroundRepeatT               -> "background-repeat"+    BackgroundRepeatXT              -> "background-repeat-x"+    BackgroundRepeatYT              -> "background-repeat-y"+    BackgroundSizeT                 -> "background-size"+    BackgroundT                     -> "background"+    BaselineShiftT                  -> "baseline-shift"+    BaselineSourceT                 -> "baseline-source"+    BasePaletteT                    -> "base-palette"+    BlockSizeT                      -> "block-size"+    BorderBlockColorT               -> "border-block-color"+    BorderBlockEndColorT            -> "border-block-end-color"+    BorderBlockEndStyleT            -> "border-block-end-style"+    BorderBlockEndT                 -> "border-block-end"+    BorderBlockEndWidthT            -> "border-block-end-width"+    BorderBlockStartColorT          -> "border-block-start-color"+    BorderBlockStartStyleT          -> "border-block-start-style"+    BorderBlockStartT               -> "border-block-start"+    BorderBlockStartWidthT          -> "border-block-start-width"+    BorderBlockStyleT               -> "border-block-style"+    BorderBlockT                    -> "border-block"+    BorderBlockWidthT               -> "border-block-width"+    BorderBottomColorT              -> "border-bottom-color"+    BorderBottomLeftRadiusT         -> "border-bottom-left-radius"+    BorderBottomRightRadiusT        -> "border-bottom-right-radius"+    BorderBottomStyleT              -> "border-bottom-style"+    BorderBottomT                   -> "border-bottom"+    BorderBottomWidthT              -> "border-bottom-width"+    BorderCollapseT                 -> "border-collapse"+    BorderColorT                    -> "border-color"+    BorderEndEndRadiusT             -> "border-end-end-radius"+    BorderEndStartRadiusT           -> "border-end-start-radius"+    BorderImageOutsetT              -> "border-image-outset"+    BorderImageRepeatT              -> "border-image-repeat"+    BorderImageSliceT               -> "border-image-slice"+    BorderImageSourceT              -> "border-image-source"+    BorderImageT                    -> "border-image"+    BorderImageWidthT               -> "border-image-width"+    BorderInlineColorT              -> "border-inline-color"+    BorderInlineEndColorT           -> "border-inline-end-color"+    BorderInlineEndStyleT           -> "border-inline-end-style"+    BorderInlineEndT                -> "border-inline-end"+    BorderInlineEndWidthT           -> "border-inline-end-width"+    BorderInlineStartColorT         -> "border-inline-start-color"+    BorderInlineStartStyleT         -> "border-inline-start-style"+    BorderInlineStartT              -> "border-inline-start"+    BorderInlineStartWidthT         -> "border-inline-start-width"+    BorderInlineStyleT              -> "border-inline-style"+    BorderInlineT                   -> "border-inline"+    BorderInlineWidthT              -> "border-inline-width"+    BorderLeftColorT                -> "border-left-color"+    BorderLeftStyleT                -> "border-left-style"+    BorderLeftT                     -> "border-left"+    BorderLeftWidthT                -> "border-left-width"+    BorderRadiusT                   -> "border-radius"+    BorderRightColorT               -> "border-right-color"+    BorderRightStyleT               -> "border-right-style"+    BorderRightT                    -> "border-right"+    BorderRightWidthT               -> "border-right-width"+    BorderSpacingT                  -> "border-spacing"+    BorderStartEndRadiusT           -> "border-start-end-radius"+    BorderStartStartRadiusT         -> "border-start-start-radius"+    BorderStyleT                    -> "border-style"+    BorderT                         -> "border"+    BorderTopColorT                 -> "border-top-color"+    BorderTopLeftRadiusT            -> "border-top-left-radius"+    BorderTopRightRadiusT           -> "border-top-right-radius"+    BorderTopStyleT                 -> "border-top-style"+    BorderTopT                      -> "border-top"+    BorderTopWidthT                 -> "border-top-width"+    BorderWidthT                    -> "border-width"+    BottomT                         -> "bottom"+    BoxAlignT                       -> "box-align"+    BoxDecorationBreakT             -> "box-decoration-break"+    BoxDirectionT                   -> "box-direction"+    BoxFlexGroupT                   -> "box-flex-group"+    BoxFlexT                        -> "box-flex"+    BoxLinesT                       -> "box-lines"+    BoxOrdinalGroupT                -> "box-ordinal-group"+    BoxOrientT                      -> "box-orient"+    BoxPackT                        -> "box-pack"+    BoxShadowT                      -> "box-shadow"+    BoxSizingT                      -> "box-sizing"+    BreakAfterT                     -> "break-after"+    BreakBeforeT                    -> "break-before"+    BreakInsideT                    -> "break-inside"+    CaptionSideT                    -> "caption-side"+    CaretAnimationT                 -> "caret-animation"+    CaretColorT                     -> "caret-color"+    CaretShapeT                     -> "caret-shape"+    CaretT                          -> "caret"+    ClearT                          -> "clear"+    ClipPathT                       -> "clip-path"+    ClipRuleT                       -> "clip-rule"+    ClipT                           -> "clip"+    ColorAdjustT                    -> "color-adjust"+    ColorGamutT                     -> "color-gamut"+    ColorIndexT                     -> "color-index"+    ColorInterpolationFiltersT      -> "color-interpolation-filters"+    ColorInterpolationT             -> "color-interpolation"+    ColorSchemeT                    -> "color-scheme"+    ColorT                          -> "color"+    ColumnCountT                    -> "column-count"+    ColumnFillT                     -> "column-fill"+    ColumnGapT                      -> "column-gap"+    ColumnHeightT                   -> "column-height"+    ColumnRuleColorT                -> "column-rule-color"+    ColumnRuleStyleT                -> "column-rule-style"+    ColumnRuleT                     -> "column-rule"+    ColumnRuleWidthT                -> "column-rule-width"+    ColumnSpanT                     -> "column-span"+    ColumnsT                        -> "columns"+    ColumnWidthT                    -> "column-width"+    ColumnWrapT                     -> "column-wrap"+    ContainerD                      -> "container"+    ContainerNameT                  -> "container-name"+    ContainerTypeT                  -> "container-type"+    ContainIntrinsicBlockSizeT      -> "contain-intrinsic-block-size"+    ContainIntrinsicHeightT         -> "contain-intrinsic-height"+    ContainIntrinsicInlineSizeT     -> "contain-intrinsic-inline-size"+    ContainIntrinsicSizeT           -> "contain-intrinsic-size"+    ContainIntrinsicWidthT          -> "contain-intrinsic-width"+    ContainT                        -> "contain"+    ContentT                        -> "content"+    ContentVisibilityT              -> "content-visibility"+    CornerBlockEndShapeT            -> "corner-block-end-shape"+    CornerBlockStartShapeT          -> "corner-block-start-shape"+    CornerBottomLeftShapeT          -> "corner-bottom-left-shape"+    CornerBottomRightShapeT         -> "corner-bottom-right-shape"+    CornerBottomShapeT              -> "corner-bottom-shape"+    CornerEndEndShapeT              -> "corner-end-end-shape"+    CornerEndStartShapeT            -> "corner-end-start-shape"+    CornerInlineEndShapeT           -> "corner-inline-end-shape"+    CornerInlineStartShapeT         -> "corner-inline-start-shape"+    CornerLeftShapeT                -> "corner-left-shape"+    CornerRightShapeT               -> "corner-right-shape"+    CornerShapeT                    -> "corner-shape"+    CornerStartEndShapeT            -> "corner-start-end-shape"+    CornerStartStartShapeT          -> "corner-start-start-shape"+    CornerTopLeftShapeT             -> "corner-top-left-shape"+    CornerTopRightShapeT            -> "corner-top-right-shape"+    CornerTopShapeT                 -> "corner-top-shape"+    CounterIncrementT               -> "counter-increment"+    CounterResetT                   -> "counter-reset"+    CounterSetT                     -> "counter-set"+    CursorT                         -> "cursor"+    CxT                             -> "cx"+    CyT                             -> "cy"+    DeviceAspectRatioT              -> "device-aspect-ratio"+    DeviceHeightT                   -> "device-height"+    DevicePostureT                  -> "device-posture"+    DeviceWidthT                    -> "device-width"+    DirectionT                      -> "direction"+    DisplayModeT                    -> "display-mode"+    DisplayT                        -> "display"+    DominantBaselineT               -> "dominant-baseline"+    DT                              -> "d"+    DynamicRangeLimitT              -> "dynamic-range-limit"+    DynamicRangeT                   -> "dynamic-range"+    EmptyCellsT                     -> "empty-cells"+    FieldSizingT                    -> "field-sizing"+    FillOpacityT                    -> "fill-opacity"+    FillRuleT                       -> "fill-rule"+    FillT                           -> "fill"+    FilterT                         -> "filter"+    FlexBasisT                      -> "flex-basis"+    FlexDirectionT                  -> "flex-direction"+    FlexFlowT                       -> "flex-flow"+    FlexGrowT                       -> "flex-grow"+    FlexShrinkT                     -> "flex-shrink"+    FlexT                           -> "flex"+    FlexWrapT                       -> "flex-wrap"+    FloatT                          -> "float"+    FloodColorT                     -> "flood-color"+    FloodOpacityT                   -> "flood-opacity"+    FontDisplayT                    -> "font-display"+    FontFamilyT                     -> "font-family"+    FontFeatureSettingsT            -> "font-feature-settings"+    FontKerningT                    -> "font-kerning"+    FontLanguageOverrideT           -> "font-language-override"+    FontOpticalSizingT              -> "font-optical-sizing"+    FontPaletteT                    -> "font-palette"+    FontSizeAdjustT                 -> "font-size-adjust"+    FontSizeT                       -> "font-size"+    FontSmoothT                     -> "font-smooth"+    FontStretchT                    -> "font-stretch"+    FontStyleT                      -> "font-style"+    FontSynthesisPositionT          -> "font-synthesis-position"+    FontSynthesisSmallCapsT         -> "font-synthesis-small-caps"+    FontSynthesisStyleT             -> "font-synthesis-style"+    FontSynthesisT                  -> "font-synthesis"+    FontSynthesisWeightT            -> "font-synthesis-weight"+    FontT                           -> "font"+    FontVariantAlternatesT          -> "font-variant-alternates"+    FontVariantCapsT                -> "font-variant-caps"+    FontVariantEastAsianT           -> "font-variant-east-asian"+    FontVariantEmojiT               -> "font-variant-emoji"+    FontVariantLigaturesT           -> "font-variant-ligatures"+    FontVariantNumericT             -> "font-variant-numeric"+    FontVariantPositionT            -> "font-variant-position"+    FontVariantT                    -> "font-variant"+    FontVariationSettingsT          -> "font-variation-settings"+    FontWeightT                     -> "font-weight"+    FontWidthT                      -> "font-width"+    ForcedColorAdjustT              -> "forced-color-adjust"+    ForcedColorsT                   -> "forced-colors"+    GapT                            -> "gap"+    GridAreaT                       -> "grid-area"+    GridAutoColumnsT                -> "grid-auto-columns"+    GridAutoFlowT                   -> "grid-auto-flow"+    GridAutoRowsT                   -> "grid-auto-rows"+    GridColumnEndT                  -> "grid-column-end"+    GridColumnGapT                  -> "grid-column-gap"+    GridColumnStartT                -> "grid-column-start"+    GridColumnT                     -> "grid-column"+    GridGapT                        -> "grid-gap"+    GridRowEndT                     -> "grid-row-end"+    GridRowGapT                     -> "grid-row-gap"+    GridRowStartT                   -> "grid-row-start"+    GridRowT                        -> "grid-row"+    GridT                           -> "grid"+    GridTemplateAreasT              -> "grid-template-areas"+    GridTemplateColumnsT            -> "grid-template-columns"+    GridTemplateRowsT               -> "grid-template-rows"+    GridTemplateT                   -> "grid-template"+    HangingPunctuationT             -> "hanging-punctuation"+    HeightT                         -> "height"+    HorizontalViewportSegmentsT     -> "horizontal-viewport-segments"+    HoverT                          -> "hover"+    HyphenateCharacterT             -> "hyphenate-character"+    HyphenateLimitCharsT            -> "hyphenate-limit-chars"+    HyphensT                        -> "hyphens"+    ImageOrientationT               -> "image-orientation"+    ImageRenderingT                 -> "image-rendering"+    ImageResolutionT                -> "image-resolution"+    InheritsT                       -> "inherits"+    InitialLetterT                  -> "initial-letter"+    InitialValueT                   -> "initial-value"+    InlineSizeT                     -> "inline-size"+    InsetBlockEndT                  -> "inset-block-end"+    InsetBlockStartT                -> "inset-block-start"+    InsetBlockT                     -> "inset-block"+    InsetInlineEndT                 -> "inset-inline-end"+    InsetInlineStartT               -> "inset-inline-start"+    InsetInlineT                    -> "inset-inline"+    InsetT                          -> "inset"+    InteractivityT                  -> "interactivity"+    InterestDelayEndT               -> "interest-delay-end"+    InterestDelayStartT             -> "interest-delay-start"+    InterestDelayT                  -> "interest-delay"+    InterpolateSizeT                -> "interpolate-size"+    InvertedColorsT                 -> "inverted-colors"+    IsolationT                      -> "isolation"+    JustifyContentT                 -> "justify-content"+    JustifyItemsT                   -> "justify-items"+    JustifySelfT                    -> "justify-self"+    LeftT                           -> "left"+    LetterSpacingT                  -> "letter-spacing"+    LightingColorT                  -> "lighting-color"+    LineBreakT                      -> "line-break"+    LineClampT                      -> "line-clamp"+    LineHeightStepT                 -> "line-height-step"+    LineHeightT                     -> "line-height"+    ListStyleImageT                 -> "list-style-image"+    ListStylePositionT              -> "list-style-position"+    ListStyleT                      -> "list-style"+    ListStyleTypeT                  -> "list-style-type"+    MarginBlockEndT                 -> "margin-block-end"+    MarginBlockStartT               -> "margin-block-start"+    MarginBlockT                    -> "margin-block"+    MarginBottomT                   -> "margin-bottom"+    MarginInlineEndT                -> "margin-inline-end"+    MarginInlineStartT              -> "margin-inline-start"+    MarginInlineT                   -> "margin-inline"+    MarginLeftT                     -> "margin-left"+    MarginRightT                    -> "margin-right"+    MarginT                         -> "margin"+    MarginTopT                      -> "margin-top"+    MarginTrimT                     -> "margin-trim"+    MarkerEndT                      -> "marker-end"+    MarkerMidT                      -> "marker-mid"+    MarkerStartT                    -> "marker-start"+    MarkerT                         -> "marker"+    MaskBorderModeT                 -> "mask-border-mode"+    MaskBorderOutsetT               -> "mask-border-outset"+    MaskBorderRepeatT               -> "mask-border-repeat"+    MaskBorderSliceT                -> "mask-border-slice"+    MaskBorderSourceT               -> "mask-border-source"+    MaskBorderT                     -> "mask-border"+    MaskBorderWidthT                -> "mask-border-width"+    MaskClipT                       -> "mask-clip"+    MaskCompositeT                  -> "mask-composite"+    MaskImageT                      -> "mask-image"+    MaskModeT                       -> "mask-mode"+    MaskOriginT                     -> "mask-origin"+    MaskPositionT                   -> "mask-position"+    MaskRepeatT                     -> "mask-repeat"+    MaskSizeT                       -> "mask-size"+    MaskT                           -> "mask"+    MaskTypeT                       -> "mask-type"+    MathDepthT                      -> "math-depth"+    MathShiftT                      -> "math-shift"+    MathStyleT                      -> "math-style"+    MaxBlockSizeT                   -> "max-block-size"+    MaxHeightT                      -> "max-height"+    MaxInlineSizeT                  -> "max-inline-size"+    MaxWidthT                       -> "max-width"+    MinBlockSizeT                   -> "min-block-size"+    MinHeightT                      -> "min-height"+    MinInlineSizeT                  -> "min-inline-size"+    MinWidthT                       -> "min-width"+    MixBlendModeT                   -> "mix-blend-mode"+    MonochromeT                     -> "monochrome"+    ObjectFitT                      -> "object-fit"+    ObjectPositionT                 -> "object-position"+    ObjectViewBoxT                  -> "object-view-box"+    OffsetAnchorT                   -> "offset-anchor"+    OffsetDistanceT                 -> "offset-distance"+    OffsetPathT                     -> "offset-path"+    OffsetPositionT                 -> "offset-position"+    OffsetRotateT                   -> "offset-rotate"+    OffsetT                         -> "offset"+    OpacityT                        -> "opacity"+    OrderT                          -> "order"+    OrientationT                    -> "orientation"+    OrphansT                        -> "orphans"+    OutlineColorT                   -> "outline-color"+    OutlineOffsetT                  -> "outline-offset"+    OutlineStyleT                   -> "outline-style"+    OutlineT                        -> "outline"+    OutlineWidthT                   -> "outline-width"+    OverflowAnchorT                 -> "overflow-anchor"+    OverflowBlockT                  -> "overflow-block"+    OverflowClipMarginT             -> "overflow-clip-margin"+    OverflowInlineT                 -> "overflow-inline"+    OverflowT                       -> "overflow"+    OverflowWrapT                   -> "overflow-wrap"+    OverflowXT                      -> "overflow-x"+    OverflowYT                      -> "overflow-y"+    OverlayT                        -> "overlay"+    OverrideColorsT                 -> "override-colors"+    OverscrollBehaviorBlockT        -> "overscroll-behavior-block"+    OverscrollBehaviorInlineT       -> "overscroll-behavior-inline"+    OverscrollBehaviorT             -> "overscroll-behavior"+    OverscrollBehaviorXT            -> "overscroll-behavior-x"+    OverscrollBehaviorYT            -> "overscroll-behavior-y"+    PaddingBlockEndT                -> "padding-block-end"+    PaddingBlockStartT              -> "padding-block-start"+    PaddingBlockT                   -> "padding-block"+    PaddingBottomT                  -> "padding-bottom"+    PaddingInlineEndT               -> "padding-inline-end"+    PaddingInlineStartT             -> "padding-inline-start"+    PaddingInlineT                  -> "padding-inline"+    PaddingLeftT                    -> "padding-left"+    PaddingRightT                   -> "padding-right"+    PaddingT                        -> "padding"+    PaddingTopT                     -> "padding-top"+    PageBreakAfterT                 -> "page-break-after"+    PageBreakBeforeT                -> "page-break-before"+    PageBreakInsideT                -> "page-break-inside"+    PageD                           -> "page"+    PaintOrderT                     -> "paint-order"+    PerspectiveOriginT              -> "perspective-origin"+    PerspectiveT                    -> "perspective"+    PlaceContentT                   -> "place-content"+    PlaceItemsT                     -> "place-items"+    PlaceSelfT                      -> "place-self"+    PointerEventsT                  -> "pointer-events"+    PointerT                        -> "pointer"+    PositionAnchorT                 -> "position-anchor"+    PositionAreaT                   -> "position-area"+    PositionT                       -> "position"+    PositionTryD                    -> "position-try"+    PositionTryFallbacksT           -> "position-try-fallbacks"+    PositionTryOrderT               -> "position-try-order"+    PositionVisibilityT             -> "position-visibility"+    PrefersColorSchemeT             -> "prefers-color-scheme"+    PrefersContrastT                -> "prefers-contrast"+    PrefersReducedDataT             -> "prefers-reduced-data"+    PrefersReducedMotionT           -> "prefers-reduced-motion"+    PrefersReducedTransparencyT     -> "prefers-reduced-transparency"+    PrintColorAdjustT               -> "print-color-adjust"+    QuotesT                         -> "quotes"+    ReadingFlowT                    -> "reading-flow"+    ReadingOrderT                   -> "reading-order"+    ResizeT                         -> "resize"+    ResolutionT                     -> "resolution"+    ResultT                         -> "result"+    RightT                          -> "right"+    RotateT                         -> "rotate"+    RowGapT                         -> "row-gap"+    RT                              -> "r"+    RubyAlignT                      -> "ruby-align"+    RubyOverhangT                   -> "ruby-overhang"+    RubyPositionT                   -> "ruby-position"+    RxT                             -> "rx"+    RyT                             -> "ry"+    ScaleT                          -> "scale"+    ScanT                           -> "scan"+    ScriptingT                      -> "scripting"+    ScrollbarColorT                 -> "scrollbar-color"+    ScrollbarGutterT                -> "scrollbar-gutter"+    ScrollbarWidthT                 -> "scrollbar-width"+    ScrollBehaviorT                 -> "scroll-behavior"+    ScrollInitialTargetT            -> "scroll-initial-target"+    ScrollMarginBlockEndT           -> "scroll-margin-block-end"+    ScrollMarginBlockStartT         -> "scroll-margin-block-start"+    ScrollMarginBlockT              -> "scroll-margin-block"+    ScrollMarginBottomT             -> "scroll-margin-bottom"+    ScrollMarginInlineEndT          -> "scroll-margin-inline-end"+    ScrollMarginInlineStartT        -> "scroll-margin-inline-start"+    ScrollMarginInlineT             -> "scroll-margin-inline"+    ScrollMarginLeftT               -> "scroll-margin-left"+    ScrollMarginRightT              -> "scroll-margin-right"+    ScrollMarginT                   -> "scroll-margin"+    ScrollMarginTopT                -> "scroll-margin-top"+    ScrollMarkerGroupT              -> "scroll-marker-group"+    ScrollPaddingBlockEndT          -> "scroll-padding-block-end"+    ScrollPaddingBlockStartT        -> "scroll-padding-block-start"+    ScrollPaddingBlockT             -> "scroll-padding-block"+    ScrollPaddingBottomT            -> "scroll-padding-bottom"+    ScrollPaddingInlineEndT         -> "scroll-padding-inline-end"+    ScrollPaddingInlineStartT       -> "scroll-padding-inline-start"+    ScrollPaddingInlineT            -> "scroll-padding-inline"+    ScrollPaddingLeftT              -> "scroll-padding-left"+    ScrollPaddingRightT             -> "scroll-padding-right"+    ScrollPaddingT                  -> "scroll-padding"+    ScrollPaddingTopT               -> "scroll-padding-top"+    ScrollSnapAlignT                -> "scroll-snap-align"+    ScrollSnapStopT                 -> "scroll-snap-stop"+    ScrollSnapTypeT                 -> "scroll-snap-type"+    ScrollTargetGroupT              -> "scroll-target-group"+    ScrollTimelineAxisT             -> "scroll-timeline-axis"+    ScrollTimelineNameT             -> "scroll-timeline-name"+    ScrollTimelineT                 -> "scroll-timeline"+    ShapeImageThresholdT            -> "shape-image-threshold"+    ShapeMarginT                    -> "shape-margin"+    ShapeOutsideT                   -> "shape-outside"+    ShapeRenderingT                 -> "shape-rendering"+    ShapeT                          -> "shape"+    SpeakAsT                        -> "speak-as"+    SrcT                            -> "src"+    StopColorT                      -> "stop-color"+    StopOpacityT                    -> "stop-opacity"+    StrokeDasharrayT                -> "stroke-dasharray"+    StrokeDashoffsetT               -> "stroke-dashoffset"+    StrokeLinecapT                  -> "stroke-linecap"+    StrokeLinejoinT                 -> "stroke-linejoin"+    StrokeMiterlimitT               -> "stroke-miterlimit"+    StrokeOpacityT                  -> "stroke-opacity"+    StrokeT                         -> "stroke"+    StrokeWidthT                    -> "stroke-width"+    SyntaxT                         -> "syntax"+    TableLayoutT                    -> "table-layout"+    TabSizeT                        -> "tab-size"+    TextAlignLastT                  -> "text-align-last"+    TextAlignT                      -> "text-align"+    TextAnchorT                     -> "text-anchor"+    TextAutospaceT                  -> "text-autospace"+    TextBoxEdgeT                    -> "text-box-edge"+    TextBoxT                        -> "text-box"+    TextBoxTrimT                    -> "text-box-trim"+    TextCombineUprightT             -> "text-combine-upright"+    TextDecorationColorT            -> "text-decoration-color"+    TextDecorationInsetT            -> "text-decoration-inset"+    TextDecorationLineT             -> "text-decoration-line"+    TextDecorationSkipInkT          -> "text-decoration-skip-ink"+    TextDecorationSkipT             -> "text-decoration-skip"+    TextDecorationStyleT            -> "text-decoration-style"+    TextDecorationT                 -> "text-decoration"+    TextDecorationThicknessT        -> "text-decoration-thickness"+    TextEmphasisColorT              -> "text-emphasis-color"+    TextEmphasisPositionT           -> "text-emphasis-position"+    TextEmphasisStyleT              -> "text-emphasis-style"+    TextEmphasisT                   -> "text-emphasis"+    TextIndentT                     -> "text-indent"+    TextJustifyT                    -> "text-justify"+    TextOrientationT                -> "text-orientation"+    TextOverflowT                   -> "text-overflow"+    TextRenderingT                  -> "text-rendering"+    TextShadowT                     -> "text-shadow"+    TextSizeAdjustT                 -> "text-size-adjust"+    TextSpacingTrimT                -> "text-spacing-trim"+    TextTransformT                  -> "text-transform"+    TextUnderlineOffsetT            -> "text-underline-offset"+    TextUnderlinePositionT          -> "text-underline-position"+    TextWrapModeT                   -> "text-wrap-mode"+    TextWrapStyleT                  -> "text-wrap-style"+    TextWrapT                       -> "text-wrap"+    TimelineScopeT                  -> "timeline-scope"+    TopT                            -> "top"+    TouchActionT                    -> "touch-action"+    TransformBoxT                   -> "transform-box"+    TransformOriginT                -> "transform-origin"+    TransformStyleT                 -> "transform-style"+    TransformT                      -> "transform"+    TransitionBehaviorT             -> "transition-behavior"+    TransitionDelayT                -> "transition-delay"+    TransitionDurationT             -> "transition-duration"+    TransitionPropertyT             -> "transition-property"+    TransitionT                     -> "transition"+    TransitionTimingFunctionT       -> "transition-timing-function"+    TranslateT                      -> "translate"+    UnicodeBidiT                    -> "unicode-bidi"+    UnicodeRangeT                   -> "unicode-range"+    UpdateT                         -> "update"+    UserModifyT                     -> "user-modify"+    UserSelectT                     -> "user-select"+    VectorEffectT                   -> "vector-effect"+    VerticalAlignT                  -> "vertical-align"+    VerticalViewportSegmentsT       -> "vertical-viewport-segments"+    VideoDynamicRangeT              -> "video-dynamic-range"+    ViewTimelineAxisT               -> "view-timeline-axis"+    ViewTimelineInsetT              -> "view-timeline-inset"+    ViewTimelineNameT               -> "view-timeline-name"+    ViewTimelineT                   -> "view-timeline"+    ViewTransitionClassT            -> "view-transition-class"+    ViewTransitionNameT             -> "view-transition-name"+    VisibilityT                     -> "visibility"+    WhiteSpaceCollapseT             -> "white-space-collapse"+    WhiteSpaceT                     -> "white-space"+    WidowsT                         -> "widows"+    WidthT                          -> "width"+    WillChangeT                     -> "will-change"+    WordBreakT                      -> "word-break"+    WordSpacingT                    -> "word-spacing"+    WordWrapT                       -> "word-wrap"+    WritingModeT                    -> "writing-mode"+    XT                              -> "x"+    YT                              -> "y"+    ZIndexT                         -> "z-index"+    ZoomT                           -> "zoom"++toPropertyName :: Descriptor -> PropertyName+toPropertyName = \case+  CustomDescriptor i -> VarProp $ Var i+  bsd@BrowserSpecificDescriptor{} ->+    PropertyName . Ident . toStrict . dropEnd 1 $ toCssText bsd+  o -> PropertyName . Ident . toStrict . dropEnd 1 $ toCssText o
+ src/CssParser/File.hs view
@@ -0,0 +1,11 @@+module CssParser.File where++import CssParser.Prelude+import CssParser.Rule ( CssRule )+import CssParser.Rule.Show ()+import CssParser.Show ( CssShow(..) )++newtype CssFile = CssFile { rules :: [ CssRule ] } deriving (Show, Eq, Generic)++instance CssShow CssFile where+  toCssText cf = unlines (toCssText <$> cf.rules)
+ src/CssParser/FixRule.hs view
@@ -0,0 +1,124 @@+-- | Functions for Rule types helping with Happy gramma disambiguation+module CssParser.FixRule where++import Control.Monad ( foldM )+import CssParser.Descriptor (Descriptor (CustomDescriptor, KnownDescriptor), KnownDescriptor (ResultT))+import CssParser.At.Function ( ConstEntry(..) )+import CssParser.Ident+import CssParser.Prelude+import CssParser.Parser.Monad ( P(Failed), reorderInP, SyntaxTree )+import CssParser.Rule+import CssParser.Show ( CssShow(toCssText), toCssStr )+import CssParser.Rule.TypedNum+    ( TypedNum(TypedNum), RawNum(RawNum) )+import CssParser.Rule.Value+    ( CalcExpr(BinOpCe, ValCe, CalcNeg),+      CalcOp(PlusCe, MinusCe),+      Important,+      stripParens,+      PropVal(IdentRef),+      PropVals(..),+      PropValsList(PropValsList), HasParens )+import CssParser.Rule.Pseudo (AtomicPseudoClass (UnknownPc), BrowserSpecificIdent (..))+import CssParser.Rule.Show ()+import Data.Text qualified as T+import Data.Text.Lazy qualified as L+++nullTagSelector :: TagSelector+nullTagSelector = TagSelector NoBar NoTag []++identOnly :: PropertyName -> (Ident -> a) -> P a+identOnly pn f = identOnlyP pn (pure . f)++identOnlyP :: PropertyName -> (Ident -> P a) -> P a+identOnlyP (PropertyName i) f = f i+identOnlyP (VarProp i) _ = Failed $ "Var " <> show i <> " is not expected"++varOrResult :: Descriptor -> NonEmpty PropVals -> P (Either ConstEntry (NonEmpty PropVals))+varOrResult d pvs =+  case d of+    KnownDescriptor ResultT -> pure $ Right pvs+    CustomDescriptor i -> pure . Left $ ConstEntry (Var i) (PropValsList pvs)+    o -> Failed $ "Expected var or result but got " <> toCssStr o++findResult :: [ Either ConstEntry (NonEmpty PropVals) ] -> P (NonEmpty PropVals, [ConstEntry])+findResult l =+  foldM go (Nothing, []) l >>= \case+    (Nothing, _) -> Failed "Function body do not have result: descriptor"+    (Just r, ces) -> pure (r, reverse ces)+  where+    go b a =+      case (b, a) of+       ((Just r, _), Right secondR) ->+         Failed $ "Multiple result descriptors with values: " <>+           toCssStr r <> " and " <> toCssStr secondR+       ((Nothing, ces), Right r) ->+         pure (Just r, ces)+       ((r, ces), Left ce) ->+         pure (r, ce : ces)+++specificDescOnlyP :: Descriptor -> Descriptor -> P ()+specificDescOnlyP e g+  | e == g = pure ()+  | otherwise =+    Failed $ "Expected " <> toCssStr e <> " but got " <> toCssStr g++pclassToIdent :: AtomicPseudoClass -> Ident+pclassToIdent = Ident . T.drop 1 . L.toStrict . toCssText++prependPropVal :: PropVal -> PropVals -> PropVals+prependPropVal pv (PropVals pvs mi) = PropVals (pv <| pvs) mi++pclassToPropVal :: AtomicPseudoClass -> PropVal+pclassToPropVal pc = IdentRef (pclassToIdent pc)++pclassToPropVals :: Maybe Important-> AtomicPseudoClass -> PropVals+pclassToPropVals mi pc = PropVals (pclassToPropVal pc :| []) mi++mkCustomSelector :: AtomicPseudoClass -> NonEmpty Selector -> P AtRule+mkCustomSelector pc sel =+  case pc of+    UnknownPc (BrowserSpecificIdent i) -> pure $ CustomSelector (CustomSelectorName i) sel+    o -> Failed $ "Expected custom pseudo class but got: " <> toCssStr o++mkLeaf :: Descriptor -> NonEmpty PropVals -> CssRuleBodyItem+mkLeaf pn = \case+  (x :| []) -> CssLeafRule pn x+  o -> CssEnumLeaf pn (PropValsList o)++massageExpr :: (Show b, HasParens b, SyntaxTree b String) => (b -> a) -> b -> P a+massageExpr f e = fmap (f . stripParens) (reorderInP e)++chopOffLeftmostSign :: CalcExpr -> Maybe (CalcOp, CalcExpr)+chopOffLeftmostSign = \case+  BinOpCe a op b -> do+    case chopOffLeftmostSign a of+      Nothing -> Nothing+      Just (lop, a') -> Just (lop, BinOpCe a' op b)+  ValCe (TypedNum (RawNum rn) pt) ->+    case T.uncons rn of+      Just ('-', absRn) -> pure (MinusCe, ValCe (TypedNum (RawNum absRn) pt))+      Just ('+', absRn) -> pure (PlusCe, ValCe (TypedNum (RawNum absRn) pt))+      _ -> Nothing+  _ -> Nothing++recoverCalcBinOp :: CalcExpr -> CalcExpr -> P CalcExpr+recoverCalcBinOp fo = \case+  CalcNeg x ->+    pure $ BinOpCe fo MinusCe x+  so ->+    case chopOffLeftmostSign so of+      Nothing ->+        fail $ "Expected operator between " <> toCssStr fo <> " and " <> toCssStr so+      Just (lop, so') ->+        pure $ BinOpCe fo lop so'++atrCaseSensetivity :: Ident -> P CaseSensetivity+atrCaseSensetivity = \case+ Ident "i" -> pure CaseInsensetive+ Ident "I" -> pure CaseInsensetive+ Ident "s" -> pure CaseSensetive+ Ident "S" -> pure CaseSensetive+ o -> Failed $ "Expected i or s for case sensetivity but got: " <> toCssStr o
+ src/CssParser/Ident.hs view
@@ -0,0 +1,125 @@+module CssParser.Ident where++import CssParser.Prelude+import CssParser.Show+import CssParser.Utils ( encodeIdentifier )+import Data.Text (pack)++data BrowserPrefix+  = Moz+  | Na+  | Opera+  | WebKit+  | Apple+  | Microsoft+  deriving (Eq, Ord, Show, Bounded, Enum, Generic)++bpLength :: BrowserPrefix -> Int+bpLength = \case+   Moz -> 5+   Na -> 0+   Opera -> 3+   WebKit -> 8+   Apple -> 7+   Microsoft -> 4++instance CssShow BrowserPrefix where+  toCssText = \case+    Moz -> "-moz-"+    Na -> ""+    Opera -> "-o-"+    Apple -> "-apple-"+    WebKit -> "-webkit-"+    Microsoft -> "-ms-"++newtype Ident = Ident Text+  deriving newtype (Eq, Ord, Show, Semigroup, Monoid, IsString)+  deriving (Generic)++instance GEnum Ident where+  genum = [Ident "a"]++data TagName+  = TagName Ident+  | AmpersandTag+  | AsteriskTag+  | NoTag+  deriving (Eq, Ord, Show, Generic)++data Namespace+  = NoBar+  | NoNs+  | AsteriskNs+  | Namespace Ident+  deriving (Eq, Ord, Show, Generic)++data AttrName+  = AttrName+  { attrNs :: Namespace+  , attrName :: Ident+  } deriving (Eq, Ord, Show, Generic)++instance CssShow AttrName where+  toCssText (AttrName n (Ident e)) = toCssText n <> encodeIdentifier e++data CaseSensetivity+  = CaseSensetive+  | CaseInsensetive+  deriving (Eq, Ord, Show, Generic)++instance CssShow CaseSensetivity where+  toCssText = \case+    CaseSensetive -> " s"+    CaseInsensetive -> " i"++newtype Var = Var Ident deriving newtype (Show, Eq, Ord, IsString) deriving (Generic)++instance CssShow Var where+  toCssText (Var i) = "--" <> toCssText i++data PropertyName+  = PropertyName Ident+  | VarProp Var+  deriving  (Eq, Ord, Show, Generic)++instance IsString PropertyName where+  fromString ('-':'-':v) = VarProp (Var . Ident $ pack v)+  fromString o = PropertyName . Ident $ pack o++instance CssShow PropertyName where+  toCssText = \case+    PropertyName i -> toCssText i+    VarProp v -> toCssText v++instance CssShow Namespace where+  toCssText = \case+    NoBar -> ""+    NoNs  -> "|"+    AsteriskNs -> "*|"+    Namespace (Ident t) -> encodeIdentifier t <> "|"++instance CssShow TagName where+  toCssText = \case+    NoTag -> ""+    AsteriskTag -> "*"+    AmpersandTag -> "&"+    TagName (Ident lt) -> fromStrict lt++instance CssShow Ident where+  toCssText (Ident i) = encodeIdentifier i++newtype LayerName = LayerName Ident deriving newtype (Show, Eq, Ord, CssShow, IsString) deriving (Generic)++instance ShowSpaceBetween LayerName LayerName where+  cssSpace _ _ = ", "++newtype Charset = Charset Text deriving newtype (Show, Eq, Ord, IsString) deriving (Generic)++instance CssShow Charset where+  toCssText (Charset cs) = encodeStringLiteral cs++newtype CustomSelectorName = CustomSelectorName Ident+  deriving (Show, Ord, Eq, Generic)++instance CssShow CustomSelectorName where+  toCssText (CustomSelectorName i) = ":" <> toCssText i
+ src/CssParser/Lexer.x view
@@ -0,0 +1,299 @@+{+module CssParser.Lexer where++import CssParser.At.MediaQuery (MediaType(..))+import CssParser.At.Page+import CssParser.Descriptor+import CssParser.Ident qualified as I+import CssParser.Lexer.Token+import CssParser.Prelude hiding (Space)+import CssParser.Rule hiding (Heading, Host)+import CssParser.Rule.Pseudo hiding (Left, Right, ViewTransition)+import CssParser.Rule.Pseudo qualified as P+import CssParser.Rule.Type qualified as F+import CssParser.Rule.TypedNum (PropValType(..))+import CssParser.Rule.Value (Ratio(..), readRatio)+import CssParser.Show (smartLookup)+import CssParser.TextMarshal+import CssParser.Utils(readCssString, readIdentifier)+import Data.Text (pack)+}++%wrapper "monadUserState"++$nonascii = [^\0-\x9f]+$w        = [\ \t\r\n\f]+$tl       = [\~]+$pm       = [\-\+]+@nl       = \r|\n|\r\n|\f+@unicode  = \\[0-9a-fA-F]{1,6}(\r\n|[ \n\r\t\f])?+@escape   = @unicode | \\[^\n\r\f0-9a-fA-F]+@wo = $w*+@nonaesc = $nonascii | @escape+@nmstart = [_a-zA-Z] | @nonaesc+@nmchar  = [_\-a-zA-Z0-9] | @nonaesc++@name    = @nmchar++@dec     = [0-9]+@int     = @dec++@uint    = @dec++@hexdig  = [0-9a-fA-F]+@updig  = [0-9a-fA-F\?]+@hexdigs = @hexdig++@string1 = \'([^\n\r\f\\\'] | \\@nl | @nonaesc )*\'   -- strings with single quote+@string2 = \"([^\n\r\f\\\"] | \\@nl | @nonaesc )*\"   -- strings with double quotes+@string  = @string1 | @string2++@a       = a|A+@b       = b|B+@c       = c|C+@d       = d|D+@e       = e|E+@f       = f|F+@g       = g|G+@h       = h|H+@i       = i|I+@j       = j|J+@k       = k|K+@l       = l|L+@m       = m|M+@n       = n|N+@o       = o|O+@p       = p|P+@q       = q|Q+@r       = r|R+@s       = s|S+@t       = t|T+@u       = u|U+@v       = v|V+@w       = w|W+@x       = x|X+@y       = y|Y+@z       = z|Z++@moz     = "-" @m@o@z "-"+@webkit  = "-" @w@e@b@k@i@t "-"+@ms      = "-" @m@s "-"+@apple   = "-" @a@p@p@l@e "-"+@opera   = "-" @o "-"++@browserPrefix = [\-](@m@o@z|@w@e@b@k@i@t|@m@s|@o|@a@p@p@l@e)[\-]+@ident   = @browserPrefix? @nmstart @nmchar*+@attrName = @nmstart @nmchar*++@anum    = [\-\+]? ( @dec+ ([\.]@dec+)? (@e [\-\+]? @dec+)? | [\.]@dec+ )+@var     = [\-][\-]+@selector = @s@e@l@e@c@t@o@r+@keyframes = @k@e@y@f@r@a@m@e@s+@charset = @c@h@a@r@s@e@t+@import  = @i@m@p@o@r@t+@style   = @s@t@y@l@e+@counter = @c@o@u@n@t@e@r+@namespace = @n@a@m@e@s@p@a@c@e+@property = @p@r@o@p@e@r@t@y+@not     = @n@o@t+@and     = @a@n@d+@or      = @o@r+@only    = @o@n@l@y+@url     = @u@r@l+@cmo     = \/\*+@cmc     = \*\/+@psc     = [:]+@pse     = [:][:]+@lang    = [A-Za-z\-]+++tokens :-+ <0> {+  \\ "0"                                               ;+  @wo "=" @wo                                          { constoken TEqual }+  @wo ","  @wo                                         { constoken Comma }+  (@wo ";" @wo)+                                       { constoken Semicolon }++  "!" @wo @i@m@p@o@r@t@a@n@t                           { constoken ImportantT }+  "@"                                                  { constoken (AtT I.Na) }+  "@" @moz                                             { constoken (AtT I.Moz) }+  "@" @ms                                              { constoken (AtT I.Microsoft) }+  "@" @apple                                           { constoken (AtT I.Apple) }+  "@" @opera                                           { constoken (AtT I.Opera) }+  "@" @webkit                                          { constoken (AtT I.WebKit) }++  @selector "("                                        { constoken SelectorFunT }+  @t@y@p@e "("                                         { constoken TypeFunT }+  @a@t@t@r "("                                         { constAndBegin AttrFunT attr_fun_st }+  @url "("                                             { constoken UrlT }+  @url "(" [^\"\'\)]* ")"                              { tokenize (UnquotedUrlT . readUnquotedUrl) }+  "."                                                  { constoken Dot }+  "." @ident                                           { tokenize (ClassT . readIdentifier . drop 1) }+  "*"                                                  { constoken Asterisk }+  "&"                                                  { constoken Ampersand }+  "|"                                                  { constoken Pipe }+  @wo "/"                                              { constoken DivT }+  @ident                                               { tokenizeDescriptor }+  @string                                              { tokenize (String . readCssString) }+  @u "+" ("?" | "1")? ("?" | "0")? @updig{1,4} ("-" ("?" | "1")? ("?" | "0")? @updig{1,4})?+                                                       { tokenize (UnicodeRangeVal . drop 2) }+  @var @name                                           { tokenize (Var . readIdentifier . drop 2) }+  "#"                                                  { constoken SharpT }+  "#" @name                                            { tokenize (THash . readIdentifier . drop 1) }++  @anum ([a-zA-Z]+ | "%")?                             { tokenize TypedNum }++  @uint "/" @uint                                      { tokenize2 ((pure . RatioT) <=< readRatio) }+  "+"                                                  { constoken Plus }+  "-"                                                  { constoken Minus }++  @wo "<" [a-zA-Z\-]+ ">"                              { tokenizeE SyntaxTypeT (F.readSyntaxType . dropWhile isSpace) }+  @wo "<" @wo                                          { constoken Less }+  @wo ">" @wo                                          { constoken Greater }+  @wo ">=" @wo                                         { constoken GreaterEqual }+  @wo "<=" @wo                                         { constoken LessEqual }+  @wo $tl @wo                                          { constoken Tilde }+  "[" @wo                                              { constAndBegin BOpen attr_st }+  @wo "{" @wo                                          { constoken COpen }+  @wo "}" @wo                                          { constoken CClose }++  @pse [a-zA-Z0-9_\-]+                                 { tokenize (tokenizePseudoElement) }++  @psc @l@a@n@g "("                                    { constAndBegin TLang lang_state }+  @psc @h@e@a@d@i@n@g                                  { constoken THeading }+  @psc @h@o@s@t                                        { constoken THost }+  @psc @s@t@a@t@e "("                                  { constoken TState }++  @psc [a-zA-Z0-9_\-]+                                 { tokenize (tokenizePseudoClass) }++  @wo ")"                                              { constoken TClose }+  "("                                                  { constoken TOpen }++  @psc @wo                                             { constoken Colon }+  $w @wo                                               { constoken Space }+  @wo @cmo                                             { begin comment }+  "<!--"                                               { begin htmlComment }++  "--" @name @psc                                      { tokenize readCustomDescriptor }+  @moz @name @psc                                      { tokenize (readBpDescriptor I.Moz) }+  @ms @name @psc                                       { tokenize (readBpDescriptor I.Microsoft) }+  @apple @name @psc                                    { tokenize (readBpDescriptor I.Apple) }+  @opera @name @psc                                    { tokenize (readBpDescriptor I.Opera) }+  @webkit @name @psc                                   { tokenize (readBpDescriptor I.WebKit) }+ }+ <comment> {+  [.\n]                                                ;+  @cmc                                                 { begin start }+ }+ <attr_fun_st> {+  @r@a@w "-" @s@t@r@i@n@g                              { constoken RawStringT }+  @attrName                                            { tokenize (IdentT . pack . readIdentifier) }+  "%"                                                  { constoken PercentT }+  @wo ")"                                              { constAndBegin TClose start }+  "*"                                                  { constoken Asterisk }+  "|"                                                  { constoken Pipe }+  $w @wo                                               { constoken Space }+  @t@y@p@e "("                                         { constAndBegin TypeFunT start }+  @wo ","  @wo                                         { constAndBegin Comma start }+ }+ <attr_st> {+  @attrName                                            { tokenize (IdentT . pack . readIdentifier) }+  @wo "]"                                              { constAndBegin BClose start }+  "*"                                                  { constoken Asterisk }+  "|"                                                  { constoken Pipe }+  @wo "=" @wo                                          { constAndBegin TEqual attr_pat_st }+  @wo "~=" @wo                                         { constAndBegin TIncludes attr_pat_st }+  @wo "|=" @wo                                         { constAndBegin TDashMatch attr_pat_st }+  @wo "^=" @wo                                         { constAndBegin TPrefixMatch attr_pat_st }+  @wo "$=" @wo                                         { constAndBegin TSuffixMatch attr_pat_st }+  @wo "*=" @wo                                         { constAndBegin TSubstringMatch attr_pat_st }+  @wo @cmo                                             { begin comment }+  "<!--"                                               { begin htmlComment }+ }+ <attr_pat_st> {+  @string                                              { tokenize (String . readCssString) }+  @name                                                { tokenize (AttrPatT . pack . readIdentifier) }+  $w @wo                                               { constoken Space }+  @wo @cmo                                             { begin comment }+  "<!--"                                               { begin htmlComment }+  @wo "]"                                              { constAndBegin BClose start }+ }+ <htmlComment> {+  [.\n]                                                ;+  "-->"                                                { begin start }+ }+ <lang_state> {+  @lang                                                { tokenize String }+  $w @wo                                               { skip }+  ")"                                                  { constAndBegin TClose start }+ }++{+data TokenLoc = TokenLoc Token String (Maybe AlexPosn) deriving (Show, Eq)++getToken :: TokenLoc -> Token+getToken (TokenLoc t _ _) = t++type AlexUserState = ()++tokenizeE :: (a -> Token) -> (String -> Either String a) -> AlexInput -> Int -> Alex TokenLoc+tokenizeE f fe (p, _, _, str) len =+  case fe str' of+    Left err ->+      alexError ("FAILED " <> err <> "; len: " <> show len <> "; str = [" <> str <> "]")+    Right v ->+      pure (TokenLoc (f v) str' (Just p))+  where str' = take len str++tokenize :: (String -> Token) -> AlexInput -> Int -> Alex TokenLoc+tokenize f (p, _, _, str) len = pure (TokenLoc (f str') str' (Just p))+  where str' = take len str++moveRightBy :: AlexPosn -> Int -> AlexPosn+moveRightBy (AlexPn a l c) n = AlexPn (a+n) l (c+n)++tokenizeDescriptor :: AlexInput -> Int -> Alex TokenLoc+tokenizeDescriptor (pos, _pc, _bs, cis) len =+  case splitAt len cis of+    (i, ':':cis') ->+      let it = pack i in+        case smartLookup it knownDescriptorMap of+          Just d -> do+            alexSetInput (pos `moveRightBy` (len + 1), ':', [], cis')+            pure (TokenLoc (DescriptorT $ KnownDescriptor d) i (Just pos))+          Nothing -> pure (TokenLoc (IdentT . pack $ readIdentifier i) i (Just pos))+    (i, _) ->+      let it = pack i in+        case smartLookup it descriptorKeywords of+          Just d ->+            pure (TokenLoc d i (Just pos))+          Nothing ->+            pure (TokenLoc (IdentT . pack $ readIdentifier i) i (Just pos))++tokenize2 :: (String -> Either String Token) -> AlexInput -> Int -> Alex TokenLoc+tokenize2 f (p, _, _, str) len =+  case f str' of+    Left err ->+      alexError ("FAILED " <> err <> "; len: " <> show len <> "; str = [" <> str <> "]")+    Right v ->+      pure (TokenLoc v str' (Just p))+  where+    str' = take len str++constoken :: Token -> AlexInput -> Int -> Alex TokenLoc+constoken = tokenize . const++constAndBegin :: Token -> Int -> AlexInput -> Int -> Alex TokenLoc+constAndBegin = andBegin . constoken++start :: Int+start = 0++alexInitUserState :: AlexUserState+alexInitUserState = ()++alexEOF :: Alex TokenLoc+alexEOF = pure (TokenLoc undefined "" Nothing)++alexScanTokens :: String -> Either String [TokenLoc]+alexScanTokens str = runAlex str loop+  where loop :: Alex [TokenLoc]+        loop = alexMonadScan >>= p+        p (TokenLoc _ _ Nothing) = pure []+        p toc = (toc:) <$> loop+}
+ src/CssParser/Lexer/Token.hs view
@@ -0,0 +1,274 @@+module CssParser.Lexer.Token where++import CssParser.At.MediaQuery (MediaType(..))+import CssParser.At.Page ( PageMargin(..) )+import CssParser.Descriptor (Descriptor (BrowserSpecificDescriptor, CustomDescriptor))+import CssParser.Ident ( BrowserPrefix, Ident(Ident), bpLength )+import CssParser.Prelude+import CssParser.Rule.Pseudo+    ( AtomicPseudoClass(UnknownPc),+      BrowserSpecificIdent(BrowserSpecificIdent),+      PseudoElement(UnknownPe, After, Before) )+import CssParser.Rule.Type ( AtomicCssType )+import CssParser.Rule.TypedNum ( NumberStr )+import CssParser.Rule.Value+    ( CalcFns(CalcFn, MinFn, MaxFn, ClampFn), Ratio )+import CssParser.Show ( mkDecodingMap', smartLookup )+import CssParser.Utils ( readIdentifier )+import Data.Text (pack)+import Data.HashMap.Strict qualified as HM++data Token+  = AlphaT+  | Ampersand+  | AndT+  | Asterisk+  | AtomicPseudoClassT AtomicPseudoClass+  | AtT BrowserPrefix+  | AttrFunT+  | AttrPatT Text+  | BClose+  | BOpen+  | CalcFunT CalcFns+  | CClose+  | CharsetT+  | ClassT String+  | Colon+  | ColorProfileT+  | Comma+  | ContainerT+  | CustomMediaT+  | CustomSelectorT+  | COpen+  | CounterStyleT+  | DescriptorT Descriptor+  | DivT+  | Dot+  | EvenT+  | FalseT+  | FontFaceT+  | FontFeatureValuesT+  | FontPaletteValuesT+  | FromT+  | FunctionT+  | GlobalT+  | Greater+  | GreaterEqual+  | IdentT Text+  | ImportantT+  | ImportT+  | KeyframesT+  | LayerT+  | Less+  | LessEqual+  | MediaT+  | MediaTypeT MediaType+  | MixinT+  | DefineMixinT+  | Minus+  | NamespaceT+  | NotT+  | OddT+  | OfT+  | OnlyT+  | OrT+  | PageMarginT PageMargin+  | PageT+  | PercentT+  | Pipe+  | Plus+  | PositionTryT+  | PropertyT+  | PseudoElementT PseudoElement+  | RatioT Ratio+  | RawStringT+  | ReturnsT+  | ScopeT+  | SelectorFunT+  | Semicolon+  | SharpT+  | Space+  | StartingStyleT+  | String String+  | SupportsT+  | SyntaxTypeT AtomicCssType+  | TActiveViewTransitionType+  | TClose+  | TDashMatch+  | TDir+  | TEqual+  | THas+  | THash String+  | THeading+  | THighlight+  | THost+  | Tilde+  | TIncludes+  | TInt Int+  | TIs+  | TLang+  | TNot+  | TOpen+  | ToT+  | TPart+  | TPicker+  | TPrefixMatch+  | TrueT+  | TScrollButton+  | TSlotted+  | TState+  | TSubstringMatch+  | TSuffixMatch+  | TViewTransitionGroup+  | TViewTransitionImagePair+  | TViewTransitionNew+  | TViewTransitionOld+  | TWhere+  | TypedNum NumberStr+  | TypeFunT+  | UnicodeRangeVal String+  | UnquotedUrlT String+  | UrlT+  | Var String+  | ViewTransitionT++  | NthChildT+  | NthOfTypeT+  | NthLastChildT+  | NthLastOfTypeT++  deriving (Show, Eq)++pseudoClassMap :: HashMap Text Token+pseudoClassMap = auto <> hand+  where+    auto = fmap AtomicPseudoClassT . mkDecodingMap' $ drop 1 genum+    hand =+      HM.fromList+      [ (":after", PseudoElementT After)+      , (":before", PseudoElementT Before)+      , (":global", GlobalT)+      , (":not", TNot)+      , (":where", TWhere)+      , (":has", THas)+      , (":is", TIs)+      , (":active-view-transition-type", TActiveViewTransitionType)+      , (":dir", TDir)+      , (":state", TState)+      , (":nth-child",         NthChildT     )+      , (":nth-of-type",       NthOfTypeT    )+      , (":nth-last-child",    NthLastChildT )+      , (":nth-last-of-type",  NthLastOfTypeT)+      ]++tokenizePseudoClass :: String -> Token+tokenizePseudoClass s =+  case smartLookup (pack s) pseudoClassMap of+    Just pc -> pc+    Nothing ->+      AtomicPseudoClassT . UnknownPc . BrowserSpecificIdent . Ident . pack $ drop 1 s++pseudoElementMap :: HashMap Text Token+pseudoElementMap = auto <> hand+  where+    auto = fmap PseudoElementT . mkDecodingMap' $ drop 1 genum+    hand =+      HM.fromList+      [ ("::highlight", THighlight)+      , ("::part", TPart)+      , ("::picker", TPicker)+      , ("::scroll-button", TScrollButton)+      , ("::slotted", TSlotted)+      , ("::view-transition-group", TViewTransitionGroup)+      , ("::view-transition-image-pair", TViewTransitionImagePair)+      , ("::view-transition-new", TViewTransitionNew)+      , ("::view-transition-old", TViewTransitionOld)+      ]++tokenizePseudoElement :: String -> Token+tokenizePseudoElement s =+  case smartLookup (pack s) pseudoElementMap of+    Just pe -> pe+    Nothing ->+      PseudoElementT . UnknownPe . Ident . pack $ drop 2 s++readCustomDescriptor :: String -> Token+readCustomDescriptor =+  DescriptorT . CustomDescriptor . Ident . pack . readIdentifier . dropEnd 1 . drop 2++readBpDescriptor :: BrowserPrefix -> String -> Token+readBpDescriptor bp =+  DescriptorT . BrowserSpecificDescriptor bp . Ident . pack . readIdentifier . dropEnd 1 . drop (bpLength bp)++descriptorKeywords :: HashMap Text Token+descriptorKeywords =+  HM.fromList+  [ ("all"                                            , MediaTypeT AllMt)+  , ("alpha"                                          , AlphaT)+  , ("aural"                                          , MediaTypeT Aural)+  , ("bottom-center"                                  , PageMarginT BottomCenter)+  , ("bottom-left"                                    , PageMarginT BottomLeft)+  , ("bottom-left-corner"                             , PageMarginT BottomLeftCorner)+  , ("bottom-right"                                   , PageMarginT BottomRight)+  , ("bottom-right-corner"                            , PageMarginT BottomRightCorner)+  , ("braille"                                        , MediaTypeT Braille)+  , ("color-profile"                                  , ColorProfileT)+  , ("container"                                      , ContainerT)+  , ("custom-media"                                   , CustomMediaT)+  , ("custom-selector"                                , CustomSelectorT)+  , ("embossed"                                       , MediaTypeT Embossed)+  , ("false"                                          , FalseT)+  , ("font-face"                                      , FontFaceT)+  , ("font-feature-values"                            , FontFeatureValuesT)+  , ("font-palette-values"                            , FontPaletteValuesT)+  , ("from"                                           , FromT)+  , ("function"                                       , FunctionT)+  , ("handheld"                                       , MediaTypeT Handheld)+  , ("layer"                                          , LayerT)+  , ("left-bottom"                                    , PageMarginT LeftBottom)+  , ("left-middle"                                    , PageMarginT LeftMiddle)+  , ("left-top"                                       , PageMarginT LeftTop)+  , ("media"                                          , MediaT)+  , ("mixin"                                          , MixinT)+  , ("define-mixin"                                   , DefineMixinT)+  , ("of"                                             , OfT)+  , ("even"                                           , EvenT)+  , ("odd"                                            , OddT)+  , ("page"                                           , PageT)+  , ("position-try"                                   , PositionTryT)+  , ("print"                                          , MediaTypeT Print)+  , ("projection"                                     , MediaTypeT Projection)+  , ("property"                                       , PropertyT)+  , ("charset"                                        , CharsetT)+  , ("namespace"                                      , NamespaceT)+  , ("counter-style"                                  , CounterStyleT)+  , ("import"                                         , ImportT)+  , ("only"                                           , OnlyT)+  , ("not"                                            , NotT)+  , ("and"                                            , AndT)+  , ("or"                                             , OrT)+  , ("keyframes"                                      , KeyframesT)+  , ("returns"                                        , ReturnsT)+  , ("right-bottom"                                   , PageMarginT RightBottom)+  , ("right-middle"                                   , PageMarginT RightMiddle)+  , ("right-top"                                      , PageMarginT RightTop)+  , ("scope"                                          , ScopeT)+  , ("screen"                                         , MediaTypeT Screen)+  , ("speech"                                         , MediaTypeT Speech)+  , ("starting-style"                                 , StartingStyleT)+  , ("supports"                                       , SupportsT)+  , ("min"                                            , CalcFunT MinFn)+  , ("max"                                            , CalcFunT MaxFn)+  , ("clamp"                                          , CalcFunT ClampFn)+  , ("calc"                                           , CalcFunT CalcFn)+  , ("to"                                             , ToT)+  , ("top-center"                                     , PageMarginT TopCenter)+  , ("top-left"                                       , PageMarginT TopLeft)+  , ("top-left-corner"                                , PageMarginT TopLeftCorner)+  , ("top-right"                                      , PageMarginT TopRight)+  , ("top-right-corner"                               , PageMarginT TopRightCorner)+  , ("true"                                           , TrueT)+  , ("tty"                                            , MediaTypeT Tty)+  , ("tv"                                             , MediaTypeT Tv)+  , ("view-transition"                                , ViewTransitionT)+  ]
+ src/CssParser/List.hs view
@@ -0,0 +1,26 @@+module CssParser.List where++import Control.Arrow (first)+import Data.List.NonEmpty (NonEmpty (..))+import Prelude++unsnocNe :: NonEmpty a -> ([a], a)+unsnocNe (x :| xs) = go x xs+  where+    go y [] = ([], y)+    go y (z : zs) = let ~(ws, w) = go z zs in (y : ws, w)++dropEnd :: Int -> [a] -> [a]+dropEnd i xs+  | i <= 0 = xs+  | otherwise = f xs (drop i xs)+  where+    f (x:xs') (_y:ys) = x : f xs' ys+    f _ _             = []++_initLast :: [a] -> Maybe ([a], a)+_initLast [] = Nothing+_initLast (a : as) = Just (go as a)+  where+    go [] x = ([], x)+    go (y : ys) x = first (x :) (go ys y)
+ src/CssParser/MonoPair.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE UndecidableInstances #-}+module CssParser.MonoPair where++import CssParser.Prelude+import CssParser.Show++data MonoPair a+  = EmptyPair+  | HalfPair a+  | FullPair a a+  deriving (Show, Ord, Eq, Generic)++instance+  ( ShowSpaceBetween (MonoPair a) a+  , ShowParenthesis (MonoPair a) a+  , CssShow a+  ) => CssShow (MonoPair a) where+  toCssText = \case+    EmptyPair -> ""+    HalfPair x ->  wrap (toCssText x)+    FullPair x y -> wrap (toCssText x <> cssSpace (MonoPair a) a <> toCssText y)+    where+      wrap x = left (MonoPair a) a <> x <> right (MonoPair a) a
+ src/CssParser/Norm.hs view
@@ -0,0 +1,71 @@+module CssParser.Norm where++import CssParser.Prelude+import GHC.Generics++class GNorm f where+  gnorm :: f a -> f a++instance GNorm V1 where+  gnorm x = case x of {}+instance GNorm U1 where+  gnorm U1 = U1+instance (Norm c) => GNorm (K1 i c) where+  gnorm (K1 a) = K1 $ normalize a+instance (GNorm a, Constructor c) => GNorm (M1 C c a) where+  gnorm (M1 x) = M1 $ gnorm x+instance (GNorm a, Selector s) => GNorm (M1 S s a) where+  gnorm (M1 x) = M1 $ gnorm x+instance (GNorm a) => GNorm (M1 D d a) where+  gnorm (M1 x) = M1 $ gnorm x+instance (GNorm a, GNorm b) => GNorm (a :+: b) where+  gnorm (L1 x) = L1 $ gnorm x+  gnorm (R1 x) = R1 $ gnorm x+instance (GNorm a, GNorm b) => GNorm (a :*: b) where+  gnorm (a :*: b) = gnorm a :*: gnorm b+instance GNorm UChar where+  gnorm = id+instance GNorm UDouble where+  gnorm = id+instance GNorm UFloat where+  gnorm = id+instance GNorm UInt where+  gnorm = id+instance GNorm UWord where+  gnorm = id++class Norm a where+  normalize :: a -> a+  default normalize :: (Generic a, GNorm (Rep a)) => a -> a+  normalize = gNormalizeDefault++gNormalizeDefault :: (Generic a, GNorm (Rep a)) => a -> a+gNormalizeDefault = to . gnorm . from++instance Norm a => Norm [a] where+  normalize = fmap normalize++instance Norm a => Norm (Maybe a) where+  normalize = fmap normalize++instance Norm a => Norm (NonEmpty a) where+  normalize = fmap normalize++instance Norm () where+  normalize = id++instance Norm Text where+  normalize = id++instance Norm LText where+  normalize = id++instance Norm Integer where+  normalize = id++normUntilConst :: (Norm a, Eq a) => a -> a+normUntilConst a =+  let a' = normalize a in+    if a' == a+    then a'+    else normUntilConst a'
+ src/CssParser/Parser.y view
@@ -0,0 +1,803 @@+{+module CssParser.Parser where++import CssParser.At.Container+import CssParser.At.CustomMedia+import CssParser.At.FontFeatureValues+import CssParser.At.FontPaletteValues+import CssParser.At.Function qualified as F+import CssParser.At.Import+import CssParser.At.Keyframe+import CssParser.At.MediaQuery+import CssParser.At.Page+import CssParser.At.Supports hiding (FeatureQuery)+import CssParser.Descriptor (Descriptor, toPropertyName)+import CssParser.File+import CssParser.FixRule+import CssParser.Ident hiding (Ident, Namespace, Var)+import CssParser.Ident qualified as R+import CssParser.Lexer (AlexPosn(AlexPn), TokenLoc(TokenLoc))+import CssParser.Lexer.Token qualified as L+import CssParser.Lexer.Token+  ( Token+    ( TIncludes, TEqual, TDashMatch, TPrefixMatch, TSuffixMatch, TSubstringMatch, IdentT+    , Comma, Plus, SharpT, Minus, Tilde, Dot, Asterisk, Space, BOpen, BClose+    , PseudoElementT, TInt, TNot, TLang, String, THash+    , COpen, CClose, Colon, Semicolon, Var, Pipe, AtomicPseudoClassT, Ampersand+    , CharsetT, ImportT, MediaT, LayerT, NamespaceT, CounterStyleT, PropertyT+    , NotT, OfT, OrT, AndT, OnlyT, ReturnsT, GlobalT, RawStringT, DefineMixinT+    , TOpen, TClose, DescriptorT, ClassT, AttrPatT, PercentT, MixinT+    , Greater, Less, LessEqual, GreaterEqual, AttrFunT, AlphaT+    , RatioT, ImportantT, MediaTypeT, CalcFunT, TypeFunT, FunctionT, SyntaxTypeT+    , UrlT, UnquotedUrlT, TWhere, THas, TIs, PageT, PageMarginT, CustomSelectorT+    , KeyframesT, ColorProfileT, FontFaceT, UnicodeRangeVal, CustomMediaT, TrueT, FalseT+    , FontFeatureValuesT, AtT, FontPaletteValuesT, ContainerT, DivT, PositionTryT+    , StartingStyleT, ViewTransitionT, ScopeT, ToT, FromT, SupportsT, SelectorFunT+    , TActiveViewTransitionType, TDir, THeading, THost, TState, EvenT, OddT+    , THighlight, TPart, TPicker, TScrollButton, TSlotted, TViewTransitionGroup+    , TViewTransitionImagePair, TViewTransitionNew, TViewTransitionOld+    , NthChildT, NthOfTypeT, NthLastChildT, NthLastOfTypeT+    )+  )+import CssParser.MonoPair+import CssParser.Norm+import CssParser.Parser.Monad+import CssParser.Prelude+  ( mapMaybe, prependList, NonEmpty((:|)), (<|), leftToMaybe+  , rightToMaybe, These(..), fromMaybe+  )+import CssParser.Rule+import CssParser.Rule.Pseudo qualified as P+import CssParser.Rule.Pseudo hiding (Left, Right, ViewTransition, Heading, Host)+import CssParser.Rule.Type qualified as T+import CssParser.Rule.TypedNum+import CssParser.Rule.Value hiding (UnicodeRangeVal)+import CssParser.Rule.Value qualified as Vl+import CssParser.Show+import Data.Text (Text, pack)+import Data.Text.Lazy (toStrict)+import Prelude+}++%monad { P } { thenP } { returnP }+%name cssParser+%tokentype { TokenLoc }+%error { happyError }++%token+    ','         { TokenLoc Comma _ _ }+    ':'         { TokenLoc Colon _ _ }+    ';'         { TokenLoc Semicolon _ _ }+    '>'         { TokenLoc Greater _ _ }+    '>='        { TokenLoc GreaterEqual _ _ }+    '<'         { TokenLoc Less _ _ }+    '<='        { TokenLoc LessEqual _ _ }+    '+'         { TokenLoc Plus _ _ }+    '%'         { TokenLoc PercentT _ _ }+    '#'         { TokenLoc SharpT _ _ }+    '-'         { TokenLoc Minus _ _ }+    '|'         { TokenLoc Pipe _ _ }+    '~'         { TokenLoc Tilde _ _ }+    '.'         { TokenLoc Dot _ _ }+    ' '         { TokenLoc Space _ _ }+    '*'         { TokenLoc Asterisk _ _ }+    '&'         { TokenLoc Ampersand _ _ }+    '['         { TokenLoc BOpen _ _ }+    ']'         { TokenLoc BClose _ _ }+    '{'         { TokenLoc COpen _ _ }+    '}'         { TokenLoc CClose _ _ }+    '='         { TokenLoc TEqual _ _ }+    attrPat     { TokenLoc (AttrPatT $$) _ _ }+    mediaType   { TokenLoc (MediaTypeT $$) _ _ }+    charset     { TokenLoc CharsetT _ _ }+    alpha       { TokenLoc AlphaT _ _ }+    '@'         { TokenLoc (AtT $$) _ _ }+    important   { TokenLoc ImportantT _ _ }+    supports    { TokenLoc SupportsT _ _ }++    returns     { TokenLoc ReturnsT _ _ }+    viewTransition+                { TokenLoc ViewTransitionT _ _ }+    startingStyle+                { TokenLoc StartingStyleT _ _ }+    positionTry { TokenLoc PositionTryT _ _ }+    fontPaletteValues+                { TokenLoc FontPaletteValuesT _ _ }+    fontFeatureValues+                { TokenLoc FontFeatureValuesT _ _ }+    'to'        { TokenLoc ToT _ _ }+    from        { TokenLoc FromT _ _ }+    scope       { TokenLoc ScopeT _ _ }+    container   { TokenLoc ContainerT _ _ }+    property    { TokenLoc PropertyT _ _ }+    colorProfile+                { TokenLoc ColorProfileT _ _ }+    namespace   { TokenLoc NamespaceT _ _ }+    keyframes   { TokenLoc KeyframesT _ _ }+    counterStyle+                { TokenLoc CounterStyleT _ _ }+    fontFace    { TokenLoc FontFaceT _ _ }+    import      { TokenLoc ImportT _ _ }+    layer       { TokenLoc LayerT _ _ }+    page        { TokenLoc PageT _ _ }+    pageMargin  { TokenLoc (PageMarginT $$) _ _ }+    media       { TokenLoc MediaT _ _ }+    mixin       { TokenLoc MixinT _ _ }+    defineMixin { TokenLoc DefineMixinT _ _ }+    true        { TokenLoc TrueT _ _ }+    false       { TokenLoc FalseT _ _ }+    customMedia { TokenLoc CustomMediaT _ _ }+    customSelector+                { TokenLoc CustomSelectorT _ _ }+    only        { TokenLoc OnlyT _ _ }+    'not'       { TokenLoc NotT _ _ }+    or          { TokenLoc OrT _ _ }+    of          { TokenLoc OfT _ _ }+    and         { TokenLoc AndT _ _ }+    'url('      { TokenLoc UrlT _ _ }+    'uqUrl'     { TokenLoc (UnquotedUrlT $$) _ _ }+    'selector(' { TokenLoc SelectorFunT _ _ }+    calcFns     { TokenLoc (CalcFunT $$) _ _ }+    'attr('     { TokenLoc AttrFunT _ _ }+    rawString   { TokenLoc RawStringT _ _ }+    'type('     { TokenLoc TypeFunT _ _ }+    function    { TokenLoc FunctionT _ _ }+    syntaxType  { TokenLoc (SyntaxTypeT $$) _ _ }+    '^='        { TokenLoc TPrefixMatch _ _ }+    '$='        { TokenLoc TSuffixMatch _ _ }+    '*='        { TokenLoc TSubstringMatch _ _ }+    '|='        { TokenLoc TDashMatch _ _ }+    '~='        { TokenLoc TIncludes _ _ }+    unRangeVal  { TokenLoc (UnicodeRangeVal $$) _ _ }+    class       { TokenLoc (ClassT $$) _ _ }+    ident       { TokenLoc (IdentT $$) _ _ }+    string      { TokenLoc (String $$) _ _ }+    hash        { TokenLoc (THash $$) _ _ }+    descriptor  { TokenLoc (DescriptorT $$) _ _ }+    pseude      { TokenLoc (PseudoElementT $$) _ _ }+    highlight   { TokenLoc THighlight _ _ }+    part        { TokenLoc TPart _ _ }+    picker      { TokenLoc TPicker _ _ }+    scrollButton+                { TokenLoc  TScrollButton _ _ }+    slotted     { TokenLoc  TSlotted _ _ }+    viewTransitionGroup+                { TokenLoc TViewTransitionGroup _ _ }+    viewTransitionImagePair+                { TokenLoc TViewTransitionImagePair _ _ }+    viewTransitionNew+                { TokenLoc TViewTransitionNew _ _ }+    viewTransitionOld+                { TokenLoc TViewTransitionOld _ _ }+    even        { TokenLoc EvenT _ _ }+    odd         { TokenLoc OddT _ _ }+    nthChild    { TokenLoc NthChildT _ _ }+    nthLastChild+                { TokenLoc NthLastChildT _ _ }+    nthOfType   { TokenLoc NthOfTypeT _ _ }+    nthLastOfType+                { TokenLoc NthLastOfTypeT _ _ }+    pseudc      { TokenLoc (AtomicPseudoClassT $$) _ _ }+    int         { TokenLoc (TInt $$) _ _ }+    'ratio'     { TokenLoc (RatioT $$) _ _ }+    typedNum    { TokenLoc (L.TypedNum $$) _ _ }+    var         { TokenLoc (Var $$) _ _ }+    ':not'      { TokenLoc TNot _ _ }+    ':global'   { TokenLoc GlobalT _ _ }+    ':where'    { TokenLoc TWhere _ _ }+    ':is'       { TokenLoc TIs _ _ }+    ':has'      { TokenLoc THas _ _ }+    ':lang('    { TokenLoc TLang _ _ }+    ':dir'      { TokenLoc TDir _ _ }+    heading     { TokenLoc THeading _ _ }+    host        { TokenLoc THost _ _ }+    ':state('   { TokenLoc TState _ _ }+    activeViewTransitionType+                { TokenLoc TActiveViewTransitionType _ _ }+    '('         { TokenLoc TOpen _ _ }+    ')'         { TokenLoc TClose _ _ }+    '/'         { TokenLoc DivT _ _ }++%%++CssFile :: { CssFile }+    : CssFileBody                                 { CssFile $1 }+Import -- :: { Import SelectorList }+    : Source Os                                   { ImportUrlSupports $1 Nothing [] }+    | Source Os layer Os                          { ImportDefaultLayer $1 }+    | Source Os layer LayerNameMb Os              { ImportUrlLayer $1 $4 Nothing [] }+    | Source Os layer LayerNameMb Os Supports Os  { ImportUrlLayer $1 $4 (Just $6) [] }+    | Source Os layer LayerNameMb Os Supports Os MediaQueryList Os+                                                  { ImportUrlLayer $1 $4 (Just $6) $8 }+    | Source Os layer LayerNameMb Os MediaQueryList Os+                                                  { ImportUrlLayer $1 $4 Nothing $6 }+    | Source Os Supports Os                       { ImportUrlSupports $1 (Just $3) [] }+    | Source Os Supports Os MediaQueryList Os     { ImportUrlSupports $1 (Just $3) $5 }+    | Source Os MediaQueryList Os                 { ImportUrlSupports $1 Nothing $3 }+LayerNameMb :: { Maybe LayerName }+    : P(Maybe(LayerName))                         { $1 }+Supports :: { FeatureQuery }+    : supports Op MediaFeature ')'                { FqMediaFeature $3 }+    | supports Op FeatureQuery ')'                { normalize $3 }+Source+    : Url                                         { UrlSource $1 }+    | Str                                         { StrSource $1 }+LayerNames :: { [LayerName] }+    : LayerName                                   { [$1] }+    | LayerName ',' LayerNames                    { $1 : $3 }+LayerName :: { LayerName }+    : Os IdKwd                                    { LayerName $2 }+CssFileBody :: { [ CssRule ] }+    :                                             { [] }+    | Os CssRule Os CssFileBody                   { $2 : $4 }+CssRule :: { CssRule }+    : SelectorList ERB                            { CssRule $1 $2 }+    | '@' AtRule                                  { AtRule $1 $2 }+AtRule :: { AtRule }+    : media Os '{' OsCssRuleBody '}'              { MediaRule (MediaQueryList []) $4 }+    | media Os MediaQueryList ERB                 { MediaRule (MediaQueryList $3) $4 }+    | customMedia Os Var Os CustomMediaQuery ';'  { CustomMedia $3 $5 }+    | namespace Os IdKwdMb Os Source ';'          { Namespace $3 $5 }+    | import Os Import ';'                        { ImportStmt $3 }+    | mixin Os IdKwd ';'                          { Mixin $3 }+    | defineMixin Os IdKwd ERB                    { DefineMixin $3 $4 }+    | customSelector Os pseudc Os SelectorList ';'+                                                  {% mkCustomSelector $3 $5 }+    | charset Os Str ';'                          { CharsetStmt (Charset $3) }+    | layer ' ' LayerName ';'                     { LayerStmt (pure $3) }+    | layer ' ' LayerName ',' LayerNames Os ';'   { LayerStmt ($3 :| $5) }+    | layer ' ' LayerName ERB                     { LayerBlock (Just $3) $4 }+    | layer ' ' '{' OsCssRuleBody '}'             { LayerBlock Nothing $4 }+    | layer '{' OsCssRuleBody '}'                 { LayerBlock Nothing $3 }+    | page Os '{' OsCssRuleBody '}'               { Page (PageSelectorList []) $4 }+    | page Os PageSelectorList ERB                { Page (PageSelectorList $3) $4 }+    | pageMargin ERB                              { PageMarginBlock $1 $2 }+    | counterStyle Os IdKwd ERB                   { CounterStyle $3 $4 }+    | property Os Var ERB                         { Property $3 $4 }+    | keyframes Os IdKwd Ocb SepList(Os, Keyframe) '}'+                                                  { Keyframes (KeyframeSet (KeyframeSetName $3) $5) }+    | colorProfile Os PropN Os ERB                { ColorProfile $3 $5 }+    | fontFace Os ERB                             { FontFaceBlock $3 }+    | fontFeatureValues ' ' StrEitherIds Os Ocb FontFeatureValBlocks '}'+                                                  { FontFeatureValuesBlock+                                                      (FontFeatureValues+                                                        $3+                                                        (mapMaybe leftToMaybe $6)+                                                        (mapMaybe rightToMaybe $6))+                                                  }+    | fontPaletteValues ' ' Var Os Ocb PropEntries '}'+                                                  { FontPaletteValuesBlock (FontPaletteValues $3 $6) }+    | container Os ContainerQueryMap ERB          { Container (ContainerQueryMap $3) $4 }+    | positionTry Os Var Ocb PropEntries '}'      { PositionTry $3 $5 }+    | startingStyle Os ERB                        { StartingStyle $3 }+    | viewTransition Os ERB                       { ViewTransition $3 }+    | scope Os SelectorPair ERB                   { ScopeBlock $3 $4 }+    | function Os Function                        { FunctionBlock $3 }+    | supports Os FeatureQuery ERB                { Supports (normalize $3) $4 }+    | Ident Os CommaSeparatedList Os ERB          { UnknownGramma $1 (Just (CommaSeparatedList $3)) $5 }+    | Ident Os ERB                                { UnknownGramma $1 Nothing $3 }+Bool :: { Bool }+    : true                                        { True }+    | false                                       { False }+CustomMediaQuery :: { CustomMediaQuery }+    : Bool                                        { CustomMediaFlag $1 }+    | MediaQueryList                              { CustomMediaQuery (MediaQueryList $1) }+Function :: { CssFunction }+    : Var Op FunArgs Os ')' Os RetType Os Ocb FunEntries CssFileBody '}'+                                                  { F.Function $1 $3 $7 (snd $10) (fst $10) $11 }+FunArgs :: { [ F.FunArg ] }+    :                                             { [] }+    | FunArg                                      { [ $1 ] }+    | FunArg Os ',' Os FunArgs                    { $1 : $5 }+FunArg :: { F.FunArg }+    : Var                                         { F.FunArg $1 Nothing Nothing }+    | Var Os TypeFun                              { F.FunArg $1 (Just $3) Nothing }+    | Var Os TypeFun Os ':' Os PropVal            { F.FunArg $1 (Just $3) (Just $7) }+    | Var Os ':' Os PropVal                       { F.FunArg $1 Nothing (Just $5) }+FunEntries :: { (NonEmpty PropVals, [ F.ConstEntry ]) }+    : LocalConst                                  {% findResult $1 }+LocalConst :: { [Either F.ConstEntry (NonEmpty PropVals)] }+    :                                             {% pure [] }+    | descriptor Os PropParValsList               {% fmap (:[]) (varOrResult $1 $3) }+    | descriptor Os PropParValsList ';' LocalConst+                                                  {% fmap (:$5) (varOrResult $1 $3) }+RetType :: { Maybe T.CssType }+    :                                             { Nothing }+    | returns Os TypeFun                          { Just $3 }+TypeFun :: { T.CssType }+    : 'type(' Os CssType Os ')'                   { $3 }+    | CssLeaf                                     { T.Once $1 }+    | '*'                                         { T.AnyCssType }+CssType :: { T.CssType }+    : '*'                                         { T.AnyCssType }+    | CssLeaf                                     { T.Once $1 }+    | CssLeaf '#'                                 { T.CommaSeparated $1 }+    | CssLeaf '+'                                 { T.SpaceSeparated $1 }+    | CssLeaf Os '|' Os CssType                   { T.OrLeaf $1 $5 }+CssLeaf :: { T.CssLeafType }+    : syntaxType                                  { T.AtomicCssType $1 }+FeatureQuery :: { FeatureQuery }+    : Op MediaFeature ')'                         { FqMediaFeature $2 }+    | Op FeatureQuery ')'                         { FqParen $2 }+    | FeatureQuery Os BOP FeatureQuery            { FqBop $3 $1 $4 }+    | 'not' Os FeatureQuery                       { FqNot $3 }+    | 'not' Os FeatureQuery Os BOP FeatureQuery   { FqBop $5 (FqNot $3) $6 }+    | 'selector(' SelectorList ')'                { FqApp (FqSelectorFun $2) }+    | Ident Op PropVals ')'                       { FqApp (FqSomeFun $1 $3) }+SL :: { SelectorList }+    :  SelectorList ')'                           { $1 }+ESL :: { SelectorList }+    : '(' SelectorList ')'                        { $2 }+SelectorPair :: { MonoPair SelectorList }+    :                                             { EmptyPair }+    | ESL Os                                      { HalfPair $1 }+    | ESL Os 'to' Os ESL Os                       { FullPair $1 $5 }+OsCssRuleBody :: { [CssRuleBodyItem] }+    : Os CssRuleBody                              { $2 }+ERB :: { [CssRuleBodyItem] } -- Embraced Rule Body+    : '{' OsCssRuleBody '}'                       { $2 }+ContainerQueryMap :: { NonEmpty (These R.Ident ContainerQuery) }+    : NonEmpty(',', IdContainerQuery)             { $1 }+IdContainerQuery :: { These R.Ident ContainerQuery }+    : Ident                                       { This $1 }+    | Ident ' ' Ident Os Op CQ ')'                { These $1 (CqFeature (AsIs (CqApp $3 $6))) }+    | Ident ' ' Ident Os Op CQ ')' Os BOP CQ      { These $1+                                                      (CqBin+                                                        $9+                                                        (AsIs+                                                          (CqApp $3 $6))+                                                        $10)+                                                  }+    | Ident ' ' CQ                                { These $1 $3 }+    | Ident Op CQ ')'                             { That (CqFeature (AsIs (CqApp $1 $3))) }+    | Ident Op CQ ')' Os BOP CQ                   { That+                                                      (CqBin+                                                        $6+                                                        (AsIs+                                                          (CqApp $1 $3))+                                                        $7)+                                                  }+    | CQ                                          { That $1 }+DescAsPropName :: { PropertyName }+    : descriptor                                  { toPropertyName $1 }+CQ :: { ContainerQuery }+    : Ident Os Op CQ ')'                          { CqFeature (AsIs (CqApp $1 $4)) }+    | Ident Os Op CQ ')' Os BOP CQ                { CqBin+                                                      $7+                                                      (AsIs (CqApp $1 $4))+                                                      $8+                                                  }+    | DescAsPropName Os PropParVals               { CqFeature (AsIs (CqOpFeature (PlainMf $1 $3))) }+    | PropN Os ':' PropParVals                    { CqFeature (AsIs (CqOpFeature (PlainMf $1 $4))) }+    | 'not' Os Op MediaFeature ')' Os BOP CQ      { CqBin $7 (Not (CqOpFeature $4)) $8 }+    | 'not' Os Op MediaFeature ')'                { CqFeature (Not (CqOpFeature $4)) }+    | 'not' Os Ident Os Op CQ ')'                 { CqFeature (Not (CqApp $3 $6)) }+    | 'not' Os Ident Os Op CQ ')' Os BOP CQ       { CqBin $9 (Not (CqApp $3 $6)) $10 }+    | Op MediaFeature ')' Os BOP CQ               { CqBin $5 (AsIs (CqOpFeature $2)) $6 }+    | Op MediaFeature ')'                         { CqFeature (AsIs (CqOpFeature $2)) }+BOP :: { BinOp }+    : and Os                                      { And }+    | or Os                                       { Or }+FontFeatureValBlocks :: { [ Either PropEntry FontFeatureValuesSubBlock ] }+    :                                             { [] }+    | FontFeatureValBlock FontFeatureValBlocks    { Right $1 : $2 }+    | PropEntry FontFeatureValBlocks              { Left $1 : $2 }+FontFeatureValBlock :: { FontFeatureValuesSubBlock }+    : '@' IdKwd Os Ocb FontFeatureEntries '}'     { FontFeatureValuesSubBlock $1 $2 $5 }+FontFeatureEntries :: { [ FontFeatureEntry ] }+    :                                             { [] }+    | FontFeatureEntry                            { [$1] }+    | FontFeatureEntry ';'                        { [$1] }+    | FontFeatureEntry ';' FontFeatureEntries     { $1 : $3 }+FontFeatureEntry :: { FontFeatureEntry }+    : IdKwd Os ':' Os NonEmpty(' ', Unsigned)     { FontFeatureEntry $1 (SslNe $5) }+    | Desc Os NonEmpty(' ', Unsigned)             {% identOnly+                                                       (toPropertyName $1)+                                                       (`FontFeatureEntry` (SslNe $3))+                                                  }+StrEitherIds :: { Either LiteralString IdentList }+    : Str                                         { Left (LiteralString $1) }+    | NonEmpty(' ', IdKwd)                        { Right (IdentList $1) }+CommaSeparatedList :: { NonEmpty PropVals }+    : CssPropertyVals Important                   { (PropVals $1 $2) :| [] }+    | CssPropertyVals Important ',' CommaSeparatedList+                                                  { (PropVals $1 $2) <| $4 }+UnicodeRange :: { UnicodeRange }+    : unRangeVal                                  { UnicodeRange (pack $1) }+Keyframe :: { Keyframe }+    : Os NonEmpty(',', KeyframeAdr) Os Ocb PropEntries '}'+                                                  { Keyframe (CslNe $2) $5 }+TypedNum :: { TypedNum }+    : typedNum                                    {% parseTypedNum $1 }+KeyframeAdr+    : from                                        { KeyframeStart }+    | 'to'                                        { KeyframeEnd }+    | TypedNum                                    {% fmap KeyframePercentAdr (tryGet Percent $1) }+PropEntries :: { [PropEntry] }+    : List(PropEntry)                             { $1 }+Desc :: { Descriptor }+    : descriptor                                  { $1 }+PropN :: { PropertyName }+    : IdKwd                                       { PropertyName $1 }+    | Var                                         { VarProp $1 }+PropEntry :: { PropEntry }+    : descriptor Os PropParVals ';'               { PropEntry $1 $3 }+    | descriptor Os PropParVals                   { PropEntry $1 $3 }+PageSelectorList+    : PageSelector                                { [ $1 ] }+    | PageSelector Os PageSelectorList            { $1 : $3 }+PageSelector+    : IdKwd PseudoPageList                        { PageSelector (Just (PageName $1)) $2 }+    | IdKwd                                       { PageSelector (Just (PageName $1)) [] }+    | PseudoPageList                              { PageSelector Nothing $1 }+PseudoPageList+    : pseudc                                      { [$1] }+    | heading                                     { [P.Heading] }+    | host                                        { [P.Host] }+    | pseudc PseudoPageList                       { $1 : $2 }+    | heading PseudoPageList                      { P.Heading : $2 }+    | host PseudoPageList                         { P.Host : $2 }+IdKwdMb+    :                                             { Nothing }+    | IdKwd                                       { Just $1 }+MediaQueryList :: { [ MediaQuery ] }+    : MediaQuery                                  { [ $1 ] }+    | MediaQuery ',' MediaQueryList               { $1 : $3 }+    | MediaQuery or Os MediaQueryList             { $1 : $4 }+MediaQuery :: { MediaQuery }+    : 'not' Os MediaCondition                     {% massageExpr MediaQueryConditionOnly (NotMc $3) }+    | 'not' Os MediaCondition Os MediaAnds        {% massageExpr MediaQueryConditionOnly+                                                                 (mkBopMcTree And (NotMc $3 :| $5)) }+    | 'not' Os MediaCondition Os MediaOrs         {% massageExpr MediaQueryConditionOnly+                                                                 (mkBopMcTree Or  (NotMc $3 :| $5)) }+    | 'not' Os MediaType Os and Os MediaCondition {% massageExpr (MediaQueryWithMt (Just MtNot) $3 . Just) $7 }+    | 'not' Os MediaType Os                       { MediaQueryWithMt (Just MtNot) $3 Nothing }+    | only Os MediaType Os                        { MediaQueryWithMt (Just MtOnly) $3 Nothing }+    | only Os MediaType Os and Os MediaCondition  {% massageExpr (MediaQueryWithMt (Just MtOnly) $3 . Just) $7 }+    | MediaType Os                                { MediaQueryWithMt Nothing $1 Nothing }+    | MediaType Os and Os MediaCondition          {% massageExpr (MediaQueryWithMt Nothing $1 . Just) $5 }+    | MediaCondition                              {% massageExpr MediaQueryConditionOnly $1 }+MediaType :: { MediaType }+    : mediaType                                   { $1 }+MediaCondition :: { MediaCondition }+    : MediaNot                                    { $1 }+    | MediaNot Os MediaAnds                       { mkBopMcTree And ($1 :| $3) }+    | MediaNot Os MediaOrs                        { mkBopMcTree Or ($1 :| $3) }+    | MediaInParens                               { $1 }+    | MediaInParens Os MediaAnds                  { mkBopMcTree And ($1 :| $3) }+    | MediaInParens Os MediaOrs                   { mkBopMcTree Or ($1 :| $3) }+MediaNot :: { MediaCondition }+    : 'not' Os MediaInParens                      { NotMc $3 }+MediaAnds :: { [MediaCondition] }+    : MediaAnd                                    { [$1] }+    | MediaAnd Os MediaAnds                       { $1 : $3 }+MediaAnd :: { MediaCondition }+    : and Os MediaInParens                        { $3 }+    | and Os MediaNot                             { $3 }+MediaOrs :: { [MediaCondition] }+    : MediaOr                                     { [$1] }+    | MediaOr Os MediaOrs                         { $1 : $3 }+MediaOr :: { MediaCondition }+    : or Os MediaInParens                         { $3 }+    | or Os MediaNot                              { $3 }+MediaInParens+    : Op MediaCondition ')'                       { ParenMc $2 }+    | Op MediaFeature ')'                         { FeatureMc $2 }+MediaFeature :: { MediaFeature }+    : DescAsPropName Os PropParVals               { PlainMf $1 $3 }+    | PropN Os ':' Os PropParVals                 { PlainMf $1 $5 }+    | PropN MfRel PropParVal                      { OpenRangeFeature $1 $2 $3 }+    | PropN MfRel PropN MfRel PropParVal          { MfClosedRange (propRef $1) $2 $3 $4 $5 }+    | PropN Op PropParVals ')' MfRel PropN        { OpenRangeFeatureFlipped+                                                      (AppFun $1 $3)+                                                      $5+                                                      $6+                                                  }+    | PropN Op PropParVals ')' '/' Os PropParVal MfRel PropN+                                                  { OpenRangeFeatureFlipped+                                                      (Div+                                                        (AppFun $1 $3)+                                                        $7)+                                                      $8 $9+                                                  }+    | PropN Op PropParVals ')' '/' Os PropParVal MfRel PropN MfRel PropParVal+                                                  { MfClosedRange+                                                      (Div+                                                        (AppFun $1 $3)+                                                        $7)+                                                      $8 $9 $10 $11+                                                  }+    | PropN Op PropParVals ')' MfRel PropN MfRel PropParVal+                                                  { MfClosedRange+                                                      (AppFun $1 $3)+                                                      $5+                                                      $6+                                                      $7+                                                      $8+                                                  }+    | PropN                                       { BooleanMf $1 }+    | PropVal MfRel PropN                         { OpenRangeFeatureFlipped $1 $2 $3 }+    | PropVal MfRel PropN MfRel PropParVal        { MfClosedRange $1 $2 $3 $4 $5 }+MfRel :: { MfRelation }+    : '<'                                         { MfLt }+    | '>'                                         { MfGt }+    | '<='                                        { MfLe }+    | '>='                                        { MfGe }+    | '='                                         { MfEq }+Url :: { Url }+    : 'url(' Str ')'                              { Url $2 }+    | 'uqUrl'                                     { UnquotedUrl (pack $1) }+PropVal :: { PropVal }+    : TypedNum                                    { IntVal $1 }+    | hash                                        { HexColor (HC (pack $1)) }+    | PropN                                       { propRef $1 }+    | Str                                         { StrVal $1 }+    | PropVal '/' Os PropVal                      { Div $1 $4 }+    | PropN Op PropParValsList ')'                { mkAppFun $1 $3 }+    | PropN Op ')'                                { AppConst $1 }+    | '.'                                         { DotVal }+    | Url                                         { UrlVal $1 }+    | Bool                                        { BoolVal $1 }+    | 'attr(' Os AttrName Os Maybe(AttrType) AttrDefVal Os ')'+                                                  { AttrFun $3 $5 $6 }+    | calcFns Op CalcExprList Os ')'              {% massageExpr CalcFun (CalcCe $1 $3) }+    | Op Os CalcExprList Os ')'                   {% massageExpr CalcFun (CalcCe NoFn $3) }+    | 'ratio'                                     { RatioVal $1 }+    | UnicodeRange                                { Vl.UnicodeRangeVal $1 }+    | alpha Op Ident Os '=' Os Unsigned Os ')'    { AlphaF $7 }+    | '[' Ident ']'                               { BracketVal $2 }+PropParVal :: { PropVal }+    : TypedNum                                    { IntVal $1 }+    | hash                                        { HexColor (HC (pack $1)) }+    | PropN                                       { propRef $1 }+    | Str                                         { StrVal $1 }+    | PropVal '/' Os PropVal                      { Div $1 $4 }+    | PropN Op PropParValsList ')'                { mkAppFun $1 $3 }+    | PropN Op ')'                                { AppConst $1 }+    | '.'                                         { DotVal }+    | Url                                         { UrlVal $1 }+    | Bool                                        { BoolVal $1 }+    | 'attr(' Os AttrName Os Maybe(AttrType) AttrDefVal Os ')'+                                                  { AttrFun $3 $5 $6 }+    | calcFns Op CalcExprList Os ')'              {% massageExpr CalcFun (CalcCe $1 $3) }+    | Op Os CalcExprList Os ')'                   {% massageExpr CalcFun (CalcCe NoFn $3) }+    | 'ratio'                                     { RatioVal $1 }+    | UnicodeRange                                { Vl.UnicodeRangeVal $1 }+    | alpha Op Ident Os '=' Os Unsigned Os ')'    { AlphaF $7 }+    | '[' Ident ']'                               { BracketVal $2 }++AttrType :: { AttrType }+    : TypeFun                                     { CssTypeAt $1 }+    | UnitType                                    { UnitAt $1 }+    | rawString                                   { RawString }+UnitType :: { PropValType }+    : ident                                       {% parseAsUnitType $1 }+    | '%'                                         { Percent }+AttrDefVal :: { Maybe PropVal }+    :                                             { Nothing }+    | ',' Os PropParVal                           { Just $3 }+CalcOp :: { CalcOp }+    : '+'                                         { PlusCe  }+    | '-'                                         { MinusCe }+    | '/'                                         { DivCe   }+    | '*'                                         { ProdCe  }+CalcExpr :: { CalcExpr }+   : Op CalcExprList ')' Os                      { CalcCe NoFn $2 }+   | '-' Os CalcExpr                             { CalcNeg $3 }+   | CalcExpr CalcOp Os CalcExpr                 { BinOpCe $1 $2 $4 }+   | CalcExpr CalcExpr                           {% recoverCalcBinOp $1 $2 }+   | PropN Op PropParValsList Os ')' Os          { AppCe $1 (PropValsList $3) }+   | PropN Os                                    { VarCe $1 }+   | TypedNum Os                                 { ValCe $1 }+   | calcFns Op CalcExprList ')' Os              { CalcCe $1 $3 }+CalcExprList :: { CalcExprList }+    : NonEmpty(',', CalcExpr)                     { CalcExprList $1 }+Unsigned :: { Unsigned }+    : TypedNum                                    {% fmap Unsigned (tryGet K $1) }+ContinueRule :: { CssRule }+    : SelectorList '{' Os CssRuleBody '}'         { CssRule $1 $4 }+CssRuleBody :: { [ CssRuleBodyItem ] }+    :                                             { [] }+    | Desc Os PropParValsList ';' OsCssRuleBody   { mkLeaf $1 $3 : $5 }+    | Desc Os PropParValsList                     { [ mkLeaf $1 $3 ] }+    | Desc Os ';' OsCssRuleBody                   { $4 }+    | Desc Os                                     { [] }+    | CssRule OsCssRuleBody                       { CssNestedRule $1 : $2 }+PropParValsList :: { NonEmpty PropVals }+    : NonEmpty(',', PropParVals)                     { $1 }+PropValsList :: { NonEmpty PropVals }+    : NonEmpty(',', PropVals)                     { $1 }+PropParVals :: { PropVals }+    : CssPropertyParVals Important                { PropVals $1 $2 }+PropVals :: { PropVals }+    : CssPropertyVals Important                   { PropVals $1 $2 }+Important :: { Maybe Important }+    : important                                   { Just Important }+    |                                             { Nothing }+CssPropertyParVals :: { NonEmpty PropVal }+    : PropParVal Os                               { $1 :| [] }+    | PropParVal Os CssPropertyParVals            { $1 <| $3 }+CssPropertyVals :: { NonEmpty PropVal }+    : PropVal Os                                  { $1 :| [] }+    | PropVal Os CssPropertyVals                  { $1 <| $3 }+SelectorList :: { NonEmpty Selector }+    : NonEmpty(',', Selector)                     { $1 }+Selector :: { Selector }+    : TagRelMb TagSel ZipTagRelAndTagSel          { Selector $1 $2 $3 }+    | TagRelMb TagSel ZipTagRelAndTagSel PsTgSel  { PeSelector $1 $2 $3 $4 }+    | PsTgSel                                     { PeSelectorOnly $1 }+PsTgSel :: { PseudeTagSelector }+    : CompositePe TagClasses                      { PseudeTagSelector $1 $2 }+CompositePe :: { CompositePe }+    : pseude                                      { AtomicPe $1 }+    | highlight Op IdKwd ')'                      { Highlight (Embraced $3) }+    | part Op SslNeOfIdents ')'                   { Part (Embraced $3) }+    | picker Op IdKwd Os ')'                      { Picker (Embraced $3) }+    | scrollButton Op TagName Os ')'              { ScrollButton (Embraced $3) }+    | slotted ESL                                 { Slotted (Embraced $2) }+    | viewTransitionGroup ESL                     { ViewTransitionGroup (Embraced $2) }+    | viewTransitionImagePair ESL                 { ViewTransitionImagePair (Embraced $2) }+    | viewTransitionNew ESL                       { ViewTransitionNew (Embraced $2) }+    | viewTransitionOld ESL                       { ViewTransitionOld (Embraced $2) }+TagRelMb :: { Maybe TagRelation }+    : Maybe(TagRelation)                          { $1 }+TagSel :: { TagSelector }+    : Ident '|' TagName TagClasses                { TagSelector (R.Namespace $1) $3 $4 }+    | Ident TagClasses                            { TagSelector NoBar (TagName $1) $2 }+    | '&' TagClasses                              { TagSelector NoBar AmpersandTag $2 }+    | '*' '|' TagName TagClasses                  { TagSelector AsteriskNs $3 $4  }+    | '*' TagClasses                              { TagSelector NoBar AsteriskTag $2 }+    | '|' TagName TagClasses                      { TagSelector NoNs $2 $3  }+    | TagClasses                                  { TagSelector NoBar NoTag $1 }+TagName :: { TagName }+    :                                             { NoTag }+    | '&'                                         { AmpersandTag }+    | '*'                                         { AsteriskTag }+    | Ident                                       { TagName $1 }+Hash :: { TagSubSelector }+    : hash                                        { Hash (R.Ident (pack $1)) }+TagClasses :: { [ TagSubSelector ] }+    : List(TagClass)                              { $1 }+TagClass :: { TagSubSelector }+    : Class                                       { AtomicClass $1 }+    | pseudc                                      { AtomicPseudoClass $1 }+    | pseudc ESL                                  {% mkUnknownPseudoF $1 $2 }+    | ':not' '(' SL                               { NotClass $3 }+    | ':lang(' Str ')'                            { Lang (Language $2) }+    | activeViewTransitionType Op CslOfIdents Os ')'+                                                  { ActiveViewTransitionType (Embraced $3) }+    | ':dir' Op IdKwd Os ')'                      { Dir (Embraced $3) }+    | heading Op CslOfInts Os ')'                 { Heading (Embraced $3) }+    | heading                                     { AtomicPseudoClass P.Heading }+    | host ESL                                    { Host (Embraced $2) }+    | host                                        { AtomicPseudoClass P.Host }+    | ':state(' Os IdKwd Os ')'                   { State (Embraced $3) }+    | ':global' Op SL                             { Global $3 }+    | ':where' Op SL                              { Where $3 }+    | ':is' Op SL                                 { Is $3 }+    | ':has' Op SL                                { Has $3 }+    | nthChild Op NthFormula OfTagSel Os ')'      { NthChild $3 $4 }+    | nthLastChild Op NthFormula OfTagSel Os ')'  { NthLastChild $3 $4 }+    | nthOfType Op NthFormula ')'                 { NthOfType $3 }+    | nthLastOfType Op NthFormula ')'             { NthLastOfType $3 }+    | '[' Attr ']'                                { $2 }+    | Hash                                        { $1 }+NthFormula :: { NthFormula }+    : CalcExpr                                    {% massageExpr NthExpr $1 }+    | odd Os                                      { NthEven False }+    | even Os                                     { NthEven True }+OfTagSel :: { Maybe TagSelector }+    :                                             { Nothing }+    | of Os TagSel                                { Just $3 }+CslOfIdents :: { CslNe R.Ident }+    : NonEmpty(',', IdKwd)                        { CslNe $1 }+SslNeOfIdents :: { SslNe R.Ident }+    : NonEmpty(' ', IdKwd)                        { SslNe $1 }+CslOfInts :: { CslNe Unsigned }+    : NonEmpty(Embraced(Os, ',', Os), Unsigned)   { CslNe $1 }+ZipTagRelAndTagSel :: { [ (TagRelation, TagSelector) ] }+    :                                             { [] }+    | TagRelation TagSel ZipTagRelAndTagSel       { ($1, $2) : $3 }+TagRelation :: { TagRelation }+    : ' ' '+' Os                                  { NextSibling }+    | ' ' '>' Os                                  { Child }+    | ' ' '~' Os                                  { GeneralSibling }+    | ' ' Os                                      { Descendant }+    | '+' Os                                      { NextSibling }+    | '>' Os                                      { Child }+    | '~' Os                                      { GeneralSibling }+Ocb : '{'                                         { () }+Op  :: { () }+    : '(' Os                                      { () }+Os  :                                             { () }+    | ' '                                         { () }+AtrPat :: { Maybe AtrPat }+    : Str AtrPatCase                              { Just (StrAtrPat $1 $2) }+    | attrPat AtrPatCase                          { Just (IdtAtrPat (R.Ident $1) $2) }+    |                                             { Nothing }+AtrPatCase :: { Maybe CaseSensetivity }+    :                                             { Nothing }+    | Os attrPat                                  {% fmap Just (atrCaseSensetivity (R.Ident $2)) }+AttrName :: { AttrName }+    : IdKwd                                       { AttrName NoBar $1 }+    | IdKwd '|' IdKwd                             { AttrName (R.Namespace $1) $3 }+    | '|' IdKwd                                   { AttrName NoNs $2 }+    | '*' '|' IdKwd                               { AttrName AsteriskNs $3 }+Attr :: { TagSubSelector }+    : AttrName AttrOp AtrPat                      { Attr $1 $2 $3 }+    | AttrName                                    { HasAttr $1 }+AttrOp ::  { AttrOp }+    : '='                                         { Exact }+    | '~='                                        { Include }+    | '|='                                        { DashMatch }+    | '^='                                        { PrefixMatch }+    | '$='                                        { SuffixMatch }+    | '*='                                        { SubstringMatch }+IdKwd :: { R.Ident }+    : Ident                                       { $1 }+    | MediaKeywordAsIdent                         { $1 }+    | 'to'                                        { R.Ident "to" }+    | from                                        { R.Ident "from" }+    | returns                                     { R.Ident "returns" }+    | AtId                                        { $1 }+AtId :: { R.Ident }+    : charset                                     { R.Ident "charset" }+    | colorProfile                                { R.Ident "color-profile" }+    | container                                   { R.Ident "container" }+    | counterStyle                                { R.Ident "counter-style" }+    | fontFace                                    { R.Ident "font-face" }+    | fontFeatureValues                           { R.Ident "font-feature-values" }+    | fontPaletteValues                           { R.Ident "font-palette-values" }+    | function                                    { R.Ident "function" }+    | import                                      { R.Ident "import" }+    | keyframes                                   { R.Ident "keyframes" }+    | layer                                       { R.Ident "layer" }+    | media                                       { R.Ident "media" }+    | mediaType                                   { R.Ident (toStrict (toCssText $1)) }+    | namespace                                   { R.Ident "namespace" }+    | page                                        { R.Ident "page" }+    | alpha                                       { R.Ident "alpha" }+    | pageMargin                                  { R.Ident "page-margin" }+    | positionTry                                 { R.Ident "position-try" }+    | property                                    { R.Ident "property" }+    | scope                                       { R.Ident "scope" }+    | startingStyle                               { R.Ident "starting-style" }+    | supports                                    { R.Ident "supports" }+    | viewTransition                              { R.Ident "view-transition" }+MediaKeywordAsIdent+    : or                                          { R.Ident "or" }+    | and                                         { R.Ident "and" }+    | only                                        { R.Ident "only" }+Ident :: { R.Ident }+    : ident                                       { R.Ident $1 }+Class :: { R.Ident }+    : class                                       { R.Ident (pack $1) }+IdTxt :: { Text }+    : ident                                       { $1 }+Str :: { Text }+    : string                                      { pack $1 }+Var :: { R.Var }+    : var                                         { R.Var (R.Ident (pack $1)) }+Embraced(o, p, c)+    : o p c                                       { $2 }+Clp : ')'                                         { $1 }+P(p): Embraced(Op, p, Clp)                        { $1 }+SepList(sep, elt)+    :                                             { [] }+    | elt sep List(elt)                           { $1 : $3 }+List(elt)+    :                                             { [] }+    | elt List(elt)                               { $1 : $2 }+Maybe(elt)+    :                                             { Nothing }+    | elt                                         { Just $1 }+NonEmpty(sep, elt)+    : elt                                         { $1 :| [] }+    | elt sep NonEmpty(sep, elt)                  { $1 <| $3 }+{+happyError :: [TokenLoc] -> P a+happyError (~(TokenLoc t s ~(Just (AlexPn _ l c))):_) =+  failP $ "Can not parse CSS: unpexected token \"" <>+    s <> "\" at (" <> show l <> ", " <> show c <> ")"+happyError _ = failP "Unexpected end of a CSS string"+}
+ src/CssParser/Parser/Monad.hs view
@@ -0,0 +1,62 @@+module CssParser.Parser.Monad+ ( module CssParser.Parser.Monad+ , module X+ ) where++import CssParser.Prelude+import Data.List qualified as L+import Expression.Reorder as X ( reorder, Validation(..), SyntaxTree )+import GHC.Stack (HasCallStack)++data P a = Ok a | Failed String deriving (Functor)++reorderErr :: (HasCallStack, Show a, SyntaxTree a String) => a -> a+reorderErr x =+  case reorderInP x of+    Failed x' -> error x'+    Ok x' -> x'++reorderInP :: (Show a, SyntaxTree a String) => a -> P a+reorderInP a = validationToP a (reorder a)++validationToP :: Show a => a -> Validation (NonEmpty String) a -> P a+validationToP x = \case+  Failure er ->+      Failed $ "Failed to reorder " <> show x <> " due: " <> L.intercalate "\n" (toList er)+  Success a -> Ok a++instance Applicative P where+  pure = Ok+  Ok f <*> Ok a = Ok (f a)+  Failed f <*> _ = Failed f+  _ <*> Failed b = Failed b++instance Monad P where+  Failed v >>= _ = Failed v+  Ok v >>= f = f v++instance MonadFail P where+  fail = Failed++thenP :: P a -> (a -> P b) -> P b+m `thenP` k =+   case m of+       Ok a     -> k a+       Failed e -> Failed e++returnP :: a -> P a+returnP = Ok++failP :: String -> P a+failP = Failed++catchP :: P a -> (String -> P a) -> P a+catchP m k =+   case m of+      Ok a     -> Ok a+      Failed e -> k e++fromEitherM :: Applicative m => (e -> m a) -> Either e a -> m a+fromEitherM ef = \case+  Right v -> pure v+  Left e -> ef e
+ src/CssParser/Prelude.hs view
@@ -0,0 +1,27 @@+module CssParser.Prelude+  ( LText+  , Text+  , module X+  ) where++import Control.Monad as X ((<=<))+import Control.Monad.Fail as X+import CssParser.List as X+import Data.Char as X hiding (toLower)+import Data.Either as X (partitionEithers)+import Data.Either.Combinators as X+import Data.Functor.Identity as X+import Data.HashMap.Strict as X (HashMap)+import Data.Kind as X+import Data.List.NonEmpty as X ( NonEmpty ((:|)), (<|), toList, nonEmpty, appendList, prependList)+import Data.Maybe as X+import Data.These as X+import Data.String as X (IsString (..))+import Data.Text (Text)+import Data.Text.Lazy as X (concat, intercalate, toStrict, toLower, fromStrict, cons, snoc, unlines, unwords, unpack)+import Data.Text.Lazy qualified as L+import Generics.Deriving.Enum as X hiding (range)+import GHC.Generics as X (Generic)+import Prelude as X hiding (concat, null, unlines, unwords)++type LText = L.Text
+ src/CssParser/Rule.hs view
@@ -0,0 +1,164 @@+module CssParser.Rule where++import CssParser.At.Container ( ContainerQueryMap )+import CssParser.At.CustomMedia ( CustomMediaQuery )+import CssParser.At.FontFeatureValues ( FontFeatureValues )+import CssParser.At.FontPaletteValues ( FontPaletteValues )+import CssParser.At.Function ( Function )+import CssParser.At.Import ( Import )+import CssParser.At.Keyframe ( KeyframeSet, PropEntry )+import CssParser.At.MediaQuery ( MediaQueryList )+import CssParser.At.Page ( PageMargin, PageSelectorList )+import CssParser.At.Supports qualified as S+import CssParser.Descriptor (Descriptor)+import CssParser.Ident+import CssParser.MonoPair ( MonoPair )+import CssParser.Parser.Monad ( P(Failed) )+import CssParser.Prelude+import CssParser.Rule.Pseudo+    ( AtomicPseudoClass(UnknownPc),+      BrowserSpecificIdent(BrowserSpecificIdent),+      PseudoElement,+      Language )+import CssParser.Rule.Value+    ( CommaSeparatedList,+      PropVals,+      PropValsList,+      Source,+      Unsigned,+      NthFormula )+import CssParser.Show ( toCssStr, CslNe, Embraced, SslNe )+++type SelectorList = NonEmpty Selector+type FeatureQuery = S.FeatureQuery SelectorList++data CssRule+  = CssRule SelectorList [CssRuleBodyItem]+  | AtRule BrowserPrefix AtRule+  deriving (Show, Ord, Eq, Generic)++data AtRule+  = CharsetStmt Charset+  | ColorProfile PropertyName [CssRuleBodyItem]+  | Container ContainerQueryMap [CssRuleBodyItem]+  | CounterStyle Ident [CssRuleBodyItem]+  | CustomMedia Var CustomMediaQuery+  | FontFaceBlock [CssRuleBodyItem]+  | FontFeatureValuesBlock FontFeatureValues+  | FontPaletteValuesBlock FontPaletteValues+  | FunctionBlock CssFunction+  | ImportStmt (Import SelectorList)+  | Keyframes KeyframeSet+  | LayerBlock (Maybe LayerName) [CssRuleBodyItem]+  | LayerStmt (NonEmpty LayerName)+  | MediaRule MediaQueryList [CssRuleBodyItem]+  | Mixin Ident+  | CustomSelector CustomSelectorName SelectorList+  | DefineMixin Ident [CssRuleBodyItem]+  | Namespace (Maybe Ident) Source+  | Page PageSelectorList [CssRuleBodyItem]+  | PageMarginBlock PageMargin [CssRuleBodyItem]+  | PositionTry Var [PropEntry]+  | Property Var [CssRuleBodyItem]+  | ScopeBlock (MonoPair SelectorList) [CssRuleBodyItem]+  | StartingStyle [CssRuleBodyItem]+  | Supports FeatureQuery [CssRuleBodyItem]+  | UnknownGramma Ident (Maybe CommaSeparatedList) [CssRuleBodyItem]+  | ViewTransition [CssRuleBodyItem]+  deriving (Show, Ord, Eq, Generic)++type CssFunction = Function CssRule++data Selector+  = Selector (Maybe TagRelation) TagSelector [(TagRelation, TagSelector)]+  | PeSelector (Maybe TagRelation) TagSelector [(TagRelation, TagSelector)] PseudeTagSelector+  | PeSelectorOnly PseudeTagSelector+  deriving (Eq, Ord, Show, Generic)++instance GEnum Selector where+  genum = []++data PseudeTagSelector+  = PseudeTagSelector+  { ptagName :: CompositePe+  , ptagSubs :: [TagSubSelector]+  } deriving (Eq, Ord, Show, Generic)++data CompositePe+  = AtomicPe PseudoElement+  | Highlight (Embraced Ident)+  | Part (Embraced (SslNe Ident))+  | Picker (Embraced Ident)+  | ScrollButton (Embraced TagName)+  | Slotted (Embraced SelectorList)+  | ViewTransitionGroup (Embraced SelectorList)+  | ViewTransitionImagePair (Embraced SelectorList)+  | ViewTransitionNew (Embraced SelectorList)+  | ViewTransitionOld (Embraced SelectorList)+  deriving (Eq, Ord, Show, Generic)++data TagRelation+  = Descendant+  | Child+  | NextSibling+  | GeneralSibling+  deriving (Bounded, Enum, Eq, Ord, Show, Generic)++data TagSelector+  = TagSelector+  { tagNs :: Namespace+  , tagName :: TagName+  , tagSubSelectors :: [ TagSubSelector ]+  } deriving (Show, Ord, Eq, Generic)++mkUnknownPseudoF :: AtomicPseudoClass -> SelectorList -> P TagSubSelector+mkUnknownPseudoF pc sel =+  case pc of+    UnknownPc (BrowserSpecificIdent i) ->+      pure $ UnknownPseudoF i sel+    o -> Failed $ "Expected unknown pseudo class but got " <> toCssStr o++data TagSubSelector+  = AtomicClass { unClass :: Ident }+  | AtomicPseudoClass AtomicPseudoClass+  | UnknownPseudoF Ident SelectorList+  | NotClass SelectorList+  | Lang Language+  | Global SelectorList+  | Where SelectorList+  | Has SelectorList+  | Is SelectorList+  | NthChild NthFormula (Maybe TagSelector)+  | NthLastChild NthFormula (Maybe TagSelector)+  | NthLastOfType NthFormula+  | NthOfType NthFormula+  | ActiveViewTransitionType (Embraced (CslNe Ident))+  | Dir (Embraced Ident)+  | Heading (Embraced (CslNe Unsigned))+  | Host (Embraced SelectorList)+  | State (Embraced Ident)+  | HasAttr AttrName+  | Attr AttrName AttrOp (Maybe AtrPat)+  | Hash Ident+  deriving (Eq, Ord, Show, Generic)++data CssRuleBodyItem+  = CssLeafRule Descriptor PropVals+  | CssEnumLeaf Descriptor PropValsList+  | CssNestedRule CssRule+  deriving (Show, Ord, Eq, Generic)++data AtrPat+  = StrAtrPat Text (Maybe CaseSensetivity)+  | IdtAtrPat Ident (Maybe CaseSensetivity)+  deriving (Show, Eq, Ord, Generic)++data AttrOp =+      Exact -- ^ exactly the value of the value, denoted with @=@+    | Include -- ^ whitespace separated list of items, one of these items is the value, denoted with @~=@+    | DashMatch -- ^ hyphen separated list of items, the first item is the value, denoted with @|=@+    | PrefixMatch -- ^ prefix of the value in the attribute, denoted with @^=@+    | SuffixMatch -- ^ suffix of the value in the attribute, denoted with @$=@+    | SubstringMatch -- ^ substring of the value in the attribute, denoted with @*=@+    deriving (Bounded, Enum, Eq, Ord, Show, Generic)
+ src/CssParser/Rule/Pseudo.hs view
@@ -0,0 +1,216 @@+module CssParser.Rule.Pseudo where++import CssParser.Ident ( Ident(..) )+import CssParser.Prelude hiding (Left, Right)+import CssParser.Show ( CssShow(..), ShowSpaceBetween(..) )++newtype Language = Language Text deriving newtype (Eq, Ord, Show, IsString)++instance CssShow Language where+  toCssText (Language l)  = fromStrict l++data PseudoElement+  = UnknownPe Ident+  | After+  | Backdrop+  | Before+  | Checkmark+  | Column+  | Cue+  | DetailsContent+  | FileSelectorButton+  | FirstLetter+  | FirstLine+  | GrammarError+  | Marker+  | PickerIcon+  | Placeholder+  | ScrollMarker+  | ScrollMarkerGroup+  | SearchText+  | Selection+  | SpellingError+  | TargetText+  | ViewTransition+  deriving (Eq, Ord, Show, Generic)++instance GEnum PseudoElement++newtype BrowserSpecificIdent = BrowserSpecificIdent Ident+  deriving newtype (Eq, Ord, Show, CssShow, IsString, GEnum)+  deriving (Generic)++data AtomicPseudoClass+  = UnknownPc BrowserSpecificIdent+  | Active+  | ActiveViewTransition+  | AnyList+  | Autofill+  | Blank+  | Buffering+  | Checked+  | Current+  | Default+  | Defined+  | Disabled+  | Empty+  | Enabled+  | First+  | FirstChild+  | FirstOfType+  | Focus+  | FocusVisible+  | FocusWithin+  | Fullscreen+  | Future+  | HasSlotted+  | Heading+  | Host+  | Hover+  | Indeterminate+  | InRange+  | InterestSource+  | InterestTarget+  | Invalid+  | LastChild+  | LastOfType+  | Left+  | Link+  | LocalLink+  | Modal+  | Muted+  | OnlyChild+  | OnlyOfType+  | Open+  | Optional+  | OutOfRange+  | Past+  | Paused+  | PictureInPicture+  | PlaceholderShown+  | Playing+  | PopoverOpen+  | ReadOnly+  | ReadWrite+  | Required+  | Right+  | Root+  | Scope+  | Seeking+  | Stalled+  | Target+  | TargetAfter+  | TargetBefore+  | TargetCurrent+  | UserInvalid+  | UserValid+  | Valid+  | Visited+  | VolumeLocked+  | XrOverlay+  deriving (Eq, Ord, Show, Generic)++instance GEnum AtomicPseudoClass++instance ShowSpaceBetween AtomicPseudoClass AtomicPseudoClass where+  cssSpace _ _ = ""++instance CssShow AtomicPseudoClass where+  toCssText = cons ':' . go+    where+      go :: AtomicPseudoClass -> LText+      go = \case+        UnknownPc i -> toCssText i+        Active -> "active"+        ActiveViewTransition -> "active-view-transition"+        AnyList -> "any-list"+        Autofill -> "autofill"+        Blank -> "blank"+        Buffering -> "buffering"+        Checked -> "checked"+        Current -> "current"+        Default -> "default"+        Defined -> "defined"+        Disabled -> "disabled"+        Empty -> "empty"+        Enabled -> "enabled"+        First -> "first"+        FirstChild -> "first-child"+        FirstOfType -> "first-of-type"+        Focus -> "focus"+        FocusVisible -> "focus-visible"+        FocusWithin -> "focus-within"+        Fullscreen -> "fullscreen"+        Future -> "future"+        HasSlotted -> "has-slotted"+        Heading -> "heading"+        Host -> "host"+        Hover -> "hover"+        Indeterminate -> "indeterminate"+        InRange -> "in-range"+        InterestSource -> "interest-source"+        InterestTarget -> "interest-target"+        Invalid -> "invalid"+        LastChild -> "last-child"+        LastOfType -> "last-of-type"+        Left -> "left"+        Link -> "link"+        LocalLink -> "local-link"+        Modal -> "modal"+        Muted -> "muted"+        OnlyChild -> "only-child"+        OnlyOfType -> "only-of-type"+        Open -> "open"+        Optional -> "optional"+        OutOfRange -> "out-of-range"+        Past -> "past"+        Paused -> "paused"+        PictureInPicture -> "picture-in-picture"+        PlaceholderShown -> "placeholder-shown"+        Playing -> "playing"+        PopoverOpen -> "popover-open"+        ReadOnly -> "read-only"+        ReadWrite -> "read-write"+        Required -> "required"+        Right -> "right"+        Root -> "root"+        Scope -> "scope"+        Seeking -> "seeking"+        Stalled -> "stalled"+        Target -> "target"+        TargetAfter -> "target-after"+        TargetBefore -> "target-before"+        TargetCurrent -> "target-current"+        UserInvalid -> "user-invalid"+        UserValid -> "user-valid"+        Valid -> "valid"+        Visited -> "visited"+        VolumeLocked -> "volume-locked"+        XrOverlay -> "xr-overlay"++instance CssShow PseudoElement where+  toCssText = ("::" <>) . go+    where+      go = \case+        UnknownPe i -> toCssText i+        After -> "after"+        Backdrop -> "backdrop"+        Before -> "before"+        Checkmark -> "checkmark"+        Column -> "column"+        Cue -> "cue"+        DetailsContent -> "details-content"+        FileSelectorButton -> "file-selector-button"+        FirstLetter -> "first-letter"+        FirstLine -> "first-line"+        GrammarError -> "grammar-error"+        Marker -> "marker"+        PickerIcon -> "picker-icon"+        Placeholder -> "placeholder"+        ScrollMarker -> "scroll-marker"+        ScrollMarkerGroup -> "scroll-marker-group"+        SearchText -> "search-text"+        Selection -> "selection"+        SpellingError -> "spelling-error"+        TargetText -> "target-text"+        ViewTransition -> "view-transition"
+ src/CssParser/Rule/Show.hs view
@@ -0,0 +1,183 @@+{-# OPTIONS_GHC -Wno-orphans #-}+module CssParser.Rule.Show where++import CssParser.Ident ( Ident(Ident) )+import CssParser.MonoPair ( MonoPair )+import CssParser.Prelude+import CssParser.Rule+import CssParser.Show+import CssParser.Utils ( encodeIdentifier )++instance ShowSpaceBetween CssRule CssRule where+  cssSpace _ _ = "\n"++instance CssShow CssRule where+  toCssText = \case+    CssRule sels body ->+      intercalate ", " (toList $ fmap toCssText sels) <> "{" <> toCssText body <> "}"+    AtRule pb ar ->+      "@" <> toCssText pb <> toCssText ar++instance CssShow AtRule where+  toCssText = \case+    MediaRule mql body ->+      "media " <> toCssText mql <> " {" <> toCssText body <> "}"+    Mixin l -> "mixin " <> toCssText l <> ";"+    CustomSelector sn sel ->+      "custom-selector " <> toCssText sn <> " " <> toCssText sel <> ";"+    DefineMixin m body ->+      "define-mixin " <> toCssText m <> " {" <> toCssText body <> "}"+    CustomMedia v body ->+      "custom-media " <> toCssText v <> " " <> toCssText body <> ";"+    LayerBlock mbn body ->+      "layer " <> maybe "" ((<> " ") . toCssText) mbn <> "{" <> toCssText body <> "}"+    ImportStmt i ->+      "import " <> toCssText i <> ";"+    LayerStmt l ->+      "layer " <> toCssText l <> ";"+    Namespace i s ->+      "namespace " <> maybe "" ((<> " ") . toCssText) i <> toCssText s <> ";"+    CharsetStmt cs ->+      "charset " <> toCssText cs <> ";"+    Page psl body ->+      "page " <> toCssText psl <> " {" <> toCssText body <> "}"+    PageMarginBlock pm body ->+      toCssText pm <> " {" <> toCssText body <> "}"+    CounterStyle cn body ->+      "counter-style " <> toCssText cn <> " {" <> toCssText body <> "}"+    Property pn body ->+      "property " <> toCssText pn <> " {" <> toCssText body <> "}"+    Keyframes kf ->+      "keyframes " <>  toCssText kf+    ColorProfile n b ->+      "color-profile " <> toCssText n <> " {" <> toCssText b <> "}"+    FontFaceBlock ff ->+      "font-face {" <> toCssText ff <> "}"+    FontFeatureValuesBlock ffv -> toCssText ffv+    FontPaletteValuesBlock ffv -> toCssText ffv+    Container cq body ->+      "container " <> toCssText cq <> " {" <> toCssText body <> "}"+    PositionTry v pl ->+      "position-try " <> toCssText v <> " {" <> toCssText pl <> "}"+    StartingStyle body ->+      "starting-style {" <> toCssText body <> "}"+    ViewTransition body ->+      "view-transition {" <> toCssText body <> "}"+    ScopeBlock range body ->+      "scope " <> toCssText range <> embrace body+    Supports fq body ->+      "supports " <> toCssText fq <> embrace body+    FunctionBlock f ->+      "function " <> toCssText f+    UnknownGramma i query body ->+      toCssText i <> " " <> maybe "" toCssText query <> embrace body++embrace :: CssShow a => a -> LText+embrace x = " {" <> toCssText x <> "}"++instance ShowSpaceBetween (MonoPair SelectorList) SelectorList  where+  cssSpace _ _ = ") to ("+instance ShowParenthesis (MonoPair SelectorList) SelectorList where+  left _ _ = "("+  right _ _ = ")"+instance ShowSpaceBetween CssRuleBodyItem CssRuleBodyItem where+  cssSpace _ _ = " "++instance CssShow CssRuleBodyItem where+  toCssText = \case+    CssNestedRule cr -> toCssText cr+    CssLeafRule pn pv -> toCssText pn <> toCssText pv <> ";"+    CssEnumLeaf pn pv -> toCssText pn <> toCssText pv <> ";"++instance CssShow TagRelation where+  toCssText = \case+    Descendant -> " "+    Child -> " > "+    NextSibling -> " + "+    GeneralSibling -> " ~ "++instance CssShow AttrOp where+  toCssText = \case+    Exact -> "="+    Include -> "~="+    DashMatch -> "|="+    PrefixMatch -> "^="+    SuffixMatch -> "$="+    SubstringMatch -> "*="+instance ShowSpaceBetween Selector Selector where+  cssSpace _ _ = ", "+instance CssShow TagSubSelector where+  toCssText = \case+    AtomicClass (Ident uc) -> cons '.' $ encodeIdentifier uc+    AtomicPseudoClass apc -> toCssText apc+    UnknownPseudoF i sel -> cons ':' $ toCssText i <> "(" <> toCssText sel <> ")"+    NotClass nes -> ":not(" <> toCssText nes <> ")"+    Lang l -> ":lang(" <> toCssText l <> ")"+    Global nes -> ":global(" <> toCssText nes <> ")"+    Where nes -> ":where(" <> toCssText nes <> ")"+    Is nes -> ":is(" <> toCssText nes <> ")"+    Has nes -> ":has(" <> toCssText nes <> ")"+    NthChild nth mOf ->+      ":nth-child(" <> toCssText nth <> mayCss (" of " <> ) mOf <> ")"+    NthLastChild nth mOf ->+      ":nth-last-child(" <> toCssText nth <> mayCss (" of " <> ) mOf <> ")"+    NthLastOfType nth -> ":nth-last-of-type(" <> toCssText nth <> ")"+    NthOfType nth -> ":nth-of-type(" <> toCssText nth <> ")"+    ActiveViewTransitionType x -> ":active-view-transition-type" <> toCssText x+    Dir x -> ":dir" <> toCssText x+    Heading x -> ":heading" <> toCssText x+    Host x -> ":host" <> toCssText x+    State x -> ":state" <> toCssText x+    HasAttr name -> "[" <> toCssText name <> "]"+    Attr name op val ->+      "[" <> toCssText name <>+      toCssText op <>+      toCssText val <>+      "]"+    Hash h -> cons '#' $ toCssText h++instance CssShow AtrPat where+  toCssText = \case+    StrAtrPat s cs -> encodeStringLiteral s <> toCssText cs+    IdtAtrPat i cs -> toCssText i <> toCssText cs++instance CssShow Selector where+  toCssText = \case+    Selector frl fts tss ->+      fold frl fts tss+    PeSelector frl fts tss pe ->+      fold frl fts tss <> toCssText pe+    PeSelectorOnly pe -> toCssText pe+    where+      fold frl fts =+        foldl+          (\ s (tr, ts) -> s <> toCssText tr <> toCssText ts)+          (maybe "" toCssText frl <> toCssText fts)++instance ShowSpaceBetween TagSubSelector TagSubSelector where+  cssSpace _ _ = ""++instance CssShow PseudeTagSelector where+  toCssText pts =+    toCssText pts.ptagName <> toCssText pts.ptagSubs++instance CssShow TagSelector where+  toCssText ts =+    concat $+    [ toCssText ts.tagNs+    , toCssText ts.tagName+    ]+    <> (toCssText <$> ts.tagSubSelectors)++instance CssShow CompositePe where+  toCssText = \case+    AtomicPe x -> toCssText x+    Highlight x -> "::highlight" <> toCssText x+    Part x -> "::part" <> toCssText x+    Picker x -> "::picker" <> toCssText x+    ScrollButton x -> "::scroll-button" <> toCssText x+    Slotted x -> "::slotted" <> toCssText x+    ViewTransitionGroup x -> "::view-transition-group" <> toCssText x+    ViewTransitionImagePair x -> "::view-transition-image-pair" <> toCssText x+    ViewTransitionNew x -> "::view-transition-new" <> toCssText x+    ViewTransitionOld x -> "::view-transition-old" <> toCssText x
+ src/CssParser/Rule/Type.hs view
@@ -0,0 +1,69 @@+module CssParser.Rule.Type where++import CssParser.Prelude+import CssParser.Show+import Data.Text (pack)++data AtomicCssType+  = Angle+  | Color+  | CustomIdent+  | Image+  | Integer+  | Length+  | LengthPercentage+  | Number+  | Percentage+  | Resolution+  | String+  | Time+  | TranformFunction+  | TranformList+  | UrlType+  deriving (Eq, Ord, Show, Enum, Bounded, Generic)++instance CssShow AtomicCssType where+  toCssText = \case+    Angle -> "<angle>"+    Color -> "<color>"+    CustomIdent -> "<custom-ident>"+    Image -> "<image>"+    Integer -> "<integer>"+    Length -> "<length>"+    LengthPercentage -> "<length-percentage>"+    Number -> "<number>"+    Percentage -> "<percentage>"+    Resolution -> "<resolution>"+    String -> "<string>"+    Time -> "<time>"+    TranformFunction -> "<tranform-function>"+    TranformList -> "<tranform-list>"+    UrlType -> "<url>"++typeMap :: HashMap Text AtomicCssType+typeMap = mkDecodingMap++readSyntaxType :: String -> Either String AtomicCssType+readSyntaxType s =+  case smartLookup (pack s) typeMap of+    Just x -> pure x+    Nothing -> Left $ "Unknown sytnax type: " <> s++newtype CssLeafType = AtomicCssType AtomicCssType+  deriving newtype (Eq, Ord, Show, CssShow) deriving (Generic)++data CssType+  = Once CssLeafType+  | AnyCssType+  | CommaSeparated CssLeafType+  | SpaceSeparated CssLeafType+  | OrLeaf CssLeafType CssType+  deriving (Eq, Ord, Show, Generic)++instance CssShow CssType where+  toCssText = \case+    Once a -> toCssText a+    AnyCssType -> "*"+    OrLeaf x t -> toCssText x <> " | " <> toCssText t+    CommaSeparated a -> toCssText a <> "#"+    SpaceSeparated a -> toCssText a <> "+"
+ src/CssParser/Rule/TypedNum.hs view
@@ -0,0 +1,173 @@+module CssParser.Rule.TypedNum where+++import CssParser.Parser.Monad+import CssParser.Prelude+import CssParser.Show+import Data.HashMap.Strict qualified as HM+import Data.Text qualified as C8++type NumberStr = String++data PropValType+  = Cap+  | Ch+  | Cm+  | Cqb+  | Cqh+  | Cqi+  | Cqmax+  | Cqmin+  | Cqw+  | Deg+  | Dpi+  | Dvb+  | Dvh+  | Dvi+  | Dvmax+  | Dvmin+  | Em+  | Ex+  | Fr+  | Grad+  | Hz+  | Ic+  | In+  | KHz+  | Lh+  | Lvb+  | Lvh+  | Lvi+  | Lvmax+  | Lvmin+  | Mm+  | Ms+  | N  -- virtual unit used for pasing nth-child(-2n + 1)+  | Pc+  | Pt+  | Percent+  | Px+  | Q+  | Rad+  | Rcap+  | Rch+  | Rem+  | Rex+  | Ric+  | Rlh+  | Second+  | Svb+  | Svh+  | Svi+  | Svmax+  | Svmin+  | Turn+  | Vb+  | Vh+  | Vi+  | Vmax+  | Vmin+  | Vw+  | K+  deriving (Eq, Ord, Show, Enum, Bounded, Generic)++instance CssShow PropValType where+  toCssText = \case+    Cap -> "cap"+    Ch -> "ch"+    Cm -> "cm"+    Cqb -> "cqb"+    Cqh -> "cqh"+    Cqi -> "cqi"+    Cqmax -> "cqmax"+    Cqmin -> "cqmin"+    Cqw -> "cqw"+    Deg -> "deg"+    Dpi -> "dpi"+    Dvb -> "dvb"+    Dvh -> "dvh"+    Dvi -> "dvi"+    Dvmax -> "dvmax"+    Dvmin -> "dvmin"+    Em -> "em"+    Ex -> "ex"+    Fr -> "fr"+    Grad -> "grad"+    Hz -> "Hz"+    KHz -> "kHz"+    Ic -> "ic"+    In -> "in"+    Lh -> "lh"+    Lvb -> "lvb"+    Lvh -> "lvh"+    Lvi -> "lvi"+    Lvmax -> "lvmax"+    Lvmin -> "lvmin"+    Mm -> "mm"+    Ms -> "ms"+    N  -> "n"+    Pc -> "pc"+    Pt -> "pt"+    Percent -> "%"+    Px -> "px"+    Q -> "q"+    Rad -> "rad"+    Rcap -> "rcap"+    Rch -> "rch"+    Rem -> "rem"+    Rex -> "rex"+    Ric -> "ric"+    Rlh -> "rlh"+    Second -> "s"+    Svb -> "svb"+    Svh -> "svh"+    Svi -> "svi"+    Svmax -> "svmax"+    Svmin -> "svmin"+    Turn -> "turn"+    Vb -> "vb"+    Vh -> "vh"+    Vi -> "vi"+    Vmax -> "vmax"+    Vmin -> "vmin"+    Vw -> "vw"+    K -> ""++unitMap :: HM.HashMap Text PropValType+unitMap = mkDecodingMap++newtype RawNum = RawNum Text+  deriving newtype (Eq, Ord, Show, IsString) deriving (Generic)++mkRawNum :: String -> RawNum+mkRawNum = RawNum . C8.pack++instance CssShow RawNum where+  toCssText (RawNum x) = fromStrict x++data TypedNum = TypedNum RawNum PropValType deriving (Show, Ord, Eq, Generic)++instance CssShow TypedNum where+  toCssText (TypedNum n t) = toCssText n <> toCssText t++tryGet :: PropValType -> TypedNum -> P RawNum+tryGet tt tn@(TypedNum v t)+  | t == tt = pure v+  | otherwise = Failed $ "Expected " <> toCssStr tt <> " but " <> toCssStr tn++parseTypedNum :: NumberStr -> P TypedNum+parseTypedNum = go . break isDigit . reverse+  where+    go (rSuf, rNum) =+      let suf = reverse rSuf in+        case smartLookup (C8.pack suf) unitMap of+          Just pvt ->+            pure $ TypedNum (mkRawNum $ reverse rNum) pvt+          Nothing ->+            Failed $ "Unknown number unit: " <> suf <> " in " <> reverse rNum++parseAsUnitType :: Text -> P PropValType+parseAsUnitType ut =+  case smartLookup ut unitMap of+    Just pvt -> pure pvt+    Nothing -> Failed $ "Unkown number unit: " <> C8.unpack ut
+ src/CssParser/Rule/Value.hs view
@@ -0,0 +1,270 @@+{-# OPTIONS_GHC -Wno-orphans #-}+module CssParser.Rule.Value+  ( module CssParser.Rule.Value+  , reorder+  ) where++import CssParser.Ident ( Ident, Var, PropertyName(..), AttrName )+import CssParser.Prelude+import CssParser.Rule.Type ( CssType )+import CssParser.Rule.TypedNum+    ( TypedNum, RawNum(..), PropValType, mkRawNum )+import CssParser.Show+    ( CssShow(..), ShowSpaceBetween(..), encodeStringLiteral, mayCss )+import Expression.Reorder++newtype Unsigned = Unsigned RawNum+  deriving newtype (Eq, Show, Ord, CssShow)+  deriving (Generic)++data Ratio = Ratio Unsigned Unsigned deriving (Eq, Show, Ord, Generic)++instance CssShow Ratio where+  toCssText (Ratio a b) = toCssText a <> "/" <> toCssText b++readRatio :: String -> Either String Ratio+readRatio s =+  case span (/= '/') s of+    ([], _) -> Left $ "No divisible in Ratio [" <> s <> "]"+    ("/", _) -> Left $ "No divisible in Ratio [" <> s <> "]"+    (_, []) -> Left $ "No divisor in Ratio [" <> s <> "]"+    (_, "/") -> Left $ "No divisor in Ratio [" <> s <> "]"+    (divisibleStr, '/':divisorStr) ->+      Right $ Ratio (Unsigned (mkRawNum  divisibleStr)) (Unsigned (mkRawNum divisorStr))+    (_, _) -> Left $ "No slash in ratio [" <> s <> "]"++data Url+  = Url { unUrl :: Text }+  | UnquotedUrl { unUrl :: Text }+  deriving (Show, Eq, Ord, Generic)+instance CssShow Url where+  toCssText = \case+    Url u -> "url(" <> encodeStringLiteral u <> ")"+    UnquotedUrl u -> "url(" <> fromStrict u <> ")"++data Source = UrlSource Url | StrSource Text+  deriving (Show, Ord, Eq, Generic)++instance CssShow Source where+  toCssText = \case+    UrlSource u -> toCssText u+    StrSource t -> encodeStringLiteral t+++newtype HexColor = HC Text deriving (Eq, Ord, Show, Generic)++instance CssShow HexColor where+  toCssText (HC s) = "#" <> fromStrict s++propRef :: PropertyName -> PropVal+propRef = \case+  PropertyName i -> IdentRef i+  VarProp v -> VarRef v+++data CalcOp = PlusCe | MinusCe | DivCe | ProdCe deriving (Eq, Ord, Show, Enum, Bounded, Generic)++instance CssShow CalcOp where+  toCssText = \case+    PlusCe -> " + "+    MinusCe -> " - "+    DivCe -> " / "+    ProdCe -> " * "++data CalcFns+  = CalcFn+  | MinFn+  | NoFn+  | MaxFn+  | ClampFn+  deriving (Eq, Ord, Show, Bounded, Enum, Generic)++instance CssShow CalcFns where+  toCssText = \case+    CalcFn -> "calc"+    MinFn -> "min"+    NoFn -> ""+    MaxFn -> "max"+    ClampFn -> "clamp"++data NthFormula+  = NthEven Bool+  | NthExpr CalcExpr+  deriving (Eq, Ord, Show, Generic)++instance CssShow NthFormula where+  toCssText = \case+    NthEven True -> "even"+    NthEven False -> "odd"+    NthExpr e -> toCssText e++data CalcExpr+  = BinOpCe CalcExpr CalcOp CalcExpr+  | ValCe TypedNum+  | VarCe PropertyName+  | AppCe PropertyName PropValsList+  | CalcCe CalcFns CalcExprList+  | CalcNeg CalcExpr+  deriving (Eq, Ord, Show, Generic)++instance HasParens CalcExpr where+  stripParens = \case+    BinOpCe l op r -> BinOpCe (stripParens l) op (stripParens r)+    CalcCe f (CalcExprList l) -> CalcCe f . CalcExprList $ fmap stripParens l+    CalcNeg (CalcCe NoFn (CalcExprList (x :| []))) -> stripParens $ CalcNeg x+    CalcNeg o -> CalcNeg $ stripParens o+    o@VarCe {} -> o+    o@ValCe {} -> o+    o@AppCe {} -> o++newtype CalcExprList = CalcExprList (NonEmpty CalcExpr)+  deriving newtype (Eq, Ord)+  deriving (Show, Generic)++instance CssShow CalcExprList where+  toCssText (CalcExprList cs) = intercalate ", " (toCssText <$> toList cs)++class HasParens a where+  stripParens :: a -> a++class HasFixity x where+  fixityOf :: x -> Fixity++instance HasFixity CalcOp where+  fixityOf = \case+    PlusCe -> Fixity AssocLeft 1+    MinusCe -> Fixity AssocLeft 2+    ProdCe -> Fixity AssocLeft 3+    DivCe -> Fixity AssocLeft 4++instance Semigroup a => Monad (Validation a) where+  Failure f >>= _ = Failure f+  Success a >>= f = f a++-- happy builtin capabilites for operator priority is pretty limited+-- %left and %right are just ignored for the gramma+-- AST tree is alway represented as a list always spanning to the right+instance SyntaxTree CalcExpr String where+  reorderChildren = \case+    BinOpCe l op r -> BinOpCe <$> reorder l <*> pure op <*> reorder r+    CalcCe x (CalcExprList args) -> CalcCe x . CalcExprList <$> mapM reorder args+    CalcNeg x -> CalcNeg <$> reorder x+    o -> pure o+  structureOf = \case+    BinOpCe l op r -> NodeInfix (fixityOf op) l r (`BinOpCe` op)+    CalcNeg x -> NodePrefix 5 x CalcNeg+    _ -> NodeLeaf+  makeError err _ = show err++instance CssShow CalcExpr where+  toCssText = \case+    BinOpCe a op b -> toCssText a <> toCssText op <> toCssText b+    ValCe v -> toCssText v+    VarCe v -> toCssText v+    AppCe f a -> toCssText f <> "(" <> toCssText a <> ")"+    CalcCe f a -> toCssText f <> "(" <> toCssText a <> ")"+    CalcNeg x@CalcNeg {} -> "-(" <> toCssText x <> ")"+    CalcNeg x@BinOpCe {} -> "-(" <> toCssText x <> ")"+    CalcNeg x@(VarCe _) -> "- " <> toCssText x+    CalcNeg x@(AppCe _ _) -> "- " <> toCssText x+    CalcNeg x -> "-" <> toCssText x++data AttrType+  = CssTypeAt CssType+  | UnitAt PropValType+  | RawString+  deriving (Show, Eq, Ord, Generic)++instance CssShow AttrType where+  toCssText = \case+    RawString -> " raw-string"+    CssTypeAt x -> " type(" <> toCssText x <> ")"+    UnitAt x -> " " <> toCssText x++data PropVal+  = AlphaF Unsigned+  | AppConst PropertyName+  | AppFun PropertyName PropVals+  | AppFunEnum PropertyName PropValsList+  | AttrFun AttrName (Maybe AttrType) (Maybe PropVal)+  | BoolVal Bool+  | CalcFun CalcExpr+  | Div PropVal PropVal+  | DotVal+  | HexColor HexColor+  | IdentRef Ident+  | IntVal TypedNum+  | RatioVal Ratio+  | StrVal Text+  | UnicodeRangeVal UnicodeRange+  | UrlVal Url+  | VarRef Var+  | BracketVal Ident+  deriving (Eq, Ord, Show, Generic)++newtype LiteralString = LiteralString Text deriving newtype (Eq, Ord, Show, IsString) deriving (Generic)++instance CssShow LiteralString where+  toCssText (LiteralString s) = encodeStringLiteral s++instance CssShow PropVal where+  toCssText = \case+    IntVal i -> toCssText i+    VarRef v -> toCssText v+    IdentRef i -> toCssText i+    HexColor c -> toCssText c+    StrVal s -> encodeStringLiteral s+    UrlVal u -> toCssText u+    BoolVal bv -> toCssText bv+    RatioVal rv -> toCssText rv+    CalcFun ce -> toCssText ce+    BracketVal ce -> "[" <> toCssText ce <> "]"+    AttrFun an at dv ->+      "attr(" <> toCssText an <> toCssText at <> mayCss (", " <>) dv <> ")"+    Div a b -> toCssText a <> " / " <> toCssText b+    AppFun fn args -> toCssText fn <> "(" <> toCssText args <> ")"+    AppFunEnum fn args -> toCssText fn <> "(" <> toCssText args <> ")"+    AppConst fn -> toCssText fn <> "()"+    UnicodeRangeVal ur -> toCssText ur+    AlphaF o -> "alpha(opacity=" <> toCssText o <> ")"+    DotVal -> "."+++data Important = Important deriving (Show, Eq, Ord, Generic)+instance CssShow Important where+  toCssText _  = " !important"++data PropVals = PropVals (NonEmpty PropVal) (Maybe Important) deriving (Show, Eq, Ord, Generic)++instance CssShow PropVals where+  toCssText (PropVals ne mi) =+    unwords $ (toCssText <$> toList ne) <> maybeToList (toCssText <$> mi)++instance ShowSpaceBetween PropVals PropVals where+  cssSpace _ _ = ", "++newtype PropValsList = PropValsList (NonEmpty PropVals) deriving (Show, Eq, Ord, Generic)++instance CssShow PropValsList where+  toCssText (PropValsList l) = toCssText l++mkAppFun :: PropertyName -> NonEmpty PropVals -> PropVal+mkAppFun pn = \case+  (x :| []) -> AppFun pn x+  o -> AppFunEnum pn (PropValsList o)++newtype UnicodeRange = UnicodeRange Text deriving (Show, Eq, Ord, Generic)+instance CssShow UnicodeRange where+  toCssText (UnicodeRange t) = "U+" <> fromStrict t+instance ShowSpaceBetween UnicodeRange UnicodeRange where+  cssSpace _ _ = ", "++newtype CommaSeparatedList+  = CommaSeparatedList (NonEmpty PropVals)+  deriving (Show, Eq, Ord, Generic)++instance ShowSpaceBetween CommaSeparatedList CommaSeparatedList where+  cssSpace _ _ = "; "+type SrcVal = CommaSeparatedList+instance CssShow CommaSeparatedList where+  toCssText (CommaSeparatedList l) = toCssText l
+ src/CssParser/Show.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE RequiredTypeArguments #-}+{-# LANGUAGE UndecidableInstances #-}+module CssParser.Show+  ( module X+  , CssShow (..)+  , ShowSpaceBetween (..)+  , ShowParenthesis (..)+  , Embraced (..)+  , Encurled (..)+  , CslNe (..)+  , SslNe (..)+  , Csl (..)+  , mayCss+  , mkDecodingMap+  , mkDecodingMap'+  , smartLookup+  , toCssStr+  ) where++import CssParser.Prelude+import CssParser.TextMarshal as X+import Data.HashMap.Strict qualified as HM+import Data.Text qualified as T++class ShowSpaceBetween (a :: Type) (b :: Type) where+  cssSpace :: forall aa -> forall bb -> (aa ~ a, bb ~ b) => LText++class CssShow a where+  toCssText :: a -> LText++instance (ShowSpaceBetween a a, CssShow a) => CssShow [a] where+  toCssText = intercalate (cssSpace a a) . fmap toCssText+{- HLINT ignore "Use concatMap" -}++instance (ShowSpaceBetween a a, CssShow a) => CssShow (NonEmpty a) where+  toCssText = toCssText . toList++instance CssShow () where+  toCssText () = " 0 ";++instance CssShow a => CssShow (Maybe a) where+  toCssText = maybe "" ((<> " ") . toCssText)++instance (CssShow a, CssShow b) => CssShow (Either a b) where+  toCssText = \case+    Left e -> toCssText e+    Right v -> toCssText v++instance (CssShow at, CssShow bt, ShowSpaceBetween at bt) => CssShow (These at bt) where+  toCssText = \case+    This a -> toCssText a+    That b -> toCssText b+    These a b -> toCssText a <> cssSpace at bt <> toCssText b++class ShowParenthesis (parent :: Type) (this :: Type) where+  left :: forall pp -> forall tt -> (pp ~ parent, tt ~ this) => LText+  right :: forall pp -> forall tt -> (pp ~ parent, tt ~ this) => LText++newtype Csl a = Csl [a] deriving (Show, Eq, Ord, Generic)++instance CssShow a => CssShow (Csl a) where+  toCssText (Csl l) =  intercalate ", " $ fmap toCssText l++newtype CslNe a = CslNe (NonEmpty a) deriving (Show, Eq, Ord, Generic)++instance CssShow a => CssShow (CslNe a) where+  toCssText (CslNe l) = toCssText . Csl $ toList l++newtype SslNe a = SslNe (NonEmpty a) deriving (Show, Eq, Ord, Generic)++instance CssShow a => CssShow (SslNe a) where+  toCssText (SslNe l) =  unwords .  fmap toCssText $ toList l++newtype Embraced a = Embraced a deriving (Show, Eq, Ord, Generic)+instance CssShow a => CssShow (Embraced a) where+  toCssText (Embraced a) = "("  <> toCssText a <> ")"++newtype Encurled a = Encurled a deriving (Show, Eq, Ord, Generic)+instance CssShow a => CssShow (Encurled a) where+  toCssText (Encurled a) = "{"  <> toCssText a <> "}"++instance CssShow Integer where+  toCssText = numToText++instance CssShow Bool where+  toCssText = \case+    False -> "false"+    True -> "true"++mayCss :: CssShow a => (LText -> LText) -> Maybe a -> LText+mayCss f = maybe "" (f . toCssText)++mkDecodingMap :: forall a. (Enum a, Bounded a, CssShow a) => HM.HashMap Text a+mkDecodingMap = mkDecodingMap' $ enumFromTo minBound maxBound++mkDecodingMap' :: (CssShow a) => [a] -> HM.HashMap Text a+mkDecodingMap' kds = HM.fromList (zip origKeys kds <> zip lowKeys kds)+  where+    origKeys = toStrict . toCssText <$> kds+    lowKeys = T.toLower <$> origKeys++smartLookup :: Text -> HM.HashMap Text a -> Maybe a+smartLookup k m =+  case HM.lookup k m of+    Nothing -> HM.lookup (T.toLower k) m+    o -> o+toCssStr :: CssShow a => a -> String+toCssStr = unpack . toCssText
+ src/CssParser/TextMarshal.hs view
@@ -0,0 +1,40 @@+module CssParser.TextMarshal+  ( module CssParser.TextMarshal+  , readEither+  ) where++import CssParser.Prelude+import Data.Text.Lazy qualified as L+import Data.Text.Lazy.Builder (toLazyText)+import Data.Text.Lazy.Builder.Int (decimal)+import Text.Read (readEither)++numToText :: Integral a => a -> LText+numToText = toLazyText . decimal++showHex :: Int -> ShowS+showHex = go (6 :: Int)+  where+    go 0 _ s = s+    go k n rs = go (k - 1) q (intToDigit r : rs)+      where+        ~(q, r) = quotRem n 16++encodeStringChar :: Char -> LText+encodeStringChar c+  | c `notElem` ("\"\\\n\t\r\f" :: String) = L.singleton c+  | otherwise = cons '\\' (L.pack (showHex (ord c) ""))++encodeStringLiteral :: Text -> LText+encodeStringLiteral t =+  cons c (snoc (L.concatMap encodeStringChar $ fromStrict t) c)+  where+    c = '"'++readHex :: String -> Either String Integer+readHex = \case+  [a, b, c] -> readEither ['0', 'x', a, a, b, b, c, c]+  o -> readEither $ '0':'x':o++readUnquotedUrl :: String -> String+readUnquotedUrl = dropEnd 1 . drop 4
+ src/CssParser/Utils.hs view
@@ -0,0 +1,101 @@+module CssParser.Utils where++import CssParser.List ( _initLast )+import Data.Char (chr, digitToInt, intToDigit, isAsciiLower, isAsciiUpper, isHexDigit, isDigit, ord)+import Data.Text (Text, cons, pack, singleton)+import qualified Data.Text.Lazy as LT+import Prelude++_isQuote :: Char -> Bool+_isQuote '"' = True+_isQuote '\'' = True+_isQuote _ = False++-- | Parses a css string literal to a string that ontains the content of that+-- string literal.+readCssString ::+  -- | The string that contains the string literal in the css selector.+  String ->+  -- | A string that contains the content of the string literal.+  String+readCssString (c : xs) | _isQuote c = f+  where+    f+      | Just (vs, c') <- _initLast xs = g c' vs+      | otherwise = "The string literal should contain at least two quotation marks."+      where+        g c' vs+          | c == c' = _readCssString c vs+          | otherwise = "The start and end quotation mark should be the same."+readCssString _ = error "The string should start with an \" or ' and end with the same quotation."++_readCssString :: Char -> String -> String+_readCssString c' = go+  where+    go [] = []+    go ('\\' : '\n' : xs) = go xs+    go ('\\' : ca@(c : xs))+      | c == c' = c : go xs+      | otherwise = let ~(y, ys) = _parseEscape ca in y : go ys+    go (x : xs)+      | x == c' = error "The string can not contain a " ++ show x ++ ", you should escape it."+      | otherwise = x : go xs++readIdentifier :: String -> String+readIdentifier = _readCssString '\\'++_notEncode :: Char -> Bool+_notEncode x = isAsciiLower x || isAsciiUpper x || x == '-' || x == '_' || isDigit x++-- | Convert a string to a css selector string literal. This is done by putting+-- quotes around the content, and escaping certain characters.+encodeString ::+  -- | The type of quotes that should be put around the content (should be @'@ or @"@).+  Char ->+  -- | The string that should be converted to a css selector string literal.+  String ->+  -- | The corresponding css selector string literal.+  String+encodeString c' = (c' :) . go+  where+    go [] = [c']+    go (c : cs)+      | _notEncode c = c : go cs+      | otherwise = '\\' : _showHex (ord c) (go cs)++encodeCharacter :: Char -> LT.Text+encodeCharacter c+  | _notEncode c = LT.singleton c+  | otherwise = LT.cons '\\' (LT.pack (_showHex (ord c) ""))++_encodeCharacter :: Char -> Text+_encodeCharacter c+  | _notEncode c = singleton c+  | otherwise = cons '\\' (pack (_showHex (ord c) ""))++-- | Encode a given identifier to its css selector equivalent by escaping+-- certain characters.+encodeIdentifier ::+  -- | The identifier to encode.+  Text ->+  -- | The encoded identifier.+  LT.Text+encodeIdentifier = LT.concatMap encodeCharacter . LT.fromStrict++_showHex :: Int -> ShowS+_showHex = go (6 :: Int)+  where+    go 0 _ s = s+    go k n rs = go (k - 1) q (intToDigit r : rs)+      where+        ~(q, r) = quotRem n 16++_parseEscape :: String -> (Char, String)+_parseEscape = go (6 :: Int) 0+  where+    go 0 n cs = yield n cs+    go _ n "" = yield n ""+    go i n ca@(c : cs)+      | isHexDigit c = go (i - 1) (16 * n + digitToInt c) cs+      | otherwise = yield n ca+    yield n s = (chr n, s)
+ test/CssParser/Test/Arbitrary.hs view
@@ -0,0 +1,83 @@+{-# OPTIONS_GHC -fconstraint-solver-iterations=24 #-}+{-# OPTIONS_GHC -Wno-orphans #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+module CssParser.Test.Arbitrary+  ( module X+  , pack+  , module CssParser.Test.Arbitrary+  )where++import Data.List qualified as L+import Data.HashSet ( fromList, member, HashSet )+import Data.Text (pack, tails, inits)+import Data.Text qualified as T+import CssParser.At.MediaQuery ( MediaType )+import CssParser.Prelude as X+import CssParser.Show ( CssShow(toCssText), CslNe(..), Embraced(..), SslNe(..) )+import Test.QuickCheck as X+import Test.QuickCheck.Gen as X+import Test.QuickCheck.Instances as X ()+import Test.QuickCheck.Arbitrary.Generic as X++enumDomain :: forall a. (Enum a, Bounded a) => [a]+enumDomain = enumFromTo (minBound @a) (maxBound @a)++arbitraryLetter :: Gen Char+arbitraryLetter = elements [ 'a' .. 'z' ]++arbitraryHex :: Gen Char+arbitraryHex = elements $ ['0' .. '9'] <> [ 'a' .. 'f' ] <> [ 'A' .. 'F' ]++arbitraryWord :: Gen Text+arbitraryWord = pack <$> listOf1 arbitraryLetter++keywords :: HashSet Text+keywords = fromList $! initL <> fmap toTxt (enumDomain @MediaType)+  where+    toTxt = toStrict . toCssText+    initL =+      T.words "not of or and only src false true from to url x y d r"+      <>+      T.words "cx cy rx ry is dir odd even open min max clamp calc"++arbitraryIdent :: Gen Text+arbitraryIdent = do+  i <- arbitraryText fl nl+  if i `member` keywords+    then pure $ i <> "_"+    else pure i+  where+    fl = ['a' .. 'z'] <> "_"+    nl = fl <> ['-', '0' .. '9']++arbitraryText :: String -> String -> Gen Text+arbitraryText fl nl = do+  fc <- elements fl+  pack . (fc :) <$> listOf (elements nl)++shrinkText :: Text -> [Text]+shrinkText = liftA2 (zipWith (<>)) inits (tails . T.drop 1)++shrinkIdent :: Text -> [Text]+shrinkIdent t+    | T.length t < 2 = []+    | otherwise = L.filter isGood $ shrinkText t+  where+    isGood s = s `isMissing` keywords &&+      (case T.uncons s of+         Nothing -> False+         Just (l, _) -> isLetter l || l == '_')++    isMissing a b = not (a `member` b)++instance Arbitrary a => Arbitrary (Embraced a) where+  arbitrary = Embraced <$> arbitrary+  shrink (Embraced a) =  Embraced <$> shrink a++instance Arbitrary a => Arbitrary (CslNe a) where+  arbitrary = CslNe <$> arbitrary+  shrink (CslNe a) = CslNe <$> shrink a++instance Arbitrary a => Arbitrary (SslNe a) where+  arbitrary = SslNe <$> arbitrary+  shrink (SslNe a) = SslNe <$> shrink a
+ test/CssParser/Test/Arbitrary/At.hs view
@@ -0,0 +1,67 @@+{-# OPTIONS_GHC -fconstraint-solver-iterations=24 #-}+{-# OPTIONS_GHC -Wno-orphans #-}+module CssParser.Test.Arbitrary.At where++import CssParser.At.Keyframe+import CssParser.At.Page+import CssParser.Ident+import CssParser.Norm ( Norm(..) )+import CssParser.Rule.Pseudo+    ( AtomicPseudoClass(Blank), BrowserSpecificIdent(..) )+import CssParser.Rule.Value ( Source(..) )+import CssParser.Test.Arbitrary+import CssParser.Test.Arbitrary.Ident ()+import CssParser.Test.Arbitrary.Value ()+import Data.Text (isPrefixOf)+import Data.Text qualified as T++browserPrefixes :: [Text]+browserPrefixes =  T.words "-moz- -ms- -webkit- -apple- -o-"++instance Arbitrary BrowserSpecificIdent where+  arbitrary =+    prependIfMissing <$> elements browserPrefixes <*> arbitrary+    where+      prependIfMissing pre = \case+        o@(Ident x)+          | any (`isPrefixOf` x) browserPrefixes ->+            BrowserSpecificIdent o+          | otherwise ->+            BrowserSpecificIdent (Ident $ pre <> x)++  shrink = filter skipBadUpc . genericShrink+    where+      skipBadUpc (BrowserSpecificIdent (Ident x)) =  T.length x > 9++deriving via (GenericArbitrary AtomicPseudoClass) instance Arbitrary AtomicPseudoClass++instance Arbitrary Charset where+  arbitrary = Charset <$> elements ["UTF-8", "iso-8859-15"]++instance Arbitrary Source where+  arbitrary =+    oneof+    [ UrlSource <$> arbitrary+    , StrSource <$> arbitraryWord+    ]+deriving via (GenericArbitrary LayerName) instance Arbitrary LayerName++deriving via (GenericArbitrary PageMargin) instance Arbitrary PageMargin++deriving via (GenericArbitrary PageName) instance Arbitrary PageName+deriving via (GenericArbitrary PageSelectorList) instance Arbitrary PageSelectorList++instance Norm PageSelector where+  normalize = \case+    PageSelector Nothing [] -> PageSelector Nothing [Blank]+    o -> o++instance Arbitrary PageSelector where+  arbitrary = normalize <$> genericArbitrary+  shrink = filter (/= PageSelector Nothing []) . genericShrink++deriving via (GenericArbitrary KeyframeSet) instance Arbitrary KeyframeSet+deriving via (GenericArbitrary KeyframeSetName) instance Arbitrary KeyframeSetName+deriving via (GenericArbitrary Keyframe) instance Arbitrary Keyframe+deriving via (GenericArbitrary KeyframeAdr) instance Arbitrary KeyframeAdr+deriving via (GenericArbitrary PropEntry) instance Arbitrary PropEntry
+ test/CssParser/Test/Arbitrary/Container.hs view
@@ -0,0 +1,39 @@+{-# OPTIONS_GHC -fconstraint-solver-iterations=24 #-}+{-# OPTIONS_GHC -Wno-orphans #-}+module CssParser.Test.Arbitrary.Container where++import CssParser.At.Container+    ( ContainerQuery, CqOp(CqOpFeature), ContainerQueryMap(..) )+import CssParser.At.MediaQuery ( Not, toPlainMf )+import CssParser.Norm ( Norm(..) )+import CssParser.Rule.Value ( PropVal(IntVal), PropVals(PropVals) )+import CssParser.Rule.TypedNum+    ( TypedNum(TypedNum), RawNum(RawNum), PropValType(Mm) )+import CssParser.Test.Arbitrary+    ( Applicative(pure),+      Maybe(Nothing),+      (<$>),+      genericShrink,+      Arbitrary(..),+      Gen(MkGen),+      genericArbitrary,+      GenericArbitrary(GenericArbitrary) )+import CssParser.Test.Arbitrary.Ident ()+import CssParser.Test.Arbitrary.At ()+import CssParser.Test.Arbitrary.Media ()++instance Norm CqOp where+  normalize = \case+    CqOpFeature mf ->+      let zero = IntVal (TypedNum (RawNum "0") Mm) in+        CqOpFeature (toPlainMf (PropVals (pure zero) Nothing) mf)+    o -> o++deriving via (GenericArbitrary (Not ContainerQuery CqOp)) instance Arbitrary (Not ContainerQuery CqOp)+deriving via (GenericArbitrary ContainerQuery) instance Arbitrary ContainerQuery++instance Arbitrary CqOp where+  arbitrary = normalize <$> genericArbitrary+  shrink = normalize <$> genericShrink++deriving via (GenericArbitrary ContainerQueryMap) instance Arbitrary ContainerQueryMap
+ test/CssParser/Test/Arbitrary/File.hs view
@@ -0,0 +1,11 @@+{-# OPTIONS_GHC -fconstraint-solver-iterations=24 #-}+{-# OPTIONS_GHC -Wno-orphans #-}+module CssParser.Test.Arbitrary.File where++import CssParser.File ( CssFile )+import CssParser.Test.Arbitrary+import CssParser.Test.Arbitrary.At ()+import CssParser.Test.Arbitrary.Media ()+import CssParser.Test.Arbitrary.Rule ()++deriving via (GenericArbitrary CssFile) instance Arbitrary CssFile
+ test/CssParser/Test/Arbitrary/FontFeatureValues.hs view
@@ -0,0 +1,14 @@+{-# OPTIONS_GHC -fconstraint-solver-iterations=24 #-}+{-# OPTIONS_GHC -Wno-orphans #-}+module CssParser.Test.Arbitrary.FontFeatureValues where++import CssParser.At.FontFeatureValues+import CssParser.Test.Arbitrary+import CssParser.Test.Arbitrary.Ident ()+import CssParser.Test.Arbitrary.Value ()+import CssParser.Test.Arbitrary.At ()++deriving via (GenericArbitrary IdentList) instance Arbitrary IdentList+deriving via (GenericArbitrary FontFeatureValuesSubBlock) instance Arbitrary FontFeatureValuesSubBlock+deriving via (GenericArbitrary FontFeatureValues) instance Arbitrary FontFeatureValues+deriving via (GenericArbitrary FontFeatureEntry) instance Arbitrary FontFeatureEntry
+ test/CssParser/Test/Arbitrary/FontPaletteValues.hs view
@@ -0,0 +1,11 @@+{-# OPTIONS_GHC -fconstraint-solver-iterations=24 #-}+{-# OPTIONS_GHC -Wno-orphans #-}+module CssParser.Test.Arbitrary.FontPaletteValues where++import CssParser.At.FontPaletteValues ( FontPaletteValues )+import CssParser.Test.Arbitrary+    ( Arbitrary, Gen(MkGen), GenericArbitrary(GenericArbitrary) )+import CssParser.Test.Arbitrary.Ident ()+import CssParser.Test.Arbitrary.At ()++deriving via (GenericArbitrary FontPaletteValues) instance Arbitrary FontPaletteValues
+ test/CssParser/Test/Arbitrary/Function.hs view
@@ -0,0 +1,21 @@+{-# OPTIONS_GHC -fconstraint-solver-iterations=24 #-}+{-# OPTIONS_GHC -Wno-orphans #-}+module CssParser.Test.Arbitrary.Function where++import CssParser.At.Function ( FunArg, ConstEntry, Function )+import CssParser.Test.Arbitrary+    ( genericShrink,+      Arbitrary(..),+      Gen(MkGen),+      genericArbitrary,+      GenericArbitrary(GenericArbitrary) )+import CssParser.Test.Arbitrary.Ident ()+import CssParser.Test.Arbitrary.Value ()+import CssParser.Test.Arbitrary.At ()++deriving via (GenericArbitrary FunArg) instance Arbitrary FunArg+deriving via (GenericArbitrary ConstEntry) instance Arbitrary ConstEntry++instance Arbitrary r => Arbitrary (Function r) where+  arbitrary = genericArbitrary+  shrink = genericShrink
+ test/CssParser/Test/Arbitrary/Ident.hs view
@@ -0,0 +1,36 @@+{-# OPTIONS_GHC -fconstraint-solver-iterations=24 #-}+{-# OPTIONS_GHC -Wno-orphans #-}++module CssParser.Test.Arbitrary.Ident where++import CssParser.Ident+import CssParser.Descriptor+import CssParser.Test.Arbitrary+import Data.Text as T++instance Arbitrary Ident where+  arbitrary = Ident <$> arbitraryIdent+  shrink (Ident a) = Ident <$> shrinkIdent a++instance Arbitrary Descriptor where+  arbitrary = (\case+                  BrowserSpecificDescriptor Na i -> BrowserSpecificDescriptor Opera i+                  o -> o) <$> genericArbitrary++deriving via (GenericArbitrary KnownDescriptor) instance Arbitrary KnownDescriptor+deriving via (GenericArbitrary BrowserPrefix) instance Arbitrary BrowserPrefix+deriving via (GenericArbitrary Var) instance Arbitrary Var+deriving via (GenericArbitrary PropertyName) instance Arbitrary PropertyName++deriving via (GenericArbitrary Namespace) instance Arbitrary Namespace+deriving via (GenericArbitrary AttrName) instance Arbitrary AttrName+deriving via (GenericArbitrary CustomSelectorName) instance Arbitrary CustomSelectorName++instance Arbitrary TagName where+  arbitrary = frequency+    [ (1, pure NoTag)+    , (2, pure AsteriskTag)+    , (3, pure AmpersandTag)+    , (6, TagName . Ident <$> elements (T.words "div p b i a span s"))+    ]+  shrink _ = []
+ test/CssParser/Test/Arbitrary/Media.hs view
@@ -0,0 +1,63 @@+{-# OPTIONS_GHC -fconstraint-solver-iterations=24 #-}+{-# OPTIONS_GHC -Wno-orphans #-}+module CssParser.Test.Arbitrary.Media where++import CssParser.At.CustomMedia ( CustomMediaQuery(..) )+import CssParser.At.MediaQuery+import CssParser.Norm ( Norm(..) )+import CssParser.Rule.Value+import CssParser.Test.Arbitrary+import CssParser.Test.Arbitrary.Ident ()+import CssParser.Test.Arbitrary.Value ()++stripTopPar :: PropVal -> PropVal+stripTopPar = \case+  CalcFun (CalcCe NoFn x) -> CalcFun (CalcCe CalcFn x)+  o -> o++instance Norm MediaFeature where+  normalize = \case+    OpenRangeFeatureFlipped v@IdentRef {} r i -> OpenRangeFeature i (flipRel r) v+    OpenRangeFeatureFlipped v@VarRef {} r i -> OpenRangeFeature i (flipRel r) v+    OpenRangeFeatureFlipped x r i -> OpenRangeFeatureFlipped (stripTopPar x) r i+    MfClosedRange lv MfEq i _ _ -> PlainMf i $ PropVals (pure lv) Nothing+    MfClosedRange lv l i r rv -> MfClosedRange (stripTopPar lv) l i r rv+    o -> o++instance Arbitrary MediaFeature where+  arbitrary = normalize <$> genericArbitrary+  shrink x = normalize <$> genericShrink x++flipRel :: MfRelation -> MfRelation+flipRel = \case+  MfEq -> MfEq+  MfGt -> MfLt+  MfLt -> MfGt+  MfGe -> MfLe+  MfLe -> MfGe++deriving via (GenericArbitrary BinOp) instance Arbitrary BinOp+deriving via (GenericArbitrary MediaCondition) instance Arbitrary MediaCondition+deriving via (GenericArbitrary MfRelation) instance Arbitrary MfRelation+deriving via (GenericArbitrary MtModifier) instance Arbitrary MtModifier+deriving via (GenericArbitrary MediaType) instance Arbitrary MediaType++instance Norm MediaQuery where+  normalize = \case+    MediaQueryConditionOnly mc -> MediaQueryConditionOnly $ stripParens mc+    MediaQueryWithMt md mt mc -> MediaQueryWithMt md mt (stripParens <$> mc)++instance Arbitrary MediaQuery where+  arbitrary = normalize <$> genericArbitrary+  shrink x = normalize <$> genericShrink x++deriving via (GenericArbitrary MediaQueryList) instance Arbitrary MediaQueryList++instance Norm CustomMediaQuery where+  normalize = \case+    CustomMediaQuery (MediaQueryList []) -> CustomMediaFlag False+    o -> o++instance Arbitrary CustomMediaQuery where+  arbitrary = normalize <$> genericArbitrary+  shrink x = normalize <$> genericShrink x
+ test/CssParser/Test/Arbitrary/MonoPair.hs view
@@ -0,0 +1,15 @@+{-# OPTIONS_GHC -fconstraint-solver-iterations=24 #-}+{-# OPTIONS_GHC -Wno-orphans #-}++module CssParser.Test.Arbitrary.MonoPair where++import CssParser.MonoPair+import CssParser.Test.Arbitrary++instance Arbitrary a => Arbitrary (MonoPair a) where+  arbitrary =+    frequency+    [ (1, pure EmptyPair)+    , (4, HalfPair <$> arbitrary)+    , (3, FullPair <$> arbitrary <*> arbitrary)+    ]
+ test/CssParser/Test/Arbitrary/Rule.hs view
@@ -0,0 +1,94 @@+{-# OPTIONS_GHC -fconstraint-solver-iterations=24 #-}+{-# OPTIONS_GHC -Wno-orphans #-}+{-# LANGUAGE RecordWildCards #-}+module CssParser.Test.Arbitrary.Rule where++import CssParser.At.Supports ( FqFun )+import CssParser.At.Supports qualified as S+import CssParser.Ident+    ( CaseSensetivity, TagName(NoTag, AsteriskTag) )+import CssParser.Norm ( normUntilConst, Norm(..) )+import CssParser.Prelude+import CssParser.Rule+import CssParser.Rule.Pseudo ( Language(..), PseudoElement )+import CssParser.Rule.Value ( PropValsList(PropValsList) )+import CssParser.At.Import+    ( Import(ImportUrlSupports, ImportUrlLayer) )+import CssParser.Test.Arbitrary+import CssParser.Test.Arbitrary.At ()+import CssParser.Test.Arbitrary.Container ()+import CssParser.Test.Arbitrary.FontFeatureValues ()+import CssParser.Test.Arbitrary.FontPaletteValues ()+import CssParser.Test.Arbitrary.Function ()+import CssParser.Test.Arbitrary.Ident ()+import CssParser.Test.Arbitrary.Media ()+import CssParser.Test.Arbitrary.MonoPair ()+import Data.List ( null )++instance Arbitrary Language where+  arbitrary = Language <$> elements ["en", "af-ZA", "ar", "de", "ar-BH", "pl", "ru"]++isPartialTagSelector :: TagSelector -> Bool+isPartialTagSelector TagSelector {..} =+  tagName == NoTag && null tagSubSelectors++instance Arbitrary TagSelector where+  arbitrary = do+    ts <- genericArbitrary+    if isPartialTagSelector ts+      then pure ts { tagName = AsteriskTag }+      else pure ts+  shrink = filter (not . isPartialTagSelector) . genericShrink++instance Norm Selector where+  normalize = \case+    Selector _ fts ots -> Selector Nothing fts ots+    PeSelector _ fts ots pe -> PeSelector Nothing fts ots pe+    PeSelectorOnly pe -> PeSelectorOnly pe++instance Arbitrary Selector where+  arbitrary = normalize <$> genericArbitrary+  shrink x = fmap normalize (genericShrink x)++deriving via (GenericArbitrary TagRelation) instance Arbitrary TagRelation+deriving via (GenericArbitrary TagSubSelector) instance Arbitrary TagSubSelector++instance Norm CssRuleBodyItem where+  normalize = \case+    CssEnumLeaf pn (PropValsList (x :| [])) -> CssLeafRule pn x+    o -> o++deriving via (GenericArbitrary AtRule) instance Arbitrary AtRule+deriving via (GenericArbitrary CssRule) instance Arbitrary CssRule++instance Arbitrary CssRuleBodyItem where+  arbitrary = normalize <$> genericArbitrary+  shrink x = normalize <$>  genericShrink x++deriving via (GenericArbitrary AttrOp) instance Arbitrary AttrOp+deriving via (GenericArbitrary PseudoElement) instance Arbitrary PseudoElement+deriving via (GenericArbitrary CompositePe) instance Arbitrary CompositePe+deriving via (GenericArbitrary PseudeTagSelector) instance Arbitrary PseudeTagSelector+deriving via (GenericArbitrary CaseSensetivity) instance Arbitrary CaseSensetivity+deriving via (GenericArbitrary AtrPat) instance Arbitrary AtrPat++deriving via (GenericArbitrary (FqFun SelectorList)) instance Arbitrary (FqFun SelectorList)++instance Arbitrary FeatureQuery where+  arbitrary = normUntilConst <$> genericArbitrary+  shrink = normUntilConst <$> genericShrink++expandToParen :: FeatureQuery ->  FeatureQuery+expandToParen = \case+  S.FqParen x -> expandToParen x+  o -> o++instance Norm (Import SelectorList) where+  normalize = \case+    ImportUrlLayer src ln mfq mqs -> ImportUrlLayer src ln (expandToParen <$> mfq) mqs+    ImportUrlSupports src mfq mqs -> ImportUrlSupports src (expandToParen <$> mfq) mqs+    o -> o++instance Arbitrary (Import SelectorList) where+  arbitrary = normalize <$> genericArbitrary+  shrink = normalize <$> genericShrink
+ test/CssParser/Test/Arbitrary/Value.hs view
@@ -0,0 +1,164 @@+{-# OPTIONS_GHC -fconstraint-solver-iterations=24 #-}+{-# OPTIONS_GHC -Wno-orphans #-}+module CssParser.Test.Arbitrary.Value where++import CssParser.Ident+import CssParser.Norm ( Norm(..) )+import CssParser.Parser.Monad ( reorderErr )+import CssParser.Rule.Value+import CssParser.Rule.Type ( CssType, CssLeafType, AtomicCssType )+import CssParser.Rule.TypedNum ( TypedNum(..), RawNum(..), PropValType (Mm, K), mkRawNum )+import CssParser.Test.Arbitrary+import CssParser.Test.Arbitrary.Ident ()+import Data.Text qualified as T++instance Norm NthFormula where+  normalize = \case+    NthExpr e -> NthExpr . normNeg . stripParens $ reorderErr e+    o -> o++instance Arbitrary NthFormula where+  arbitrary = normalize <$> genericArbitrary+  shrink x = normalize <$> genericShrink x++deriving via (GenericArbitrary CssType) instance Arbitrary CssType+deriving via (GenericArbitrary CssLeafType) instance Arbitrary CssLeafType+deriving via (GenericArbitrary AtomicCssType) instance Arbitrary AtomicCssType++data Anum+  = IntAnum Int+  | PositiveIntAnum Word+  | FloatAnum Int Word+  | EAnum Int Int+  | NoWholeAnum Word+  | EAnum2 Int Word Int+  deriving (Eq, Generic)++instance Show Anum where+  show = \case+    IntAnum x -> show x+    PositiveIntAnum x -> "+" <> show x+    FloatAnum x y -> show x <> "." <> show y+    EAnum x y -> show x <> "e" <> show y+    EAnum2 x y z -> show x <> "." <> show y <> "e" <> show z+    NoWholeAnum x -> "." <> show x++deriving via (GenericArbitrary Anum) instance Arbitrary Anum++deriving via (GenericArbitrary Important) instance Arbitrary Important++instance Arbitrary RawNum where+  arbitrary = mkRawNum . show <$> (arbitrary :: Gen Anum)+  shrink _ = []++instance Arbitrary HexColor where+  arbitrary = HC . pack <$> vectorOf 6 arbitraryHex+  shrink (HC x)+    | T.length x == 6 = [HC $ T.take 3 x]+    | otherwise = []+++instance Arbitrary Unsigned where+  arbitrary = Unsigned . RawNum . pack <$> listOf1 (elements [ '0' .. '9' ])+  shrink = genericShrink+instance Arbitrary Url where+  arbitrary = oneof+    [ pure $ Url "https://ooo.com/aoeu/style.css"+    , pure $ UnquotedUrl "https://ooo.com:443/aoeu/style.css?y=3&x=ok#eoeu"+    , pure $ UnquotedUrl "./file.css"+    , pure $ UnquotedUrl "/style.css"+    , pure $ UnquotedUrl "/../style.css"+    ]++deriving via Ident instance Arbitrary LiteralString+deriving via (GenericArbitrary Ratio) instance Arbitrary Ratio+deriving via (GenericArbitrary PropVals) instance Arbitrary PropVals+deriving via (GenericArbitrary PropValsList) instance Arbitrary PropValsList+deriving via (GenericArbitrary PropValType) instance Arbitrary PropValType++deriving via (GenericArbitrary CalcFns) instance Arbitrary CalcFns+deriving via (GenericArbitrary CalcOp) instance Arbitrary CalcOp++isD :: Char -> Bool+isD x = isDigit x || x == '.'++normNeg :: CalcExpr -> CalcExpr+normNeg = \case+  o@(CalcNeg (ValCe (TypedNum (RawNum rn) pt))) ->+     case T.uncons rn of+      Just ('-', absRn) -> ValCe (TypedNum (RawNum absRn) pt)+      Just ('+', absRn) ->+        case T.uncons absRn of+          Just (fc, _)+            | isD fc -> ValCe (TypedNum (RawNum (T.cons '-' absRn)) pt)+            | otherwise -> ValCe (TypedNum (RawNum absRn) pt)+          Nothing -> o+      Just (fc, _)+        | isD fc -> ValCe (TypedNum (RawNum (T.cons '-' rn)) pt)+        | otherwise -> o+      _ -> o+  CalcNeg (CalcNeg x) -> normNeg x+  CalcNeg x -> CalcNeg $ normNeg x+  BinOpCe l op r -> BinOpCe (normNeg l) op (normNeg r)+  CalcCe f (CalcExprList l) -> CalcCe f . CalcExprList $ fmap normNeg l+  o -> o++deriving via (GenericArbitrary CalcExpr) instance Arbitrary CalcExpr+deriving via (GenericArbitrary TypedNum) instance Arbitrary TypedNum+deriving via (GenericArbitrary CalcExprList) instance Arbitrary CalcExprList++rightMost :: PropVal -> PropVal+rightMost = \case+  Div _ y -> rightMost y+  o -> o++instance Norm CalcExpr where+  normalize = normNeg++instance Norm PropVal where+  normalize = \case+    Div x y -> Div (rightMost y) (normalize x)+    AppFunEnum f (PropValsList (a :| [])) -> AppFun f a+    CalcFun ce@CalcCe {} -> CalcFun . normNeg . stripParens $ reorderErr ce+    CalcFun ce ->+      CalcFun . CalcCe CalcFn . CalcExprList . (:| []) . normNeg . stripParens $ reorderErr ce+    o -> o++instance Arbitrary PropVal where+  arbitrary = normalize <$> genericArbitrary+  shrink = normalize <$> genericShrink++deriving via (GenericArbitrary CommaSeparatedList) instance Arbitrary CommaSeparatedList++instance Norm AttrType where+  normalize = \case+    UnitAt K -> UnitAt Mm+    o -> o++instance Arbitrary AttrType where+  arbitrary = normalize <$> genericArbitrary+  shrink x = normalize <$> genericShrink x++unPatternLetter :: Gen Char+unPatternLetter = elements ( '?' : ['0' .. '9' ] <> ['a' .. 'f' ])++unPattern :: Gen Text+unPattern = do+  s <- unPatternLetter+  b <- sublistOf =<< vectorOf 3 unPatternLetter+  f <- maybeToList <$> elements [  Nothing, Just '0' ]+  p <- maybeToList <$> elements [  Nothing, Just '1' ]+  pure (pack $ p ++ f ++ b ++ [s])++unDoublePattern :: Gen Text+unDoublePattern = liftA2 (\a b -> a <> "-" <> b) unPattern unPattern++instance Arbitrary UnicodeRange where+  arbitrary = UnicodeRange <$> oneof [unPattern, unDoublePattern]+  shrink (UnicodeRange ur) =+    case T.dropEnd 1 ur of+      ur'->+        case T.unsnoc ur' of+          Just (ur'', '-') -> shrink (UnicodeRange ur'')+          Just (_, _) -> [UnicodeRange ur']+          Nothing -> []
+ test/Spec.hs view
@@ -0,0 +1,621 @@+-- cabal test   --test-option=--quickcheck-tests=10 --test-option=--quickcheck-max-size=22 --test-option=--hide-successes+-- cabal test   --test-option=--quickcheck-tests=11  --test-option=--quickcheck-max-size=20    --test-option=--quickcheck-verbose --test-option=--quickcheck-replay="(SMGen 1498744052230560514 7024820165127764247,10)"+module Main where++import CssParser+    ( CssShow(toCssText),+      P(Ok, Failed),+      CssFile,+      getToken,+      alex,+      parseCssP,+      parseCss )+import CssParser.Test.Arbitrary.File ()+import CssParser.Test.Arbitrary.Media ()+import CssParser.Utils (encodeString, readCssString, encodeIdentifier, readIdentifier)+import Data.Text (pack)+import Data.Text.Lazy qualified as TL+import Prelude+import Test.Tasty ( defaultMain, testGroup, TestTree )+import Test.Tasty.HUnit ( testCase, (@=?) )+import Test.Tasty.QuickCheck+    ( (===), {-label,-} withMaxSize, withMaxSuccess, Property, testProperty )++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "CssParser"+  [ testGroup "Encode-decode"+    [ testProperty "Encode-decode identity 1" (encodeDecode '"')+    , testProperty "Encode-decode identity 2" (encodeDecode '\'')+    , testProperty "Encode-decode identifier" encodeDecodeId+    ]+  , testGroup "Examples"+    [ testGroup "Empty body"+      (fmap (\x -> cpt x (x <> " {}")) validSelectors)+    , testGroup "Nested1"+      (fmap (\x -> cpt x (x <> " {\n" <> x <> "{\t}\r}"))+        validSelectors)+    , testGroup "Layer"+      [ testGroup "Stmt"+        (fmap (\x -> cpt x (x <> ";")) layerStmt)+      ]+    , testGroup "Properties"+      (fmap (\x -> cpt x ("p { " <> x <> " }")) properties)+    , testGroup "At"+      (fmap (\x -> testCase x (True @=? checkParse x)) at)+    ]+  , testGroup "Arbitrary "+    [ testProperty "Alex" (throttle encodeDecodeAlex)+    , testProperty "Encode-decode CSS identity"+      -- cabal test with by default runs about a minute+      (throttle encodeDecodeCss)+    ]+  ]+  where+    throttle x =+      let l = 66 in+        withMaxSize l (withMaxSuccess l x)+    cpt m x = testCase m (True @=? checkParse (x <> " {}"))++encodeDecode :: Char -> String -> Bool+encodeDecode c b = readCssString (encodeString c b) == b++encodeDecodeId :: String -> Bool+encodeDecodeId b = readIdentifier (TL.unpack (encodeIdentifier (pack b))) == b++encodeDecodeAlex :: CssFile -> Property+encodeDecodeAlex cf =+  -- label cfCss $+    case alex cfCss of+      Left e -> error $ "Alex failed: " <> e <> "After:\n" <> cfCss+      Right v -> v === v+  where+   cfCss = TL.unpack $ toCssText cf+++encodeDecodeCss :: CssFile -> Bool+encodeDecodeCss sg =+  case parseCssP sgTxt of+    Failed e ->+      error $ "Failed to parse:\n" <> sgTxt <>+      "\nTokens:\n" <>  show (fmap getToken <$> alex sgTxt) <>+      "\nError: " <> e+    Ok parsedSg+      | sg == parsedSg -> True+      | otherwise ->+        error $+          "CSS Input:\n" <> sgTxt <>+          "\nCSS Print:\n" <> TL.unpack (toCssText parsedSg) <>+          "\nParsed:\n" <> show parsedSg <>+          "\nSHOWED INPUT: "+  where+    sgTxt = TL.unpack $ toCssText sg++checkParse :: String -> Bool+checkParse x = y == y+  where y = parseCss x++at :: [String]+at =+  colorProfile <> fontFace <> fontFeatureValues <> fontPaletteValues <>+  container <> misc <> supports <> unknown <> media <> page <> layer <>+  atImport <> atFunction <> keyframe <> atProperty <> customMedia++colorProfile :: [String]+colorProfile =+  [ "@color-profile xx { src: url(\"https://example.org/SWOP2006_Coated5v2.icc\"); }"+  , "@color-profile y { src: url(example.org); src: url(example.com); }"+  ]++atProperty :: [String]+atProperty =+  [ "@property --ga { syntax: \"<angle>\"; initial-value: 0deg; inherits: false; }"+  ]++fontFace :: [String]+fontFace =+  [ "@font-face { src: local(\"x\"), format(\"z\") url(\"x.otf\"); unicode-range: U+11F?; }"+  , "@font-face {unicode-range:u+f003,u+f006}"+  , "/* */ @font-face { src: local(\"x\"); }"+  ]++fontFeatureValues :: [String]+fontFeatureValues =+  [ "@font-feature-values One Two { font-display: swap; @styleset { nice-style: 12; }}"+  ]++fontPaletteValues :: [String]+fontPaletteValues =+  [ "@font-palette-values --Ax {font-family: fa;}"+  , "@font-palette-values --Ax {font-family: fa; override-colors: 0 #00ffbb;}"+  , "@font-palette-values --Ax {font-family: fa; override-colors: 0 #fff;}"+  ]++container :: [String]+container =+  [ "@container not scroll-state(x: none) { }"+  , "@container foo (x: none) { }"+  , "@container foo  { }"+  , "@container foo (width > 100px) { }"+  , "@container style(--theme: one) or style(--theme: two) {}"+  ]++misc :: [String]+misc =+  [ "@position-try --x {x: 1px;}"+  , "@starting-style {}"+  , "@mixin input;"+  , "@define-mixin m1 { @mixin input; display: block; }"+  , "@custom-selector :--ti input[type='dt'], input[type='tm'];"+  , "[data-bs-theme=light]{--bs-btn-close-filter: }"+  , "a{a:muted, |*{}}"+  , "a{a:muted, a #f{}}"+  , "a{a:muted, a b{}}"+  , "a{a:muted, a + #y::first-line{}}"+  , "a{a:muted, a > .x::first-line{}}"+  , "a{src:muted, a b;}"+  , "a{src:muted, a b}"+  , "strong{color:currentColor}"+  , "@charset \"utf-8\"; /* */ /* */ @namespace \"x\"; "+  , "@namespace \"x\"; /* */ /* */ @namespace \"y\";"+  , "@layer a, b; /* */ /* */ @namespace \"y\";"+  , "@view-transition {}"+  , "@scope { x: 1rem; }"+  , "@scope { :scope {x: 1rem;} }"+  , "div { > p { --x: 1px; }}"+  , "div {>p{ --x: 1px; }}"+  , "div {p{ --x: 1px; }}"+  , "p{}/* */ "+  , "p{/* */ }"+  , "/* \n */p{ /* \n */ }"+  ]++supports :: [String]+supports =+  [ "@supports (transform-style: preserve-3d) or\n" <>+      "( (-moz-transform-style: preserve-3d) or (-webkit-transform-style: preserve-3d)  ) {  }"+  , "@supports not (transform-origin: 10em 10em 10em) {}"+  , "@supports (transform-origin: 5% 5%) { }"+  , "@supports not (not (transform-origin: 2px)) { }"+  , "@supports selector(h2 > p) and font-tech(color-COLRv1) or font-format(opentype) {}"+  , "@supports (display: grid) and (not (display: inline-grid)) { }"+  , "@supports (mask:url()){}"+  ]++unknown :: [String]+unknown =+  [ "@foobar{margin: 20px;}"+  , "@foobar {margin: 20px;}"+  , "@foobar aoeu {margin: 20px;}"+  , "@foobar aoeu{margin: 20px;}"+  , "@foobar \"oeu\" {margin: 20px;}"+  , "@foobar ao, eu {margin: 20px;}"+  ]++properties :: [String]+properties =+  [ "margin: 20px;"+  , "margin: 2.0px;"+  , "font-size: clamp(1mm / 2, 2mm * 2mm, 3mm + 1cm);"+  , "grid-template-columns: repeat(12, [col-start] 1fr);"+  , "padding-block: max(2 / 2 - 0.06rem);"+  , "padding-block: min((var(--c) - var(--t)) / 2 - 0.06rem, var(--t));"+  , "--uk: calc(-x);"+  , "--uk: .;"+  , "--xx: ( var(--v, 1.28572) * 1em );"+  , "filter:alpha(opacity=0)"+  , "grid-gap: 2;"+  , "fill:attr(data-x);"+  , "fill:attr(data-count type(<number>), 0);"+  , "fill:attr(data-width px, inherit);"+  , "fill:attr(data-width %);"+  , "fill:attr(data-something, \"default\");"+  , "fill:attr(data-size rem);"+  , "fill:attr(data-name raw-string);"+  , "fill:attr(id type(<custom-ident>));"+  , "fill:attr(data-count type(<number>));"+  , "all:U+0;"+  , "min-width: 0\\0;"+  , "font-feature-settings:\"kern\"1, \"liga\" 0;"+  , "margin: 1fr 2Hz 3kHz;"+  , "word-wrap: normal;"+  , "color:currentColor;"+  , "isolation:isolate;"+  , "--00-l: var(--bulma-white-00-l);"+  , "--bulma-table-cell-text-align:left;"+  , "border:ActiveText 2px solid;"+  , "border:ActiveText, solid;"+  , "border:ActiveText !important;"+  , "background-position:right -1rem center;"+  , "background-position:right;"+  , "-o-object-fit: contain !important;"+  , "cursor:not-allowed;"+  , "--bs-btn-font-family: ;"+  , "--x: hsla(var(--bulma-white-h), 1);"+  , "y: 1px; /* */ x: 100vh;"+  , "/* */ x: 100vh;"+  , "x: 100vh; /* */ "+  , "margin: 10e2px;"+  , "margin: -0.0px;"+  , "margin: -3.4e-2;"+  , "-moz-width: calc(2em * 5);"+  , "-webkit-width: calc( 2em / 5 );"+  , "width: calc(2em * 5);"+  , "width: calc(10px + 100px );"+  , "width: calc(1px+2px+3px);"+  , "width: calc( 10px+100px );"+  , "width: calc(100% - 30px);"+  , "width: calc(100%-30px);"+  , "width: calc( (var(--x) / var(--y) * 2) - x);" -- --f(3Q));"+  , "width: calc(var(--variable-width) + 20px);"+  , "margin: 1px !important;"+  , "font-family: -apple-system;"+  , "font-size: calc(var(--scale, 1) * 1em);"+  , "margin: 3cm, 1px !important ;"+  , "font-family: one, two, three,four , five;"+  , "--c: 0.0lvmax +0svmin; "+  , "--my-prop: 20px ;"+  , "padding: 12em 20mm;"+  , "padding: 12rem 25pt 2px 20mm;"+  , "margin-top: auto;"+  , "display: none;"+  , "forced-colors:active;"+  , "font-style: oblique 20deg 50deg;"+  , "transform: rotate(45deg);"+  , "transform: rotate(4grad);"+  , "transform: rotate(4turn);"+  , "transform: rotate(4rad);"+  , "border: 1px solid green;"+  , "mask:url();"+  , "x: url(\"https://example.org/SWOP2006_Coated5v2.icc\");"+  , "x: url(./file.jpg);"+  , "x: url(/file.jpg);"+  , "x: url(http://site.com:443/file.jpg);"+  , "x: url(http://site.com:443/file.jpg?yyy=oooo#aoeuao);"+  , "x: format(\"opentype\");"+  , "x: format(\"opentype\") tech(color);"+  , "container: x / y;"+  ]++page :: [String]+page =+  [ "@page {}"+  , "@page one {}"+  , "@page xxx:first{}"+  , "@page :blank eee {}"+  , "@page { @top-left {} }"+  ]++layerStmt :: [String]+layerStmt =+  [ "@layer ao-euth"+  , "@layer l1"+  , "@layer l1, and, oo "+  ]++layer :: [String]+layer =+  [ "@layer {}"+  , "@layer l1 {}"+  ]++keyframe :: [String]+keyframe =+  [ "@keyframes spinAround { from, to {transform:rotate(0deg);} to {transform: rotate(359deg);}}"+  , "@-webkit-keyframes spinAround { 0% {} 100% {}}"+  , "@keyframes x{from{transform:rotate(0deg)}to{transform:rotate(359deg)}}"+  , "@keyframes x{ from {transform:rotate(0deg)} /* * */ 2.2% {} /* * */ to {transform:rotate(1deg)} }"+  ]++atImport :: [String]+atImport =+  [ "@import \"my-imported-styles.css\";"+  , "@import url(\"chrome://communicator/skin/communicator.css\");"+  , "@import url(chrome://communicator/skin/communicator.css);"+  , "@import \"fine-print.css\" print;"+  , "@import \"bluish.css\" print, screen;"+  , "@import \"common.css\" screen;"+  , "@import \"landscape.css\" screen and (orientation: landscape); "+  , "@import \"grid.css\" supports(display: grid) screen and (width <= 400px);"+  , "@import \"flex.css\" supports((not (display: grid)) and (display: flex)) screen and (width <= 400px);"+  , "@import \"whatever.css\" supports((selector(h2 > p)) and (font-tech(color-COLRv1)));"+  , "@import \"theme.css\" layer(utilities);"+  , "@import \"theme.css\" layer();"+  , "@import \"style.css\" layer;"+  ]++atFunction :: [String]+atFunction =+  [ "@function --f() { result: 12px; }"+  , "@function --f(--x) { result: calc(var(--x) * 2); }"+  , "@function --f(--x, --y) returns type(<angle>+) { result: calc(var(--x)+var(--y)); }"+  , "@function --f(--x <length>) returns <length> { result: calc(var(--x) * 2); }"+  , "@function --f(--x type(<number> | <percentage>)) { result: calc(var(--x) * 2); }"+  , "@function --f(--x type(<angle>) : 0px) { result: calc(var(--x) / 2); }"+  , "@function --f(--x *) returns type(*) { result: calc(var(--x) * 2); }"+  , "@function --transparent(--color <color>, --alpha type(<number> | <percentage>))" +++       "returns <color> { result: oklch(from var(--color) l c h / var(--alpha)); }"+  , "@function --narrow-wide(--narrow, --wide) { result: var(--wide);" +++       "@media (width < 700px) { result: var(--narrow); } }"+  , "@function --outer(--outer-arg ){ --outer-local: 2; result:--inner();}"+  ]++media :: [String]+media =+  [ "@media screen and (width >= 900px) {}"+  , "@media not all and (hover: hover) {}"+  , "@media screen, print {}"+  , "@media screen or print {}"+  , "@media all {}"+  , "@media {} "+  , "@media (max-width: 320px){}"+  , "@media (-webkit-transform-3d) {}"+  , "@media (400px < width < 1000px) or (a) {}"+  , "@media not (f( 1) / y < x) {}"+  , "@media not (x > f( 1) / y) {}"+  , "@media not (f( 1) / y < x < f( 1) / y) {}"+  , "@media all and (-ms-high-contrast:active),(-ms-high-contrast:active) {}"+  , "@media only screen and (max-width : 600.99px) {}"+  ]++customMedia :: [String]+customMedia =+  [ "@custom-media --mobile-screen (width < 480px);"+  , "@custom-media --screen-or-print-1 screen, print or tty;"+  , "@custom-media --screen-or-print-2 screen or print;"+  , "@custom-media --medium-screen (min-width: 40em) and (max-width: 60em);"+  , "@custom-media --medium-screen ((min-width: 40em) and (max-width: 60em) and (max-height: calc(34rem - 0.02px)));"+  , "@custom-media --no-script not (script);"+  , "@custom-media --enabled true;"+  , "@custom-media --disabled false;"+  ]++-- Based on the w3c testkit: https://test.csswg.org/harness/suite/selectors-3_dev/+validSelectors :: [String]+validSelectors =+  [ "body > p"+  , "div ol>li p"+  , ".decoratorß-row"+  , ":global(.x)"+  , ":-webkit-any(input,select,textarea)"+  , "i:heading(0)"+  , "i:host(p)"+  , "i:state(p)"+  , ".progress::-webkit-progress-bar"+  , "input.is-skeleton:-moz-placeholder"+  , "*.pastoral"+  , ".pastoral¡"+  , ".media:not(:last-child)"+  , "h1.pastoral"+  , "p.pastoral.marine"+  , "h1#chapter1"+  , "#chapter1"+  , "*#z98y"+  , "math + p"+  , "/* ---- */ math +/* ]] */p/* (((- */ "+  , "<!--  ---- --> math +<!-- ]] -->p<!-- (((- --> "+  , "h1.opener + h2"+  , "h1 ~ pre"+  , "*"+  , "div :first-child"+  , "div *:first-child"+  , "body > h2:nth-of-type(n+2):nth-last-of-type(n+2)"+  , "body > h2:not(:first-of-type):not(:last-of-type)"+  , ":where(ol, ul, menu:focus)"+  , "h1:has(+ p)"+  , ":is(ol, ul) :is(ol, ul) ol"+  , "div:where(ol, ul) :where(ol, ul, menu:hover) ol"+  , "h1, h2, h3"+  , "h1"+  , "foo|h1"+  , "foo|*"+  , "|h1"+  , "*|h1"+  , "*[hreflang|=en]"+  , "[hreflang|=en]"+  , "*.warning"+  , ".warning"+  , "*#myid"+  , "#myid"+  , "ns|*"+  , "*|*"+  , "|*"+  , "*"+  , "[att]"+  , "[att=val]"+  , "[att=]"+  , "[att=-val]"+  , "[att~=val]"+  , "[att|=val]"+  , "h1[title]"+  , "span[class=\"example\"]"+  , "span[hello=\"Cleveland\"][goodbye=\"Columbus\"]"+  , "a[rel~=\"copyright\"]"+  , "a[href=\"http://www.w3.org/\"]"+  , "a[hreflang=fr]"+  , "a[hreflang|=\"en\"]"+  , ".button[disabled]"+  , "DIALOGUE[character=romeo]"+  , "DIALOGUE[character=juliet]"+  , "[att^=val]"+  , "[att$=val]"+  , "[att$=val i]"+  , "[att$=8val]"+  , "[att*=val]"+  , "[att*=-val]"+  , "object[type^=\"image/\"]"+  , "a[href$=\".html\"]"+  , "p[title*=\"hello\"]"+  , "[foo|att=val]"+  , "[*|att]"+  , "[|att]"+  , "[att]"+  , "EXAMPLE[radix=decimal]"+  , "EXAMPLE[radix=octal]"+  , "EXAMPLE"+  , "*.pastoral"+  , ".pastoral"+  , "H1.pastoral"+  , "p.pastoral.marine"+  , "h1#chapter1"+  , "#chapter1"+  , "*#z98y"+  , "a.external:visited"+  , "a:link"+  , "a:visited"+  , "a:hover"+  , "a:active"+  , "a:focus"+  , "a:focus:hover"+  , "p.note:target"+  , "*:target"+  , "*:target::before"+  , "html:lang(fr-be)"+  , "html:lang(de)"+  , ":lang(fr-be) > q"+  , ":lang(de) > q"+  , "[lang|=fr]"+  , ":lang(fr)"+  , "[lang|=fr]"+  , "tr:nth-child(2n+1)"+  , "tr:nth-child(odd)"+  , "tr:nth-child(2n+0)"+  , "tr:nth-child(even)"+  , ":nth-child(-n + 3 of li.important)"+  , ":nth-last-child(-n + 3 of li.important)"+  , ":nth-child(1 of .orbit)"+  , "p:nth-child(4n+1)"+  , "p:nth-child(4n+2)"+  , "p:nth-child(4n+3)"+  , "p:nth-child(4n+4)"+  , ":nth-child(10n-1)"+  , ":nth-child(10n+9)"+  , "foo:nth-child(0n+5)"+  , "foo:nth-child(5)"+  , "p"+  , "p::first-letter"+  , "span"+  , "h1 em"+  , "div * p"+  , "div p *[href]"+  , "body > p"+  , "div ol>li p"+  , "match + p"+  , "h1.opener + h2"+  , "h1 ~ pre"+  , "*"+  , "LI"+  , "UL LI"+  , "UL OL+LI"+  , "H1 + *[REL=up]"+  , "UL OL LI.red"+  , "LI.red.level"+  , "#x34y"+  , "#s12:not(FOO)"+  , "*"+  , "E"+  , "E[foo]"+  , "E[foo=\"bar\"]"+  , "E[foo=\"bar\" i]"+  , "E[foo=\"bar\" s]"+  , "E[foo~=\"bar\"]"+  , "E[foo^=\"bar\"]"+  , "E[foo$=\"bar\"]"+  , "E[foo*=\"bar\"]"+  , "E[foo|=\"en\"]"+  , "E:root"+  , "E:nth-child(n)"+  , "E:nth-last-child(n)"+  , "E:nth-of-type(n)"+  , "E:nth-last-of-type(n)"+  , "E:first-child"+  , "E:last-child"+  , "E:first-of-type"+  , "E:last-of-type"+  , "E:only-child"+  , "E:only-of-type"+  , "E:empty"+  , "E:link"+  , "E:visited"+  , "E:active"+  , "E:hover"+  , "E:focus"+  , "E:target"+  , "E:lang(fr)"+  , "E:enabled"+  , "E:disabled"+  , "E:checked"+  , "E::first-line"+  , "E::first-letter"+  , "E::before"+  , "E::after"+  , "E.warning"+  , "E#myid"+  , "E:not(s)"+  , "E F"+  , "E > F"+  , "E + F"+  , "E ~ F"+  , ".class"+  , ".class1.class2"+  , ".class1 .class2"+  , "#id"+  , "*"+  , "element"+  , "element.class"+  , "element,element"+  , "element element"+  , "element>element"+  , "element+element"+  , "element1~element2"+  , "[attribute]"+  , "[attribute=value]"+  , "[attribute~=value]"+  , "[attribute|=value]"+  , "[attribute^=value]"+  , "[attribute$=value]"+  , "[attribute*=value]"+  , ":active"+  , "::after"+  , "::before"+  , ":checked"+  , ":default"+  , ":disabled"+  , ":empty"+  , ":enabled"+  , ":first-child"+  , "::first-letter"+  , "::first-line"+  , ":first-of-type"+  , ":focus"+  , ":fullscreen"+  , ":hover"+  , ":in-range"+  , ":indeterminate"+  , ":invalid"+  , ":lang(language)"+  , ":last-child"+  , ":last-of-type"+  , ":link"+  , "::marker"+  , ":not(selector)"+  , ":nth-child(n)"+  , ":nth-last-child(n)"+  , ":nth-last-of-type(n)"+  , ":nth-of-type(n)"+  , ":only-of-type"+  , ":only-child"+  , ":optional"+  , ":out-of-range"+  , "::placeholder"+  , ":read-only"+  , ":read-write"+  , ":required"+  , ":root"+  , "::selection"+  , ":target"+  , ":valid"+  , ":visited"+  ]