module UU.Parsing.Derived where

import UU.Parsing.Interface

infixl 2 <?>
infixl 4  <**>, <??>, <+>
infixl 2 `opt`
infixl 5 <..>


-- =======================================================================================
-- ===== CHECKING ========================================================================
-- =======================================================================================
acceptsepsilon :: (IsParser p s) => p v -> Bool
acceptsepsilon p       = case getzerop p of {Nothing -> False; _ -> True}

mnz :: (IsParser p s) => p v -> t -> String -> t
mnz p v comb
   = if( acceptsepsilon p)
     then   usererror ("The combinator <" ++ comb ++ "> from <Derived.hs>is called with a parser that accepts the empty string.\n"
                    ++
                   "The library cannot handle the resulting left recursive formulation (which is ambiguous too).\n"
                 --  ++
                 --  (case getfirsts p of
                 --   ESeq []  ->  "There are no other alternatives for this parser"
                 --   d        ->  "The other alternatives of this parser may start with:\n"++ show d
                  ) --)
     else v
-- =======================================================================================
-- ===== START OF PRELUDE DEFINITIONS ========== =========================================
-- =======================================================================================

(<..>) :: (IsParser p s) => s -> s -> p s
a <..> b   = pRange a (Range a b)

pExcept :: (IsParser p s, Symbol s, Ord s, Eq (SymbolR s)) => (s, s, s) -> [s] -> p s
pExcept (l,r,err) elems = let ranges = filter (/= EmptyR) (Range l r `except` elems)
                          in if null ranges then pFail
                             else foldr (<|>) pFail (map (pRange err) ranges)



opt ::  (IsParser p s) => p a -> a -> p a
p `opt` v       = mnz p (p  <|> pLow v)  "opt"  -- note that opt is greedy, if you do not want this
                                                -- use "... <|> pSucceed v"  instead
                                                -- p should not recognise the empty string

-- =======================================================================================
-- ===== Special sequential compositions =========================================
-- =======================================================================================
asList ::  (IsParser p s) =>  Expecting s -> p v -> p v
asList  exp = setfirsts (ESeq [EStr "(",  exp, EStr  " ...)*"])

asList1 :: (IsParser p s) => Expecting s -> p v -> p v
asList1 exp = setfirsts (ESeq [EStr "(",  exp, EStr  " ...)+"])

asOpt :: (IsParser p s) => Expecting s -> p v -> p v
asOpt   exp = setfirsts (ESeq [EStr "( ", exp, EStr  " ...)?"])


(<+>) :: (IsParser p s) => p a -> p b -> p (a, b)
pa <+> pb       = (,) <$> pa <*> pb

(<**>) :: (IsParser p s) => p a -> p (a -> b) -> p b
p <**> q        = (\ x f -> f x) <$> p <*> q

(<$$>) :: (IsParser p s) => (a -> b -> c) -> p b -> p (a -> c)
f <$$> p        = pSucceed (flip f) <*> p

(<??>) :: (IsParser p s) => p a -> p (a -> a) -> p a
p <??> q        = p <**> (q `opt` id)

(<?>) :: (IsParser p s) => p v -> String -> p v
p <?>  str      = setfirsts  (EStr str) p

pPacked :: (IsParser p s) => p a -> p b1 -> p b -> p b
pPacked l r x   =   l *>  x <*   r

-- =======================================================================================
-- ===== Iterating ps ===============================================================
-- =======================================================================================
pFoldr_ng :: (IsParser p s) => (a -> a1 -> a1, a1) -> p a -> p a1
pFoldr_ng      alg@(op,e)     p = mnz p (asList (getfirsts p) pfm) "pFoldr_ng"
                                  where pfm = (op <$> p <*> pfm)  <|> pSucceed e
pFoldr_gr :: (IsParser p s) => (a -> b -> b, b) -> p a -> p b
pFoldr_gr      alg@(op,e)     p = mnz p (asList (getfirsts p) pfm) "pFoldr_gr"
                                  where pfm = (op <$> p <*> pfm) `opt` e
pFoldr :: (IsParser p s) =>(a -> b -> b, b) -> p a -> p b
pFoldr         alg            p = pFoldr_gr alg p

pFoldr1_gr :: (IsParser p s) => (v -> b -> b, b) -> p v -> p b
pFoldr1_gr     alg@(op,e)     p = asList1 (getfirsts p) (op <$> p <*> pFoldr_gr  alg p)
pFoldr1_ng ::  (IsParser p s) => (v -> b -> b, b) -> p v -> p b
pFoldr1_ng     alg@(op,e)     p = asList1 (getfirsts p) (op <$> p <*> pFoldr_ng  alg p)
pFoldr1 :: (IsParser p s) => (v -> b -> b, b) -> p v -> p b
pFoldr1        alg            p = pFoldr1_gr alg  p

pFoldrSep_gr :: (IsParser p s) => (v -> b -> b, b) -> p a -> p v -> p b
pFoldrSep_gr   alg@(op,e) sep p = mnz sepp (asList (getfirsts p)((op <$> p <*> pFoldr_gr alg sepp) `opt` e )) "pFoldrSep_gr (both args)"
                                  where sepp = sep *> p
