module FP.Parser.GreedyParser where

import FP.Prelude
import FP.Pretty
import FP.Parser.Common
import FP.Parser.Effects

-- WORK IN PROGRESS
--
-- Intent: Just like `Parser` but with greedy (PEG) semantics
-- - Non-distributive
-- - Much more efficient

-------------------------
-- Greedy Parser Monad --
-------------------------

newtype GreedyParser t a = GreedyParser
  { runGreedyParser  
      ReaderT (ParserEnv t) 
      (StateT (ParserState t) 
       (FailureT
        (Writer (ParserOut t)))) a 
  }
  deriving 
  (Functor,Monad
  ,MonadFailure
  ,MonadReader (ParserEnv t)
  ,MonadState (ParserState t)
  ,MonadWriter (ParserOut t)
  )

---------------------------
-- Primitive Combinators --
---------------------------

gpFail  GreedyParser t a
gpFail = do
  pi  getL parserStateInputL
  ek  askL parserEnvErrorStackL
  pc  getL parserStateErrorContextL
  tell $ ParserOut bot $ SourceErrorMaybe $ errorSourceLocalContext pi ek pc
  abort

gpPluck  GreedyParser t t
gpPluck = do
  SourceInput ts nextLoc  getL parserStateInputL
  case unconsStream ts of
    Nothing  gpAppendError "more input" gpFail
    Just (x,ts')  do
      let nextNextLoc = case unconsStream ts' of
            Nothing  bumpCol nextLoc
            Just (x',_)  locRangeBegin $ sourceTokenRange x'
      putL parserStateInputL $ SourceInput ts' nextNextLoc
      fmt  askL parserEnvRenderFormatL
      modifyL parserStateErrorContextL $ \ pc  pc  sourceLocalContextFromToken fmt x
      modifyL parserStateCaptureContextL $ \ pc  pc  sourceLocalContextFromToken fmt x
      return $ sourceTokenValue x

gpAppendError  𝕊  GreedyParser t a  GreedyParser t a
gpAppendError msg xM = do
  (stack,msg')  askL parserEnvErrorStackL
  local (update parserEnvErrorStackL (msg':stack,msg)) xM

gpNewContext  Lens (ParserState t) (SourceContextPrefix t)  GreedyParser t a  GreedyParser t (a,SourceContextPrefix t)
gpNewContext 𝓁 xM = do
  pc  getL 𝓁
  putL 𝓁 $ pushSourceLocalContext pc
  x  xM
  pc'  getL 𝓁
  putL 𝓁 $ pc  pc'
  return (x,pc')

gpCapture  GreedyParser t a  GreedyParser t (a,SourceContextPrefix t)
gpCapture = gpNewContext parserStateCaptureContextL

gpRender  Format  GreedyParser t s  GreedyParser t s
gpRender fmt = local $ alter parserEnvRenderFormatL $ () [fmt]

gpEnd  GreedyParser t ()
gpEnd = do
  ts  getL (sourceInputStreamL  parserStateInputL)
  when (shape justL $ unconsStream ts) $ gpAppendError "end of stream" gpFail

gpCatch  GreedyParser t a  GreedyParser t a  GreedyParser t a
gpCatch = (<|>)