moduleText.CSS.ParserwhereimportText.ParsecimportqualifiedData.Functor.IdentityasIimportData.ListimportText.CSS.Utils-- if no tag name was given, sName will be set to '*'-- attrs are (attr name, attr value).-- if attr value is the empty string, we just check to-- make sure that the element has that attribute.-- if the attr value is prefixed with a '~', we treat-- that attribute as a list of words separated by a space-- and make sure that at least one of those words matches.dataSelector=Selector{sName::String,sAttrs::[(String,String)],spseudoSelectores::[String]}|Space|ChildOf|FollowedByderiving(Show)-- pretty printers for debuggingppSpace="<space>"ppChildOf="<child of>"ppFollowedBy="<followed by>"pp(Selectornameattrspseudo)=showname++":"++showMapattrs++", "++showpseudowhereshowMapm=("{"++(foldl(\acc(k,v)->acc++(showk)++":"++(showv)++", ")""m))++"}"{- TYPE SELECTORS FOLLOW -}-- | selects a tag name, like @ h1 @typeSelector::ParsecT[Char]uI.Identity[Char]typeSelector=many1alphaNum-- | universal selector, selects @ * @universalSelector::ParsecT[Char]uI.IdentityStringuniversalSelector=string"*"{- SECONDARY SELECTORS FOLLOW -}-- | selects a pseudo-element or pseudo-class, like @ :link @, @ :first-child @ etc.pseudoSelector::ParsecT[Char]uI.Identity[Char]pseudoSelector=char':'>>many1(alphaNum<|>oneOf"-()")-- | class selector, selects @ .foo @classSelector::ParsecT[Char]uI.Identity([Char],[Char])classSelector=doval<-char'.'>>many1alphaNumreturn("class",'~':val)-- | id selector, selects @ #foo @idSelector::ParsecT[Char]uI.Identity([Char],[Char])idSelector=doval<-char'#'>>many1alphaNumreturn("id",val)-- | selects attributes, like @ [id] @ (element must have id) or @ [id=foo] @ (element must have id foo).attributeSelector::ParsecT[Char]uI.Identity([Char],[Char])attributeSelector=docontents<-between(char'[')(char']')(many1(alphaNum<|>oneOf"~="))if"~="`isInfixOf`contentsthenreturn$(\(a,b)->(a,'~':b))$splitOn"~="contentselseif'='`elem`contentsthenreturn$splitOn"="contentselsereturn(contents,"")-- | selector for everything after the type except pseudoSelectoressecondarySelector=many1(classSelector<|>idSelector<|>attributeSelector){- COMBINATOR SELECTORS FOLLOW -}space_=domany1$string" "returnSpacechildOf=dospaces>>string">">>spacesreturnChildOffollowedBy=dospaces>>string"+">>spacesreturnFollowedBy{- SIMPLE SELECTORS FOLLOW -}-- | selects a tagname followed by one or more secondary selectors-- example: @ a.foo @, @ *#hello @, @ h1 @ etcsimpleSelectorTag::ParsecT[Char]uI.IdentitySelectorsimpleSelectorTag=dotagName<-typeSelector<|>universalSelectorattrs<-secondarySelector<|>return[]pseudo<-many1pseudoSelector<|>return[]return$SelectortagNameattrspseudo-- | selects one or more secondary selectors-- and automatically prepends the universal selector to them.-- example: @ .foo @, @ #hello @ etcsimpleSelectorNoTag=doattrs<-secondarySelectorpseudo<-many1pseudoSelector<|>return[]return$Selector"*"attrspseudo-- | A simple selector is either a type selector or universal selector followed immediately by zero or more attribute selectors, ID selectors, or pseudo-classes, in any order.simpleSelector::ParsecT[Char]uI.IdentitySelectorsimpleSelector=simpleSelectorTag<|>simpleSelectorNoTag<|>trychildOf<|>tryfollowedBy<|>space_-- | One or more simple selectors separated by combinators. selector::ParsecT[Char]uI.Identity[[Selector]]selector=many1simpleSelector`sepBy`(spaces>>string",">>spaces)css=parseselector""