-- | Utility parser combinators for parsing CSS stylesheets.
module Data.CSS.Syntax.StylishUtil(
        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 a -> b -> c
join Parser a
left Parser b
right [Token]
tokens = (a -> b -> c
join a
x b
y, [Token]
remainder)
    where
        (a
x, [Token]
tokens') = Parser a
left [Token]
tokens
        (b
y, [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 Parser [Token]
cb (Token
token:[Token]
tokens) = (Token
tokenToken -> [Token] -> [Token]
forall a. a -> [a] -> [a]
:[Token]
captured, [Token]
tokens')
   where ([Token]
captured, [Token]
tokens') = Parser [Token]
cb [Token]
tokens
capture Parser [Token]
_ [] = ([], [])

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

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

scanBlock [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 [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) 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 [Token]
x [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 [] Parser [Token]
_ = [Char] -> ([Token], [Token])
forall a. HasCallStack => [Char] -> a
error [Char]
"Expected a token to capture."