{-# LANGUAGE RankNTypes, MultiParamTypeClasses, TypeFamilies, FlexibleContexts, CPP #-}
-- |
--
-- Module      :  Parsek
-- Copyright   :  Koen Claessen 2003
-- License     :  GPL
--
-- Maintainer  :  JP Bernardy
-- Stability   :  provisional
-- Portability :  portable
--
-- This module provides the /Parsek/ library developed by Koen Claessen in his
-- functional pearl article /Parallel Parsing Processes/, Journal of Functional
-- Programming, 14(6), 741-757, Cambridge University Press, 2004:
--
-- <http://www.cs.chalmers.se/~koen/pubs/entry-jfp04-parser.html>

module Text.ParserCombinators.Parsek
  -- basic parser type
  ( Parser         -- :: * -> * -> *; Functor, Monad, MonadPlus
  , Expect         -- :: *; = [String]

  , module Text.ParserCombinators.Class

  -- parsing & parse methods
  , ParseMethod   
  , ParseResult   
  , mapErrR
  , parseFromFile 
  , parse         

  , shortestResult             
  , longestResult              
  , longestResults             
  , allResults                 
  , allResultsStaged           
  , completeResults            

  , shortestResultWithLeftover 
  , longestResultWithLeftover  
  , longestResultsWithLeftover 
  , allResultsWithLeftover     
  
  , module Control.Applicative
  , module Control.Monad
  -- , completeResultsWithLine    -- :: ParseMethod Char a Int [a]
  )
 where

import Prelude hiding (exp,pred)
import Data.Maybe (listToMaybe)
import Control.Applicative
import Control.Monad.Fail as Fail
import Control.Monad
  ( MonadPlus(..)
  , forM_
  , guard
  , ap
  )
import Text.ParserCombinators.Class

-------------------------------------------------------------------------
-- type Parser

newtype Parser s a
  = Parser (forall res. (a -> Expect s -> P s res) -> Expect s -> P s res)

-- | Parsing processes
data P s res
  = Skip Int (P s res) -- ^ skip ahead a number of symbols. At end of file, no effect.
  | Look ([s] -> P s res)
  | Fail (Err s)
  | Result res (P s res)
  | Kill (P s res) -- ^ This is a high priority process trying to kill a low-priority one. See <<|>.

-- | Suppress 1st kill instruction 
noKill :: P s a -> P s a
noKill (Skip n p) = Skip n (noKill p)
noKill (Look fut) = Look $ noKill . fut
noKill (Fail e) = Fail e
noKill (Result res p) = Result res (noKill p)
noKill (Kill p) = p

skip :: Int -> P s a -> P s a
skip 0 = id
skip n = Skip n 

-- The boolean indicates how to interpret 'Kill' instructions. If
-- 'True', the lhs process has a high priority and will kill the rhs
-- when it reaches its Kill instruction (otherwise it fails). If
-- 'False', then both processes have the same priority, and 'Kill'
-- instructions are just propagated.
plus' :: Bool -> P s res -> P s res -> P s res
plus' hasKiller p0 q0 = plus p0 q0 where
  noKill' = if hasKiller then noKill else id
  Kill p         `plus` q              | hasKiller = p
                                       | otherwise = Kill $ p `plus` noKill q
  p              `plus` Kill q         | hasKiller = error "plus': Impossible"
                                       | otherwise = Kill $ noKill p `plus` q
  Skip m p       `plus` Skip n q       | m <= n    = Skip m $ p `plus` skip (n-m) q
                                       | otherwise = Skip n $ skip (m-n) p `plus` skip n q
  Fail err1      `plus` Fail err2      = Fail (err1 ++ err2)
  p              `plus` Result res q   = Result res (p `plus` q)
  Result res p   `plus` q              = Result res (p `plus` q)
  Look fut1      `plus` Look fut2      = Look (\s -> fut1 s `plus` fut2 s)
  Look fut1      `plus` q              = Look (\s -> fut1 s `plus` q)
  p              `plus` Look fut2      = Look (\s -> p `plus` fut2 s)
  p@(Skip _ _)   `plus` _              = noKill' p
  _              `plus` q@(Skip _ _)   = q

type Err s = [(Expect s, -- we expect this stuff
               String -- but failed for this reason
              )]

-- | An intersection (nesting) of things currently expected
type Expect s = [(String, -- Expect this
                  Maybe s -- Here
                )]

-------------------------------------------------------------------------
-- instances

instance Functor (Parser s) where
  fmap p (Parser f) =
    Parser (\fut -> f (fut . p))

instance Monad (Parser s) where
  return a = Parser (\fut -> fut a)

  Parser f >>= k =
    Parser (\fut -> f (\a -> let Parser g = k a in g fut))
#if !MIN_VERSION_base(4,11,0)
  -- Monad(fail) was removed in GHC 8.8.1
  fail = Fail.fail
#endif

instance Fail.MonadFail (Parser s) where
  fail s = Parser (\_fut exp -> Fail [(exp,s)])

instance MonadPlus (Parser s) where
  mzero = Parser (\_fut exp -> Fail [(exp,"mzero")])

  mplus (Parser f) (Parser g) =
    Parser (\fut exp -> plus' False (f fut exp) (g fut exp))

instance Applicative (Parser s) where
  pure = return
  (<*>) = ap

instance Alternative (Parser s) where
  (<|>) = mplus
  empty = mzero

instance IsParser (Parser s) where
  type SymbolOf (Parser s) = s
  satisfy pred =
    Parser $ \fut exp -> Look $ \xs -> case xs of
      (c:_) | pred c -> Skip 1 $ fut c exp
      _ -> Fail [(exp,"satisfy")]
    
  look = Parser $ \fut exp -> Look $ \s -> fut s exp

  label msg (Parser f) =
    Parser $ \fut exp ->
        Look $ \xs -> 
        f (\a _ -> fut a exp) -- drop the extra expectation in the future
          ((msg,listToMaybe xs):exp) -- locally have an extra expectation

  Parser f <<|> Parser g = Parser $ \fut exp -> 
    plus' True (f (\a x -> Kill (fut a x)) exp) (g fut exp)

-------------------------------------------------------------------------
-- type ParseMethod, ParseResult

type ParseMethod s a r = P s a -> [s] -> ParseResult s r

type ParseResult s r
  = Either (Err s) r

mapErrR :: (s -> s') -> ParseResult s r -> ParseResult s' r
mapErrR _ (Right x) = Right x
mapErrR f (Left x) = Left (mapErr f x)

first f (a,b) = (f a,b)
second f (a,b) = (a, f b)

mapErr :: (a -> b) -> Err a -> Err b
mapErr f = map (first (mapExpect f))

mapExpect :: (a -> b) -> Expect a -> Expect b
mapExpect f = map (second (fmap f))

-- parse functions

parseFromFile :: Parser Char a -> ParseMethod Char a r -> FilePath -> IO (ParseResult Char r)
parseFromFile p method file =
  do s <- readFile file
     return (parse p method s)

parse :: Parser s a -> ParseMethod s a r -> [s] -> ParseResult s r
parse (Parser f) method xs = method (f (\a _exp -> Result a (Fail [])) []) xs

-- parse methods
shortestResult :: ParseMethod s a a
shortestResult = scan
 where
  scan (Skip n p)     xs     = scan p (drop n xs)
  scan (Result res _) _      = Right res
  scan (Fail err) _          = Left err 
  scan (Look f)       xs     = scan (f xs) xs

longestResult :: ParseMethod s a a
longestResult p0 = scan p0 Nothing 
 where
  scan (Skip n p)     mres       xs     = scan p mres (drop n xs)
  scan (Result res p) _          xs     = scan p (Just res) xs
  scan (Fail err)     Nothing    _      = Left err 
  scan (Fail _  )     (Just res) _      = Right res
  scan (Look f)       mres       xs     = scan (f xs) mres xs

longestResults :: ParseMethod s a [a]
longestResults p0 = scan p0 [] [] 
 where
  scan (Skip n p)     []  old xs     = scan p [] old (drop n xs)
  scan (Skip n p )    new old xs     = scan p [] new (drop n xs)
  scan (Result res p) new old xs     = scan p (res:new) [] xs
  scan (Fail err)     []  []  _      = Left err
  scan (Fail _)       []  old _      = Right old
  scan (Fail _)       new _   _      = Right new
  scan (Look f)       new old xs     = scan (f xs) new old xs

allResultsStaged :: ParseMethod s a [[a]]
allResultsStaged p0 xs0 = Right (scan p0 [] xs0)
 where
  scan (Skip n p)     ys xs     = ys : scan p [] (drop n xs)
  scan (Result res p) ys xs     = scan p (res:ys) xs
  scan (Fail _)       ys _      = [ys]
  scan (Look f)       ys xs     = scan (f xs) ys xs

allResults :: ParseMethod s a [a]
allResults = scan
 where
  scan (Skip n p)     xs     = scan p (drop n xs)
  scan (Result res p) xs     = Right (res : scan' p xs)
  scan (Fail err)     _      = Left err
  scan (Look f)       xs     = scan (f xs) xs

  scan' p xs =
    case scan p xs of
      Left  _    -> []
      Right ress -> ress

completeResults :: ParseMethod s a [a]
completeResults = scan
 where
  scan (Skip n p)     xs     = scan p (drop n xs)
  scan (Result res p) []     = Right (res : scan' p [])
  scan (Result _ p)   xs     = scan p xs
  scan (Fail err)     _      = Left err
  scan (Look f)       xs     = scan (f xs) xs

  scan' p xs =
    case scan p xs of
      Left  _    -> []
      Right ress -> ress

-- with left overs

shortestResultWithLeftover :: ParseMethod s a (a,[s])
shortestResultWithLeftover = scan
 where
  scan (Skip n p)     xs     = scan p (drop n xs)
  scan (Result res _) xs     = Right (res,xs)
  scan (Fail err)     _      = Left err
  scan (Look f)       xs     = scan (f xs) xs

longestResultWithLeftover :: ParseMethod s a (a,[s])
longestResultWithLeftover p0 = scan p0 Nothing
 where
  scan (Skip n p)     mres         xs     = scan p mres (drop n xs)
  scan (Result res p) _            xs     = scan p (Just (res,xs)) xs
  scan (Fail err)     Nothing      _      = Left err
  scan (Fail _)       (Just resxs) _      = Right resxs
  scan (Look f)       mres         xs     = scan (f xs) mres xs

longestResultsWithLeftover :: ParseMethod s a ([a],Maybe [s])
longestResultsWithLeftover p0 = scan p0 empty empty
 where
  scan (Skip n p)     ([],_) old    xs     = scan p empty old $ drop n xs
  scan (Skip n p)     new    _      xs     = scan p empty new $ drop n xs
  scan (Result res p) (as,_) _      xs     = scan p (res:as,Just xs) empty xs
  scan (Fail err)     ([],_) ([],_) _      = Left err
  scan (Fail _)       ([],_)  old _        = Right old
  scan (Fail _)       new _   _            = Right new
  scan (Look f)       new    old    xs     = scan (f xs) new old xs

  empty = ([],Nothing)

allResultsWithLeftover :: ParseMethod s a [(a,[s])]
allResultsWithLeftover = scan
 where
  scan (Skip n p)     xs     = scan p $ drop n xs
  scan (Result res p) xs     = Right ((res,xs) : scan' p xs)
  scan (Fail err)     []     = Left err
  scan (Look f)       xs     = scan (f xs) xs

  scan' p xs =
    case scan p xs of
      Left  _    -> []
      Right ress -> ress
