stylist 1.1.1.0 → 1.2.0.0
raw patch · 13 files changed
+159/−26 lines, 13 filesdep +async
Dependencies added: async
Files
- src/Data/CSS/Preprocessor/Conditions.hs +35/−11
- src/Data/CSS/Preprocessor/Conditions/Expr.hs +20/−2
- src/Data/CSS/Style.hs +13/−1
- src/Data/CSS/Style/Cascade.hs +13/−0
- src/Data/CSS/Style/Common.hs +9/−1
- src/Data/CSS/Style/Importance.hs +3/−0
- src/Data/CSS/Style/Selector/Index.hs +4/−0
- src/Data/CSS/Style/Selector/Interpret.hs +5/−0
- src/Data/CSS/Style/Selector/Specificity.hs +3/−0
- src/Data/CSS/Syntax/Selector.hs +23/−8
- src/Data/CSS/Syntax/StyleSheet.hs +19/−0
- src/Data/CSS/Syntax/StylishUtil.hs +9/−0
- stylist.cabal +3/−3
src/Data/CSS/Preprocessor/Conditions.hs view
@@ -1,7 +1,11 @@ {-# LANGUAGE OverloadedStrings #-}+-- | Evaluates conditional CSS @rules.+-- Parse a CSS stylesheet to `ConditionalStyles` to evaluate @document & @supports rules.+-- Call `loadImports` to resolve any @import rules to @media rules.+-- And call `resolve` to convert into another `StyleSheet` instance whilst resolving @media rules. module Data.CSS.Preprocessor.Conditions( ConditionalStyles(..), conditionalStyles, ConditionalRule(..),- extractImports, resolveImports, loadImports, resolve,+ extractImports, resolveImports, loadImports, resolve, testIsStyled, Datum(..) ) where @@ -16,19 +20,29 @@ import Data.Text.Internal (Text(..)) import Data.Text (unpack) import Network.URI (URI(..), URIAuth(..), parseURI)+import Control.Concurrent.Async (forConcurrently) import Data.List +-- | Collects and evaluates conditional at-rules. data ConditionalStyles p = ConditionalStyles {+ -- | The URL to the webpage being styled, for `@document` rules. hostURL :: URI,+ -- | The type of document, `@document domain(...)` rules. mediaDocument :: String,+ -- | Whether the page provided any of it's own styling (valid or not)+ isUnstyled :: Bool,+ -- | Queued style rules, to be evaluated later. rules :: [ConditionalRule p],+ -- | PropertyParser to test against for `@supports` rules. propertyParser :: p } +-- | Constructs an empty `ConditionalStyles`. conditionalStyles :: PropertyParser p => URI -> String -> ConditionalStyles p-conditionalStyles uri mediaDocument' = ConditionalStyles uri mediaDocument' [] temp+conditionalStyles uri mediaDocument' = ConditionalStyles uri mediaDocument' False [] temp +-- | Style rules that can be queued in a `ConditionalStyles`. data ConditionalRule p = Priority Int | StyleRule' StyleRule | AtRule Text [Token] | External Query.Expr URI | Internal Query.Expr (ConditionalStyles p) @@ -66,6 +80,10 @@ addAtRule self "document" (Function "media-document":String match:RightParen:toks) | unpack match == mediaDocument self = parseAtBlock self toks | otherwise = addAtRule self "document" toks+ -- Rhapsode-specific: matches if the document didn't provide any of it's own stylings.+ addAtRule self "document" (Ident "unstyled":toks)+ | isUnstyled self = parseAtBlock self toks+ | otherwise = addAtRule self "document" toks -- TODO Support regexp() conditions, requires new dependency addAtRule self "document" tokens = (self, skipAtRule tokens) @@ -87,6 +105,12 @@ addAtRule self rule tokens = let (block, rest) = scanAtRule tokens in (addRule' self $ AtRule rule block, rest)++-- | Flags whether any style rules have been applied yet,+-- for the sake of evaluating "@document unstyled {...}".+testIsStyled :: ConditionalStyles p -> ConditionalStyles p+testIsStyled styles = styles { isUnstyled = null $ rules styles }+ -------- ---- @import/@media --------@@ -97,10 +121,12 @@ (addRule' self $ External cond uri, toks') parseAtImport self _ toks = (self, skipAtRule toks) +-- | Returns `@import` URLs that need to be imported. extractImports :: (Text -> Query.Datum) -> (Token -> Query.Datum) -> ConditionalStyles p -> [URI] extractImports vars evalToken self = [uri | External cond uri <- rules self, Query.eval vars evalToken cond] +-- | Substitutes external values in for `@import` rules. resolveImports :: ConditionalStyles p -> [(URI, ConditionalStyles p)] -> ConditionalStyles p resolveImports self responses = self {rules = map resolveImport $ rules self} where@@ -108,22 +134,20 @@ Internal cond body resolveImport x = x +-- | Evaluates a given "loader" to resolve any `@import` rules. loadImports :: PropertyParser p => (URI -> IO Text) -> (Text -> Query.Datum) -> (Token -> Query.Datum) -> ConditionalStyles p -> [URI] -> IO (ConditionalStyles p) loadImports loader vars evalToken self blocklist = do let imports = extractImports vars evalToken self- imported <- loadAll [url | url <- imports, url `notElem` blocklist] Nothing- return $ resolveImports self imported- where- loadAll urls Nothing = loadAll urls $ Just urls- loadAll (url:urls) (Just blocklist') = do+ let urls = [url | url <- imports, url `notElem` blocklist]+ imported <- forConcurrently urls $ \url -> do source <- loader url let parsed = parse self {rules = []} source- styles <- loadImports loader vars evalToken parsed (blocklist ++ blocklist')- rest <- loadAll urls $ Just blocklist'- return ((url, styles):rest)- loadAll [] _ = return []+ styles <- loadImports loader vars evalToken parsed (blocklist ++ urls)+ return (url, styles)+ return $ resolveImports self imported +-- | Evaluates any media queries, returning a new StyleSheet with the queued operations. resolve :: StyleSheet s => (Text -> Query.Datum) -> (Token -> Query.Datum) -> s -> ConditionalStyles p -> s resolve v t styles self = resolve' v t (reverse $ rules self) styles
src/Data/CSS/Preprocessor/Conditions/Expr.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE OverloadedStrings #-}+-- | Evaluates CSS media queries for @import & @media.+-- INTERNAL MODULE module Data.CSS.Preprocessor.Conditions.Expr( Expr, Op(..), parse, parse', eval, Datum(..) ) where@@ -7,16 +9,30 @@ import Data.Text.Internal (Text(..)) import Data.Text (stripPrefix) +-- | A parsed (post-order) expression. type Expr = [Op]-data Op = And | Or | Not | Var Text | Tok Token | MkRatio | Func Text [Token]- | Less | LessEq | Equal | Greater | GreaterEq deriving (Show, Eq)+-- | Operators understood by media queries.+data Op = And -- ^ Is true if both operands are true+ | Or -- ^ Is true if either operand is true+ | Not -- ^ Is true if it's operand isn't.+ | Var Text -- ^ Queries the value of an externally-specified parameter.+ | Tok Token -- ^ Tokens to be evaluated as specified by caller.+ | MkRatio -- ^ Pushes a ratio value to stack, for querying screensize.+ | Less -- ^ Is the left operand smaller than right?+ | LessEq -- ^ Is the left operand smaller or the same as right?+ | Equal -- ^ Are the operands the same?+ | Greater -- ^ Is the left operand bigger than right?+ | GreaterEq -- ^ Is the left operand bigger or the same as right?+ deriving (Show, Eq) +-- | Parses a media query to postorder form, returning the tokens after the given delimiter. parse :: Token -> [Token] -> (Expr, [Token]) parse end toks = let (toks', rest) = break (== end) toks in (parse' toks' [], rest) -------- ---- Shunting Yard parser --------+-- | Parses a media query to postorder form, given an operator stack. parse' :: [Token] -> [(Op, Int)] -> Expr parse' (Whitespace:toks) ops = parse' toks ops @@ -55,8 +71,10 @@ -------- ---- Shunting Yard Evaluator --------+-- | Dynamic types for evaluating media queries. data Datum = B Bool | N Float | Ratio Float Float deriving Eq +-- | Evaluates a media query with the given functions for evaluating vars & tokens. eval :: (Text -> Datum) -> (Token -> Datum) -> Expr -> Bool eval = eval' []
src/Data/CSS/Style.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE OverloadedStrings #-}+-- | Queries computed styles out of a specially-parsed CSS stylesheet.+-- See in particular `QueryableStyleSheet`, `queryRules`, & `cascade'`. module Data.CSS.Style( QueryableStyleSheet, QueryableStyleSheet'(..), queryableStyleSheet, queryRules,@@ -21,16 +23,22 @@ import Data.Text (isPrefixOf) import Data.List (elemIndex) +-- | A parsed CSS stylesheet from which you can query styles to match an element. type QueryableStyleSheet parser = QueryableStyleSheet' (ImportanceSplitter ( PropertyExpander parser (OrderedRuleStore (InterpretedRuleStore StyleIndex)) )) parser +-- | More generic version of `QueryableStyleSheet`. data QueryableStyleSheet' store parser = QueryableStyleSheet' {+ -- | Internal datastructure for efficient style lookup. store :: store,+ -- | The "PropertyParser" to use for property syntax validation. parser :: parser,+ -- | Whether author, useragent, or user styles are currently being parsed. priority :: Int -- author vs user agent vs user styles } +-- | Constructs an empty QueryableStyleSheet'. queryableStyleSheet :: PropertyParser p => QueryableStyleSheet p queryableStyleSheet = QueryableStyleSheet' {store = new, parser = temp, priority = 0} @@ -41,14 +49,17 @@ } --- Reexpose cascade methods+-- | Looks up style rules matching the specified element, grouped by psuedoelement. queryRules :: (PropertyParser p, RuleStore s) => QueryableStyleSheet' s p -> Element -> HashMap Text [StyleRule'] queryRules (QueryableStyleSheet' store' _ _) = Cascade.query store' +-- | Selects used property values from the given style rules,+-- & populates into a new `PropertyParser` inheriting from the one given. cascade' :: PropertyParser p => [StyleRule'] -> Props -> p -> p cascade' = Cascade.cascade ---- Facade for trivial cases+-- | Facade over `queryRules` & `cascade'` for simple cases you don't care about psuedoelements. cascade :: PropertyParser p => QueryableStyleSheet p -> Element -> Props -> p -> p cascade self el = cascade' $ lookupDefault [] "" $ queryRules self el @@ -71,6 +82,7 @@ -------- ---- var() --------+-- | `PropertyParser` that lowers var() calls before forwarding to another. data VarParser a = VarParser {vars :: Props, innerParser :: a} instance PropertyParser p => PropertyParser (VarParser p) where
src/Data/CSS/Style/Cascade.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE OverloadedStrings #-}+-- | Applies CSS selection, cascade, & inheritance.+-- INTERNAL MODULE. module Data.CSS.Style.Cascade( query, cascade, TrivialPropertyParser(..), PropertyParser(..), Props@@ -12,34 +14,43 @@ import Data.HashMap.Strict import Data.Text (unpack, pack, isPrefixOf) +-- | Defines how to parse CSS properties into an output "style" format. class PropertyParser a where+ -- | Default styles. temp :: a+ -- | Creates a style inherited from a parent style. inherit :: a -> a inherit = id + -- | Expand a shorthand property into longhand properties. shorthand :: a -> Text -> [Token] -> [(Text, [Token])] shorthand self key value | Just _ <- longhand self self key value = [(key, value)] | otherwise = [] -- longhand parent self name value longhand :: a -> a -> Text -> [Token] -> Maybe a + -- | Retrieve stored variables, optional. getVars :: a -> Props getVars _ = []+ -- | Save variable values, optional. setVars :: Props -> a -> a setVars _ = id +-- | Gather properties into a hashmap. data TrivialPropertyParser = TrivialPropertyParser (HashMap String [Token]) instance PropertyParser TrivialPropertyParser where temp = TrivialPropertyParser empty longhand _ (TrivialPropertyParser self) key value = Just $ TrivialPropertyParser $ insert (unpack key) value self +-- | "key: value;" entries to be parsed into an output type. type Props = [(Text, [Token])] -------- ---- Query/Psuedo-elements -------- +-- | Looks up style rules for an element, grouped by psuedoelement. query :: RuleStore s => s -> Element -> HashMap Text [StyleRule'] query self el = Prelude.foldr yield empty $ lookupRules self el where yield rule store = insertWith (++) (psuedoElement rule) [resolveAttr rule el] store@@ -48,6 +59,8 @@ ---- Cascade/Inheritance -------- +-- | Applies cascade for the given `StyleRule'`s & explicit styles,+-- parsed to a value of the same `PropertyParser` type passed in & inheriting from it. cascade :: PropertyParser p => [StyleRule'] -> Props -> p -> p cascade styles overrides base = construct base $ toList $ cascadeRules (getVars base ++ overrides) styles
src/Data/CSS/Style/Common.hs view
@@ -1,3 +1,5 @@+-- | Central infrastructure for implementing queryable stylesheets.+-- NOTE: This internal module isn't intended to be fully documented. module Data.CSS.Style.Common( RuleStore(..), StyleRule'(..), selector, properties, psuedoElement, styleRule', Element(..), Attribute(..),@@ -10,12 +12,18 @@ import Data.CSS.Syntax.Tokens import Data.Text.Internal (Text(..)) +-- | An inversely-linked tree of elements, to apply CSS selectors to. data Element = ElementNode {+ -- | The element's parent in the tree. parent :: Maybe Element,+ -- | The element's previous sibling in the tree. previous :: Maybe Element,+ -- | The element's name. name :: Text,- attributes :: [Attribute] -- in sorted order.+ -- | The element's attributes, in sorted order.+ attributes :: [Attribute] }+-- | A key-value attribute. data Attribute = Attribute Text String deriving (Eq, Ord) class RuleStore a where
src/Data/CSS/Style/Importance.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE OverloadedStrings #-}+-- | Evaluates !important.+-- INTERNAL MODULE. module Data.CSS.Style.Importance ( ImportanceSplitter(..) ) where@@ -18,6 +20,7 @@ --- NOTE: Prorities are defined with lower numbers being more important, --- so negate to be consistant with other priority sources. --- This API decision started out being accidental, but I find it more intuitive.+-- | Evaluates "!important" by splitting all `StyleRule'` in two. data ImportanceSplitter a = ImportanceSplitter a instance RuleStore inner => RuleStore (ImportanceSplitter inner) where new = ImportanceSplitter new
src/Data/CSS/Style/Selector/Index.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE OverloadedStrings #-}+-- | Fast lookup & storage for style rules.+-- INTERNAL MODULE. module Data.CSS.Style.Selector.Index ( StyleIndex(..), rulesForElement@@ -13,6 +15,7 @@ import Data.Text (unpack, pack) import Data.CSS.Syntax.Tokens (serialize) -- for easy hashing +-- | Fast lookup & storage for style rules. data StyleIndex = StyleIndex { indexed :: HashMap SimpleSelector [StyleRule'], unindexed :: [StyleRule']@@ -31,6 +34,7 @@ index = indexed self rules = unindexed self : Prelude.map get (testsForElement element) +-- | LEGACY TESTING API. rulesForElement :: StyleIndex -> Element -> [StyleRule] -- For testing rulesForElement self element = Prelude.map inner $ lookupRules self element
src/Data/CSS/Style/Selector/Interpret.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE OverloadedStrings #-}+-- | Evaluates CSS selectors over an element.+-- INTERNAL MODULE. module Data.CSS.Style.Selector.Interpret( compile, SelectorFunc, InterpretedRuleStore(..)@@ -10,9 +12,11 @@ import Data.List import Data.Maybe +-- | A compiled(?) CSS selector. type SelectorFunc = Element -> Bool type AttrsFunc = [Attribute] -> Bool +-- | Converts a parsed CSS selector into a callable function. compile :: Selector -> SelectorFunc compile (Element sel) = compileInner sel compile (Child upSel sel) = direct parent (compile upSel) $ compileInner sel@@ -88,6 +92,7 @@ -------- ---- RuleStore wrapper --------+-- | Compiles & fully evaluates CSS selectors. data InterpretedRuleStore inner = InterpretedRuleStore inner instance RuleStore inner => RuleStore (InterpretedRuleStore inner) where new = InterpretedRuleStore new
src/Data/CSS/Style/Selector/Specificity.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE OverloadedStrings #-}+-- | Sorts `StyleRule'`s by specificity.+-- INTERNAL MODULE. module Data.CSS.Style.Selector.Specificity( OrderedRuleStore(..) ) where@@ -27,6 +29,7 @@ add :: Vec -> Vec -> Vec add (a, b, c) (x, y, z) = (a + x, b + y, c + z) +-- | Sorts `StyleRule'`s by their selector specificity. data OrderedRuleStore inner = OrderedRuleStore inner Int instance RuleStore inner => RuleStore (OrderedRuleStore inner) where
src/Data/CSS/Syntax/Selector.hs view
@@ -1,3 +1,5 @@+-- | Parses CSS selectors+-- See `parseSelectors` module Data.CSS.Syntax.Selector( Selector(..), SimpleSelector(..), PropertyTest(..), parseSelectors@@ -8,18 +10,31 @@ import Data.Text.Internal (Text(..)) --- type Selector = [SimpleSelector]-data Selector = Element [SimpleSelector] |- Child Selector [SimpleSelector] | Descendant Selector [SimpleSelector] |- Adjacent Selector [SimpleSelector] | Sibling Selector [SimpleSelector]+-- | A CSS "selector" indicating which elements should be effected by CSS.+data Selector = Element [SimpleSelector] -- ^ Selects a single element.+ | Child Selector [SimpleSelector] -- ^ Represents "a > b" operator.+ | Descendant Selector [SimpleSelector] -- ^ Represents "a b" operator.+ | Adjacent Selector [SimpleSelector] -- ^ Represents "a + b" operator.+ | Sibling Selector [SimpleSelector] -- ^ Represents "a ~ b" operator. deriving (Show, Eq)-data SimpleSelector = Tag Text | Id Text | Class Text | Property Text PropertyTest |- Psuedoclass Text [Token]+-- | An individual test comprising a CSS stylesheet.+data SimpleSelector = Tag Text -- ^ Matches a tagname, e.g. "a"+ | Id Text -- ^ Matches the "id" attribute, e.g. "#header"+ | Class Text -- ^ Matches the "class" attribute, e.g. ".ad"+ | Property Text PropertyTest -- ^ Matches a specified property+ | Psuedoclass Text [Token] -- ^ Matches psuedoclasses provided by the caller (via a nameless property). deriving (Show, Eq)-data PropertyTest = Exists | Equals Text | Suffix Text | Prefix Text | Substring Text |- Include Text | Dash Text+-- | How should a property be matched.+data PropertyTest = Exists -- ^ Matches whether an attribute actually exists, e.g. "[title]"+ | Equals Text -- ^ Matches whether the attribute is exactly equal to the value, e.g. "="+ | Suffix Text -- ^ Matches whether attribute ends with the given value, e.g. "$="+ | Prefix Text -- ^ Matches whether attribute starts with the given value, e.g. "^="+ | Substring Text -- ^ Matches whether the attribute contains the given value, e.g. "*="+ | Include Text -- ^ Is one of the whitespace-seperated values the one specified? e.g. "~="+ | Dash Text -- ^ Matches whitespace seperated values, or their "-"-seperated prefixes. e.g. "|=" deriving (Show, Eq) +-- | Parses a CSS selector. parseSelectors :: Parser [Selector] parseSelectors tokens = concatP (:) parseCompound parseSelectorsTail $ skipSpace tokens parseSelectorsTail :: Parser [Selector]
src/Data/CSS/Syntax/StyleSheet.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE OverloadedStrings #-}+-- | Parses a CSS stylesheet+-- See `StyleSheet` & `parseForURL`. module Data.CSS.Syntax.StyleSheet ( parse, parse', parseForURL, TrivialStyleSheet(..), StyleSheet(..), skipAtRule, scanAtRule, scanBlock, skipSpace,@@ -20,20 +22,28 @@ -------- ---- Output type class --------+-- | Describes how to store, and to some extent parse, CSS stylesheets.+-- These methods are used to construct the results from `parse`, etc. class StyleSheet s where+ -- | Sets the stylesheet priority (useragent vs user vs author), optional. setPriority :: Int -> s -> s setPriority _ = id+ -- | Stores a parsed selector+properties rule. addRule :: s -> StyleRule -> s+ -- | Stores and parses an identified at-rule. addAtRule :: s -> Text -> [Token] -> (s, [Token]) addAtRule self _ tokens = (self, skipAtRule tokens) +-- | Stores the parsed selector*s*+proeprties rule. addRules :: StyleSheet ss => ss -> ([Selector], ([(Text, [Token])], Text)) -> ss addRules self (selector:selectors, val@(props, psuedoel)) = addRules self' (selectors, val) where self' = addRule self $ StyleRule selector props psuedoel addRules self ([], _) = self +-- | The properties to set for elements matching the given selector. data StyleRule = StyleRule Selector [(Text, [Token])] Text deriving (Show, Eq) +-- | Gathers StyleRules into a list, mainly for testing. data TrivialStyleSheet = TrivialStyleSheet [StyleRule] deriving (Show, Eq) instance StyleSheet TrivialStyleSheet where addRule (TrivialStyleSheet self) rule = TrivialStyleSheet $ rule:self@@ -41,9 +51,11 @@ -------- ---- Basic parsing --------+-- | Parse a CSS stylesheet parse :: StyleSheet s => s -> Text -> s parse stylesheet source = parse' stylesheet $ tokenize source +-- | Parse a CSS stylesheet, resolving all URLs to absolute form. parseForURL :: StyleSheet s => s -> URI -> Text -> s parseForURL stylesheet base source = parse' stylesheet $ rewriteURLs $ tokenize source where@@ -54,6 +66,7 @@ rewriteURLs (tok:toks) = tok : rewriteURLs toks rewriteURLs [] = [] +-- | Parse a tokenized (via `css-syntax`) CSS stylesheet parse' :: StyleSheet t => t -> [Token] -> t -- Things to skip. parse' stylesheet (Whitespace:tokens) = parse' stylesheet tokens@@ -71,6 +84,7 @@ -------- ---- Property parsing --------+-- | Parse "{key: value; ...}" property values, with a psuedoelement. parseProperties :: Parser ([(Text, [Token])], Text) parseProperties (LeftCurlyBracket:tokens) = noPsuedoel $ parseProperties' tokens parseProperties (Whitespace:tokens) = parseProperties tokens@@ -83,6 +97,7 @@ noPsuedoel :: (x, y) -> ((x, Text), y) noPsuedoel (val, tokens) = ((val, ""), tokens) +-- | Parse "key: value;"... property values, as per the HTML "style" property. parseProperties' :: Parser [(Text, [Token])] parseProperties' (Whitespace:tokens) = parseProperties' tokens parseProperties' (Ident name:tokens)@@ -96,6 +111,7 @@ -------- ---- Skipping/Scanning utilities --------+-- | Returns tokens before & after an at-rule value, terminated after a curly-bracketed block or a semicolon. scanAtRule :: Parser [Token] scanAtRule (Semicolon:tokens) = ([Semicolon], tokens) scanAtRule tokens@(LeftCurlyBracket:_) = scanInner tokens $ \rest -> ([], rest)@@ -110,9 +126,11 @@ scanAtRule tokens = capture scanAtRule tokens +-- | Returns tokens after an at-rule, as per `scanAtRule`. skipAtRule :: [Token] -> [Token] skipAtRule tokens = snd $ scanAtRule tokens +-- | Returns tokens before & after a semicolon. scanValue :: Parser [Token] scanValue (Semicolon:tokens) = ([], tokens) scanValue (Whitespace:tokens) = scanValue tokens@@ -128,5 +146,6 @@ scanValue tokens = capture scanValue tokens +-- | Returns tokens after a semicolon. skipValue :: [Token] -> [Token] skipValue tokens = snd $ scanValue tokens
src/Data/CSS/Syntax/StylishUtil.hs view
@@ -1,3 +1,4 @@+-- | Utility parser combinators for parsing CSS stylesheets. module Data.CSS.Syntax.StylishUtil( concatP, capture, skipSpace, scanBlock, skipBlock, scanInner,@@ -6,22 +7,28 @@ import Data.CSS.Syntax.Tokens +-- | A simple parser combinator type. type Parser x = [Token] -> (x, [Token])++-- | Chains two parser combinators together. concatP :: (a -> b -> c) -> Parser a -> Parser b -> Parser c concatP join left right tokens = (join x y, remainder) where (x, tokens') = left tokens (y, remainder) = right tokens' +-- | "captures" the token being parsed into the returned output. capture :: Parser [Token] -> Parser [Token] capture cb (token:tokens) = (token:captured, tokens') where (captured, tokens') = cb tokens capture _ [] = ([], []) +-- | Removes preceding `Whitespace` tokens. skipSpace :: [Token] -> [Token] skipSpace (Whitespace:tokens) = skipSpace tokens skipSpace tokens = tokens +-- | Returns tokens until the next unbalanced closing brace. scanBlock :: Parser [Token] -- TODO assert closing tags are correct -- But what should the error recovery be?@@ -36,9 +43,11 @@ scanBlock tokens = capture scanBlock tokens +-- | Returns tokens after the next unbalanced closing brace. skipBlock :: [Token] -> [Token] skipBlock tokens = snd $ scanBlock tokens +-- | Parses a block followed by the given combinator, returning the tokens the matched. scanInner :: [Token] -> Parser [Token] -> ([Token], [Token]) scanInner (token:tokens) cb = concatP gather scanBlock cb tokens where gather x y = token : x ++ y
stylist.cabal view
@@ -10,7 +10,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 1.1.1.0+version: 1.2.0.0 -- A short (one-line) description of the package. synopsis: Apply CSS styles to a document tree.@@ -66,7 +66,7 @@ -- Other library packages from which modules are imported. build-depends: base >=4.9 && <=4.12, css-syntax >=0.1 && <0.2, text, unordered-containers >= 0.2 && <0.3, hashable,- network-uri >= 2.6 && <2.7+ network-uri >= 2.6 && <2.7, async >= 2.1 && <2.3 -- Directories containing source files. hs-source-dirs: src@@ -84,6 +84,6 @@ other-modules: Data.CSS.Syntax.StyleSheet, Data.CSS.Syntax.Selector, Data.CSS.Style build-depends: base >=4.9 && <=4.12, css-syntax >=0.1 && <0.2, text, unordered-containers >= 0.2 && <0.3, hashable,- network-uri >= 2.6 && <2.7, hspec, QuickCheck,+ network-uri >= 2.6 && <2.7, async >= 2.1 && <2.3, hspec, QuickCheck, scientific >= 0.3 && <1.0 ghc-options: -Wall