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 +5/−0
- LICENSE +30/−0
- README.md +38/−0
- app/Main.hs +17/−0
- css-parser.cabal +128/−0
- src/CssParser.hs +38/−0
- src/CssParser/At/Container.hs +50/−0
- src/CssParser/At/CustomMedia.hs +16/−0
- src/CssParser/At/FontFeatureValues.hs +41/−0
- src/CssParser/At/FontPaletteValues.hs +15/−0
- src/CssParser/At/Function.hs +46/−0
- src/CssParser/At/Import.hs +43/−0
- src/CssParser/At/Keyframe.hs +44/−0
- src/CssParser/At/MediaQuery.hs +182/−0
- src/CssParser/At/Page.hs +60/−0
- src/CssParser/At/Supports.hs +63/−0
- src/CssParser/Descriptor.hs +1181/−0
- src/CssParser/File.hs +11/−0
- src/CssParser/FixRule.hs +124/−0
- src/CssParser/Ident.hs +125/−0
- src/CssParser/Lexer.x +299/−0
- src/CssParser/Lexer/Token.hs +274/−0
- src/CssParser/List.hs +26/−0
- src/CssParser/MonoPair.hs +23/−0
- src/CssParser/Norm.hs +71/−0
- src/CssParser/Parser.y +803/−0
- src/CssParser/Parser/Monad.hs +62/−0
- src/CssParser/Prelude.hs +27/−0
- src/CssParser/Rule.hs +164/−0
- src/CssParser/Rule/Pseudo.hs +216/−0
- src/CssParser/Rule/Show.hs +183/−0
- src/CssParser/Rule/Type.hs +69/−0
- src/CssParser/Rule/TypedNum.hs +173/−0
- src/CssParser/Rule/Value.hs +270/−0
- src/CssParser/Show.hs +108/−0
- src/CssParser/TextMarshal.hs +40/−0
- src/CssParser/Utils.hs +101/−0
- test/CssParser/Test/Arbitrary.hs +83/−0
- test/CssParser/Test/Arbitrary/At.hs +67/−0
- test/CssParser/Test/Arbitrary/Container.hs +39/−0
- test/CssParser/Test/Arbitrary/File.hs +11/−0
- test/CssParser/Test/Arbitrary/FontFeatureValues.hs +14/−0
- test/CssParser/Test/Arbitrary/FontPaletteValues.hs +11/−0
- test/CssParser/Test/Arbitrary/Function.hs +21/−0
- test/CssParser/Test/Arbitrary/Ident.hs +36/−0
- test/CssParser/Test/Arbitrary/Media.hs +63/−0
- test/CssParser/Test/Arbitrary/MonoPair.hs +15/−0
- test/CssParser/Test/Arbitrary/Rule.hs +94/−0
- test/CssParser/Test/Arbitrary/Value.hs +164/−0
- test/Spec.hs +621/−0
+ 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"+ ]