pFoldrSep_ng :: (IsParser p s) => (v -> b -> b, b) -> p a -> p v -> p b
pFoldrSep_ng   alg@(op,e) sep p = mnz sepp (asList (getfirsts p)((op <$> p <*> pFoldr_ng alg sepp)  <|>  pSucceed e)) "pFoldrSep_ng (both args)"
                                  where sepp = sep *> p
pFoldrSep ::  (IsParser p s) => (v -> b -> b, b) -> p a -> p v -> p b
pFoldrSep      alg        sep p = pFoldrSep_gr alg sep p

pFoldr1Sep_gr :: (IsParser p s) => (a -> b -> b, b) -> p a1 -> p a -> p b
pFoldr1Sep_gr  alg@(op,e) sep p = if acceptsepsilon sep then mnz p pfm "pFoldr1Sep_gr (both arguments)" else pfm
                                  where pfm = op <$> p <*> pFoldr_gr alg (sep *> p)
pFoldr1Sep_ng :: (IsParser p s) => (a -> b -> b, b) -> p a1 -> p a -> p b
pFoldr1Sep_ng  alg@(op,e) sep p = if acceptsepsilon sep  then mnz p pfm "pFoldr1Sep_ng (both arguments)" else pfm
                                  where pfm = op <$> p <*> pFoldr_ng alg (sep *> p)
pFoldr1Sep :: (IsParser p s) => (a -> b -> b, b) -> p a1 -> p a -> p b
pFoldr1Sep     alg        sep p = pFoldr1Sep_gr alg sep p

list_alg :: (a -> [a] -> [a], [a1])
list_alg = ((:), [])

pList_gr ::  (IsParser p s) => p a -> p [a]
pList_gr        p = pFoldr_gr     list_alg   p
pList_ng :: (IsParser p s) =>  p a -> p [a]
pList_ng        p = pFoldr_ng     list_alg   p
pList ::  (IsParser p s) => p a -> p [a]
pList           p = pList_gr p

pList1_gr ::  (IsParser p s) => p a -> p [a]
pList1_gr       p = pFoldr1_gr    list_alg   p
pList1_ng :: (IsParser p s) => p a -> p [a]
pList1_ng       p = pFoldr1_ng    list_alg   p
pList1 :: (IsParser p s) =>  p a -> p [a]
pList1          p = pList1_gr                p

pListSep_gr :: (IsParser p s) => p a1 -> p a -> p [a]
pListSep_gr   s p = pFoldrSep_gr  list_alg s p
pListSep_ng :: (IsParser p s) => p a1 -> p a -> p [a]
pListSep_ng   s p = pFoldrSep_ng  list_alg s p
pListSep :: (IsParser p s) => p a -> p a1 -> p [a1]
pListSep      s p = pListSep_gr            s p

pList1Sep_gr :: (IsParser p s) => p a1 -> p a -> p [a]
pList1Sep_gr  s p = pFoldr1Sep_gr list_alg s p
pList1Sep_ng :: (IsParser p s) =>  p a1 -> p a -> p [a]
pList1Sep_ng  s p = pFoldr1Sep_ng list_alg s p
pList1Sep :: (IsParser p s) =>p a -> p a1 -> p [a1]
pList1Sep     s p = pList1Sep_gr          s p

pChainr_gr :: (IsParser p s) => p (c -> c -> c) -> p c -> p c
pChainr_gr op x    =  if acceptsepsilon op then mnz x r "pChainr_gr (both arguments)" else r
                   where r = x <??> (flip <$> op <*> r)
pChainr_ng :: (IsParser p s) => p (a -> a -> a) -> p a -> p a
pChainr_ng op x    =  if acceptsepsilon op then mnz x r "pChainr_ng (both arguments)" else r
                   where r = x <**> ((flip <$> op <*> r)  <|> pSucceed id)
pChainr :: (IsParser p s) => p (c -> c -> c) -> p c -> p c
pChainr    op x    = pChainr_gr op x

pChainl_gr :: (IsParser p s) => p (c -> c -> c) -> p c -> p c
pChainl_gr op x    =  if acceptsepsilon op then mnz x r "pChainl_gr (both arguments)" else r
                      where
                       r      = (f <$> x <*> pList_gr (flip <$> op <*> x) )
                       f x [] = x
                       f x (func:rest) = f (func x) rest

pChainl_ng :: (IsParser p s) => p (c -> c -> c) -> p c -> p c
pChainl_ng op x    =  if acceptsepsilon op then mnz x r "pChainl_ng (both arguments)" else r
                   where
                    r      = (f <$> x <*> pList_ng (flip <$> op <*> x) )
                    f x [] = x
                    f x (func:rest) = f (func x) rest
pChainl :: (IsParser p s) => p (c -> c -> c) -> p c -> p c
pChainl    op x    = pChainl_gr op x

pAny :: (IsParser p s) =>(a -> p a1) -> [a] -> p a1
pAny  f l = if null l then usererror "pAny: argument may not be empty list" else foldr1 (<|>) (map f l)
pAnySym :: (IsParser p s) =>[s] -> p s
pAnySym l = pAny pSym l -- used to be called pAnySym

pToks :: (IsParser p s) => [s] -> p [s]
pToks []     = pSucceed []
pToks (a:as) = (:) <$> pSym a <*> pToks as

pLocate :: (IsParser p s) => [[s]] -> p [s]
pLocate list = pAny pToks list