module FP.Parser.Mixfix where

import FP.Prelude
import FP.Pretty
import FP.Parser.Parser

-- NOTES:
-- - perhaps `mixfixParser` could be optimized by using greedy `pCatch` instead
--   of backtracking `mconcat`. Still not sure about this though...
-- - See the discussion at the end of "Parsing Mixfix Operators" (Danielsson
--   and Norell)--which this implementation is based on--which references Aasa
--   (1995,1992) on a more permissive system, which I think is more
--   representative of what Agda does.
-- - Perhaps make helpers for when `a = g (f a)` and (possibly simultaneously)
--   when `g = ParserFullContext t`.

-----------------------------------
-- Fully Functor/Comonad general --
-----------------------------------

data MixesF t f a = MixesF
  { mixesFPre   Parser t (f a  a)
  , mixesFPost  Parser t (f a  a)
  , mixesFInf   Parser t (f a  f a  a)
  , mixesFInfl  Parser t (f a  f a  a)
  , mixesFInfr  Parser t (f a  f a  a)
  }

instance Monoid (MixesF t f a) where
  null = MixesF mnull mnull mnull mnull mnull
  MixesF pre₁ post₁ inf₁ infl₁ infr₁  MixesF pre₂ post₂ inf₂ infl₂ infr₂ = 
    MixesF (pre₁ <> pre₂) (post₁ <> post₂) (inf₁ <> inf₂) (infl₁ <> infl₂) (infr₁ <> infr₂)

data MixF t f a =
    PreF   (Parser t (f a  a))
  | PostF  (Parser t (f a  a))
  | InfF   (Parser t (f a  f a  a))
  | InflF  (Parser t (f a  f a  a))
  | InfrF  (Parser t (f a  f a  a))
  | TerminalF (Parser t a)

data MixfixF t f a = MixfixF
  { mixfixFTerminals  Parser t a
  , mixfixFLevels    MixesF t f a
  }
instance Monoid (MixfixF t f a) where
  null = MixfixF mnull bot
  MixfixF ts₁ ls₁  MixfixF ts₂ ls₂ = MixfixF (ts₁ <> ts₂) (ls₁  ls₂)

mixF  MixF t f a  MixfixF t f a
mixF (PreF l pre) = null {mixfixFLevels = dict [l  null {mixesFPre = pre}]}
mixF (PostF l post) = null {mixfixFLevels = dict [l  null {mixesFPost = post}]}
mixF (InfF l inf) = null {mixfixFLevels = dict [l  null {mixesFInf = inf}]}
mixF (InflF l infl) = null {mixfixFLevels = dict [l  null {mixesFInfl = infl}]}
mixF (InfrF l infr) = null {mixfixFLevels = dict [l  null {mixesFInfr = infr}]}
mixF (TerminalF term) = null {mixfixFTerminals = term}

-- PRE (PRE (x INFR (PRE (PRE y))))
-- PRE PRE x INFR PRE PRE y
-- 
-- ((((x POST) POST) INFL y) POST) POST
-- x POST POST INFL y POST POST

mixfixParserF  
   t f a. (Comonad f)
   MixfixF t f a  (Parser t a  Parser t (f a))  Parser t (f a)
