-- | Utility parser combinators for parsing CSS stylesheets.
module Stylist.Parse.Util(
        concatP, capture, skipSpace,
        scanBlock, skipBlock, scanInner,
        Parser
    ) where

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 :: (a -> b -> c) -> Parser a -> Parser b -> Parser c
concatP join :: a -> b -> c
join left :: Parser a
left right :: Parser b
right tokens :: [Token]
tokens = (a -> b -> c
join a
x b
y, [Token]
remainder)
    where
        (x :: a
x, tokens' :: [Token]
tokens') = Parser a
left [Token]
tokens
        (y :: b
y, remainder :: [Token]
remainder) = Parser b
right [Token]
tokens'

-- | "captures" the token being parsed into the returned output.
capture :: Parser [Token] -> Parser [Token]
capture :: Parser [Token] -> Parser [Token]
capture cb :: Parser [Token]
cb (token :: Token
token:tokens :: [Token]
tokens) = (Token
tokenToken -> [Token] -> [Token]
forall a. a -> [a] -> [a]
:[Token]
captured, [Token]
tokens')
   where (captured :: [Token]
captured, tokens' :: [Token]
tokens') = Parser [Token]
cb [Token]
tokens
capture _ [] = ([], [])

-- | Removes preceding `Whitespace` tokens.
skipSpace :: [Token] -> [Token]
skipSpace :: [Token] -> [Token]
skipSpace (Whitespace:tokens :: [Token]
tokens) = [Token] -> [Token]
skipSpace [Token]
tokens
skipSpace tokens :: [Token]
tokens = [Token]
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?
scanBlock :: Parser [Token]
scanBlock (RightCurlyBracket:tokens :: [Token]
tokens) = ([Token
RightCurlyBracket], [Token]
tokens)
scanBlock (RightParen:tokens :: [Token]
tokens) = ([Token
RightParen], [Token]
tokens)
scanBlock (RightSquareBracket:tokens :: [Token]
tokens) = ([Token
RightSquareBracket], [Token]
tokens)

scanBlock tokens :: [Token]
tokens@(LeftCurlyBracket:_) = [Token] -> Parser [Token] -> ([Token], [Token])
scanInner [Token]
tokens Parser [Token]
scanBlock
scanBlock tokens :: [Token]
tokens@(LeftParen:_) = [Token] -> Parser [Token] -> ([Token], [Token])
scanInner [Token]
tokens Parser [Token]
scanBlock
scanBlock tokens :: [Token]
tokens@(Function _:_) = [Token] -> Parser [Token] -> ([Token], [Token])
scanInner [Token]
tokens Parser [Token]
scanBlock
scanBlock tokens :: [Token]
tokens@(LeftSquareBracket:_) = [Token] -> Parser [Token] -> ([Token], [Token])
scanInner [Token]
tokens Parser [Token]
scanBlock

scanBlock tokens :: [Token]
tokens = Parser [Token] -> Parser [Token]
capture Parser [Token]
scanBlock [Token]
tokens

-- | Returns tokens after the next unbalanced closing brace.
skipBlock :: [Token] -> [Token]
skipBlock :: [Token] -> [Token]
skipBlock tokens :: [Token]
tokens = ([Token], [Token]) -> [Token]
forall a b. (a, b) -> b
snd (([Token], [Token]) -> [Token]) -> ([Token], [Token]) -> [Token]
forall a b. (a -> b) -> a -> b
$ Parser [Token]
scanBlock [Token]
tokens

-- | Parses a block followed by the given combinator, returning the tokens the matched.
scanInner :: [Token] -> Parser [Token] -> ([Token], [Token])
scanInner :: [Token] -> Parser [Token] -> ([Token], [Token])
scanInner (token :: Token
token:tokens :: [Token]
tokens) cb :: Parser [Token]
cb = ([Token] -> [Token] -> [Token])
-> Parser [Token] -> Parser [Token] -> Parser [Token]
forall a b c. (a -> b -> c) -> Parser a -> Parser b -> Parser c
concatP [Token] -> [Token] -> [Token]
gather Parser [Token]
scanBlock Parser [Token]
cb [Token]
tokens
    where gather :: [Token] -> [Token] -> [Token]
gather x :: [Token]
x y :: [Token]
y = Token
token Token -> [Token] -> [Token]
forall a. a -> [a] -> [a]
: [Token]
x [Token] -> [Token] -> [Token]
forall a. [a] -> [a] -> [a]
++ [Token]
y
scanInner [] _ = [Char] -> ([Token], [Token])
forall a. HasCallStack => [Char] -> a
error "Expected a token to capture."