module Hydrogen.Util.Parsec (
    module Text.Parsec.Combinator
  , module Text.Parsec.Prim
  , module Text.Parsec.Pos
  , Parser
  , ParseError
  , SomethingBad
  , Tokens
  , runTokenParser
  , mkError
  , sourceToken
  , manyBetween
  , (>+>)
  , (<+<)
  ) where

import Hydrogen.Prelude

import Text.Parsec.Combinator
import Text.Parsec.Error
import Text.Parsec.Pos
import Text.Parsec.Prim

type SomethingBad = (SourcePos, [String])
type Parser source result = source -> Either SomethingBad result
type Tokens t = [(SourcePos, t)]

mkError :: ParseError -> Either SomethingBad b
mkError e = Left (errorPos e, map messageString (errorMessages e))

runTokenParser :: (Stream a Identity t) => ParsecT a () Identity b -> Parser a b
runTokenParser p = either mkError Right . runIdentity . runParserT p () ""

sourceToken :: (Show t, Stream (Tokens t) m (SourcePos, t))
    => (t -> Maybe a)
    -> ParsecT [(SourcePos, t)] u m a
sourceToken f = tokenPrim (show . snd) nextPos (f . snd)
  where
    nextPos p _ = \case
        ((p', _) : _) -> p'
        _ -> p

manyBetween :: (Monad m, Stream s m t)
    => ParsecT s u m open -> ParsecT s u m close -> ParsecT s u m p -> ParsecT s u m [p]
manyBetween o c p = o *> manyTill p c

(>+>) :: Parser a b -> Parser b c -> Parser a c
p1 >+> p2 = join <$> fmap p2 <$> p1

(<+<) :: Parser b c -> Parser a b -> Parser a c
(<+<) = flip (>+>)