mixfixParserF (MixfixF terms levels₀) fld = loop levels₀
  where
    loop    MixesF t f a  Parser t (f a)
    loop levels = case removeMinDict levels of
      Nothing  fld $ terms
      Just ((i,mixes),levels') 
        let msg = "lvl "  alignRightFill '0' (𝕟 3) (ppString i)
        in 
        fld $ buildLevelDirected msg mixes $ 
        fld $ buildLevelNondirected msg mixes $ 
        loop levels'
    buildLevelNondirected  𝕊  MixesF t f a  Parser t (f a)  Parser t a
    buildLevelNondirected msg mixes nextLevel = do
      x  nextLevel
      mconcat
        [ pAppendError (msg  " infix") $ levelInfAfterOne x nextLevel mixes
        , return $ extract x
        ]
    buildLevelDirected  𝕊  MixesF t f a  Parser t (f a)  Parser t a
    buildLevelDirected msg mixes nextLevel = mconcat
      [ do
          x  nextLevel
          mconcat
            [ pAppendError (msg  " infixl") $ levelInflAfterOne x nextLevel mixes
            , pAppendError (msg  " infixr") $ levelInfrAfterOne x nextLevel mixes
            , return $ extract x
            ]
      , pAppendError (msg  " infixr") $ levelInfrNotAfterOne nextLevel mixes
      ]
    levelInflAfterOne  f a  Parser t (f a)  MixesF t f a  Parser t a
    levelInflAfterOne x nextLevel mixes = do
      fxs  pOneOrMoreGreedy $ mconcat
        [ mixesFPost mixes
        , do
            f  mixesFInfl mixes
            x₂  nextLevel
            return $ \ x₁  f x₁ x₂
        ]
      return $ wcompose (reverse fxs) x
    _levelInfr  Parser t (f a)  MixesF t f a  Parser t a
    _levelInfr nextLevel mixes = do
      fxs  pOneOrMoreGreedy $ mconcat
        [ mixesFPre mixes
        , do
            x₁  nextLevel
            f  mixesFInfr mixes
            return $ \ x₂  f x₁ x₂
        ]
      x  nextLevel
      return $ wcompose fxs x
    levelInfrAfterOne  f a  Parser t (f a)  MixesF t f a  Parser t a
    levelInfrAfterOne x₁ nextLevel mixes = do
      f  mixesFInfr mixes
      levelInfrAfterOneCombo (\ x₂  f x₁ x₂) nextLevel mixes
    levelInfrNotAfterOne  Parser t (f a)  MixesF t f a  Parser t a
    levelInfrNotAfterOne nextLevel mixes = do
      f  mixesFPre mixes
      levelInfrAfterOneCombo f nextLevel mixes
    levelInfrAfterOneCombo  (f a  a)  Parser t (f a)  MixesF t f a  Parser t a
    levelInfrAfterOneCombo f nextLevel mixes = do
      fxs  [f a  a]  pManyGreedy $ mconcat
        [ mixesFPre mixes
        , do
            x₁  nextLevel
            f'  mixesFInfr mixes
            return $ \ x₂  f' x₁ x₂
        ]
      x₂  nextLevel
      return $ wcompose (f:fxs) x₂
    levelInfAfterOne  f a  Parser t (f a)  MixesF t f a  Parser t a
    levelInfAfterOne x₁ nextLevel mixes = do
      f  mixesFInf mixes
      x₂  nextLevel
      return $ f x₁ x₂

---------------
-- Non-fancy --
---------------

data Mixes t a = Mixes
  { mixesPre   Parser t (a  a)
  , mixesPost  Parser t (a  a)
  , mixesInf   Parser t (a  a  a)
  , mixesInfl  Parser t (a  a  a)
  , mixesInfr  Parser t (a  a  a)
  }

instance Monoid (Mixes t a) where
  null = Mixes mnull mnull mnull mnull mnull
  Mixes pre₁ post₁ inf₁ infl₁ infr₁  Mixes pre₂ post₂ inf₂ infl₂ infr₂ = 
    Mixes (pre₁ <> pre₂) (post₁ <> post₂) (inf₁ <> inf₂) (infl₁ <> infl₂) (infr₁ <> infr₂)

mixesPure  Mixes t a  MixesF t ID a
mixesPure (Mixes pre post inf infl infr) =
  MixesF
  (map kextract pre)
  (map kextract post)
  (map kextract2 inf)
  (map kextract2 infl)
  (map kextract2 infr)

data Mixfix t a = Mixfix 
  { mixfixTerminals  Parser t a
  , mixfixLevels    Mixes t a
  }

instance Monoid (Mixfix t a) where
  null = Mixfix mnull bot
  Mixfix ts₁ ls₁  Mixfix ts₂ ls₂ = Mixfix (ts₁ <> ts₂) (ls₁  ls₂)

data Mix t a =
    Pre   (Parser t (a  a))
  | Post  (Parser t (a  a))
  | Inf   (Parser t (a  a  a))
  | Infl  (Parser t (a  a  a))
  | Infr  (Parser t (a  a  a))
  | Terminal (Parser t a)

mix  Mix t a  Mixfix t a
mix (Pre l pre) = null {mixfixLevels = dict [l  null {mixesPre = pre}]}
mix (Post l post) = null {mixfixLevels = dict [l  null {mixesPost = post}]}
mix (Inf l inf) = null {mixfixLevels = dict [l  null {mixesInf = inf}]}
mix (Infl l infl) = null {mixfixLevels = dict [l  null {mixesInfl = infl}]}
mix (Infr l infr) = null {mixfixLevels = dict [l  null {mixesInfr = infr}]}
mix (Terminal term) = null {mixfixTerminals = term}

mixfixPure  Mixfix t a  MixfixF t ID a
mixfixPure (Mixfix terminals levels) = MixfixF terminals $ map mixesPure levels

mixfixParser  Mixfix t a  Parser t a
mixfixParser mixfix = runID ^$ mixfixParserF (mixfixPure mixfix) (map ID)