module Bookhound.Parsers.Collections (collOf, listOf, tupleOf, mapOf) where

import Bookhound.Parser            (Parser, withError)
import Bookhound.ParserCombinators (anySepBy, maybeWithin, satisfies)
import Bookhound.Parsers.Char      (closeCurly, closeParens, closeSquare, comma,
                                    openCurly, openParens, openSquare)
import Bookhound.Parsers.String    (spacing)

import           Data.Map (Map)
import qualified Data.Map as Map


collOf :: Parser a -> Parser b -> Parser c -> Parser d -> Parser [d]
collOf :: Parser a -> Parser b -> Parser c -> Parser d -> Parser [d]
collOf Parser a
start Parser b
end Parser c
sep Parser d
elemParser = String -> Parser [d] -> Parser [d]
forall a. String -> Parser a -> Parser a
withError String
"Collection"
  (Parser [d] -> Parser [d]) -> Parser [d] -> Parser [d]
forall a b. (a -> b) -> a -> b
$ Parser a
start Parser a -> Parser [d] -> Parser [d]
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser [d]
elemsParser Parser [d] -> Parser b -> Parser [d]
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Parser b
end
  where
    elemsParser :: Parser [d]
elemsParser = Parser c -> Parser d -> Parser [d]
forall a b. Parser a -> Parser b -> Parser [b]
anySepBy Parser c
sep (Parser d -> Parser [d]) -> Parser d -> Parser [d]
forall a b. (a -> b) -> a -> b
$ Parser String -> Parser d -> Parser d
forall a b. Parser a -> Parser b -> Parser b
maybeWithin Parser String
spacing Parser d
elemParser

listOf :: Parser a -> Parser [a]
listOf :: Parser a -> Parser [a]
listOf = String -> Parser [a] -> Parser [a]
forall a. String -> Parser a -> Parser a
withError String
"List"
  (Parser [a] -> Parser [a])
-> (Parser a -> Parser [a]) -> Parser a -> Parser [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parser Char -> Parser Char -> Parser Char -> Parser a -> Parser [a]
forall a b c d.
Parser a -> Parser b -> Parser c -> Parser d -> Parser [d]
collOf Parser Char
openSquare Parser Char
closeSquare Parser Char
comma

tupleOf :: Parser a -> Parser [a]
tupleOf :: Parser a -> Parser [a]
tupleOf = String -> Parser [a] -> Parser [a]
forall a. String -> Parser a -> Parser a
withError String
"Tuple"
  (Parser [a] -> Parser [a])
-> (Parser a -> Parser [a]) -> Parser a -> Parser [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([a] -> Bool) -> Parser [a] -> Parser [a]
forall a. (a -> Bool) -> Parser a -> Parser a
satisfies ((Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
2) (Int -> Bool) -> ([a] -> Int) -> [a] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length)
  (Parser [a] -> Parser [a])
-> (Parser a -> Parser [a]) -> Parser a -> Parser [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parser Char -> Parser Char -> Parser Char -> Parser a -> Parser [a]
forall a b c d.
Parser a -> Parser b -> Parser c -> Parser d -> Parser [d]
collOf Parser Char
openParens Parser Char
closeParens Parser Char
comma

mapOf :: Ord b => Parser a -> Parser b -> Parser c -> Parser (Map b c)
mapOf :: Parser a -> Parser b -> Parser c -> Parser (Map b c)
mapOf Parser a
sep Parser b
p1 Parser c
p2 = String -> Parser (Map b c) -> Parser (Map b c)
forall a. String -> Parser a -> Parser a
withError String
"Map"
  (Parser (Map b c) -> Parser (Map b c))
-> Parser (Map b c) -> Parser (Map b c)
forall a b. (a -> b) -> a -> b
$ [(b, c)] -> Map b c
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(b, c)] -> Map b c) -> Parser [(b, c)] -> Parser (Map b c)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Char
-> Parser Char -> Parser Char -> Parser (b, c) -> Parser [(b, c)]
forall a b c d.
Parser a -> Parser b -> Parser c -> Parser d -> Parser [d]
collOf Parser Char
openCurly Parser Char
closeCurly Parser Char
comma Parser (b, c)
mapEntry
  where
    mapEntry :: Parser (b, c)
mapEntry = (,) (b -> c -> (b, c)) -> Parser b -> Parser (c -> (b, c))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser b
p1 Parser (c -> (b, c)) -> Parser a -> Parser (c -> (b, c))
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Parser String -> Parser a -> Parser a
forall a b. Parser a -> Parser b -> Parser b
maybeWithin Parser String
spacing Parser a
sep Parser (c -> (b, c)) -> Parser c -> Parser (b, c)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser c
p2