-----------------------------------------------------------------------------
-- Copyright 2014, Open Universiteit Nederland. This file is distributed
-- under the terms of the GNU General Public License. For more information,
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-- Basic machinery for executing a core strategy expression.
--
-----------------------------------------------------------------------------
--  $Id: Parsing.hs 6535 2014-05-14 11:05:06Z bastiaan $

module Ideas.Common.Strategy.Parsing
   ( Step(..)
   , ParseState, makeState, choices, trace
   , parseDerivationTree, replay, runCore, searchModeState, toProcess
   ) where

import Data.Function (on)
import Data.Monoid
import Ideas.Common.Classes
import Ideas.Common.DerivationTree
import Ideas.Common.Environment
import Ideas.Common.Rule
import Ideas.Common.Strategy.Core
import Ideas.Common.Strategy.Path
import Ideas.Common.Strategy.Sequential hiding (replay)
import Ideas.Common.Utils (fst3)
import qualified Ideas.Common.Strategy.Sequential as Sequential

----------------------------------------------------------------------
-- Step data type

data Step l a = Enter l | Exit l | RuleStep Environment (Rule a)
   deriving (Eq)

instance Show (Step l a) where
   show (Enter _) = "Enter"
   show (Exit _)  = "Exit"
   show (RuleStep _ r) = show r

-- A core expression where the symbols are steps instead of "only" rules
-- type StepCore l a = GCore l (Step l a)

instance Apply (Step l) where
   applyAll (RuleStep _ r) = applyAll r
   applyAll _              = return

instance Minor (Step l a) where
   setMinor b (RuleStep env r) = RuleStep env (setMinor b r)
   setMinor _ step = step

   isMinor (RuleStep _ r) = isMinor r
   isMinor _ = True

----------------------------------------------------------------------
-- State data type

data ParseState l a = S
   { trace     :: [Step l a]
   , choices   :: Path
   , remainder :: Process (Step l a, a, Path)
   }

makeState :: a -> Core l a -> ParseState l a
makeState a = S [] emptyPath . applyMin a . withPath . toProcess

----------------------------------------------------------------------
-- Parse derivation tree

parseDerivationTree :: a -> ParseState l a -> DerivationTree (Step l a) (a, ParseState l a)
parseDerivationTree = curry (makeTree next)
 where
   next (_, st) = (empty (remainder st), stateFirsts st (remainder st))

   stateFirsts st p =
      [ ( step
        , (a, st {trace = step:trace st, remainder = q, choices = path})
        )
      | ((step, a, path), q) <- Sequential.firsts p
      ]

searchModeState :: (Step l a -> Bool) -> (Step l a -> Step l a -> Bool) -> ParseState l a -> ParseState l a
searchModeState p eq state =
    state { remainder = tidyProcess eq' (not . p') $
                        uniquePath p' eq' (remainder state) }
  where
    eq' = eq `on` fst3
    p'  = p . fst3

----------------------------------------------------------------------
-- Running the parser

runCore :: Core l a -> a -> [a]
runCore = runProcess . toProcess . noLabels
 where
   runProcess p a = rec a (applyMin2 a p)

   rec a p =
      (if empty p then (a:) else id)
      [ c
      | ((_, b), q) <- firsts p
      , c <- rec b q
      ]

-----------------------------

toProcess :: Core l a -> Process (Step l a)
toProcess = fromAtoms . build . rec . coreSubstAll
 where
   rec core =
      case core of
         a :*: b   -> rec a <*> rec b
         a :|: b   -> rec a <|> rec b
         Rule r    -> single (Single (RuleStep mempty r))
         a :|>: b  -> rec a <?> rec b
         Fail      -> stop
         Succeed   -> ok
         Label l a -> Single (Enter l) ~> rec a
                      <*> single (Single (Exit l))
         a :%: b   -> concurrent switch (build (rec a)) (build (rec b))
         a :@: b   -> build (rec a) <@> build (rec b)
         Atomic a  -> atomic (build (rec a))
         Let _ _   -> error "toMin: let"
         Var _     -> error "toMin: var"

   switch (Single (Enter _)) = False
   switch _ = True

applyMin2 :: a -> Process (Step l a) -> Process (Step l a, a)
applyMin2 a0 = prune (isMajor . fst) . scanChoice step a0
 where
   step a (RuleStep _ r) =
      [ (b, (RuleStep env r, b))
      | (b, env) <- transApply (transformation r) a
      ]
   step a st = [(a, (st, a))]

applyMin :: a -> Process (Step l a, Path) -> Process (Step l a, a, Path)
applyMin a0 = prune (isMajor . fst3) . scanChoice step a0
 where
   step a (RuleStep _ r, bs) =
      [ (b, (RuleStep env r, b, bs))
      | (b, env) <- transApply (transformation r) a
      ]
   step a (st, bs) = [(a, (st, a, bs))]

replay :: Monad m => Path -> a -> Core l a -> m (ParseState l a)
replay path a core = do
   (as, p) <- Sequential.replay path $ withPath $ toProcess core
   return (S (map fst as) path (applyMin a p))