{-  Copyright 2010 Dominique Devriese

    This file is part of the grammar-combinators library.

    The grammar-combinators library is free software: you can
    redistribute it and/or modify it under the terms of the GNU
    Lesser General Public License as published by the Free
    Software Foundation, either version 3 of the License, or (at
    your option) any later version.

    Foobar is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General
    Public License along with Foobar. If not, see
    <http://www.gnu.org/licenses/>.
-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Text.GrammarCombinators.Transform.UnfoldRecursion (
  UnfoldDepth,
  unfoldRecursion,
  unfoldRecursionE,
  selectNothing,
  selectAllOnce,
  selectNT,
  modifyUnfoldDepth,
  unfoldSelective,
  unfoldSelectiveE,
  unfoldRule,
  unfoldRuleE
  ) where

import Text.GrammarCombinators.Base
import Text.GrammarCombinators.Transform.UnfoldLoops

data RPWRule p phi ixT r t v =
  RPWRule { unRPWRule :: (forall ix. phi ix -> p (r ix)) -> p v }

instance (ProductionRule p) =>
         ProductionRule (RPWRule p phi ixT r t) where
  a >>> b = RPWRule $ \g -> unRPWRule a g >>> unRPWRule b g
  a ||| b = RPWRule $ \g -> unRPWRule a g ||| unRPWRule b g
  die = RPWRule $ const die
  endOfInput = RPWRule $ const endOfInput

instance (LiftableProductionRule p) =>
         LiftableProductionRule (RPWRule p phi ixT r t) where
  epsilonL v q = RPWRule $ \_ -> epsilonL v q

instance (EpsProductionRule p) =>
         EpsProductionRule (RPWRule p phi ixT r t) where
  epsilon v = RPWRule $ \_ -> epsilon v

instance (TokenProductionRule p t) =>
         TokenProductionRule (RPWRule p phi ixT r t) t where
  token c = RPWRule $ \_ -> token c

instance (ProductionRule p) =>
         RecProductionRule (RPWRule p phi ixT r t) phi r where
  ref idx = RPWRule $ \g -> g idx

instance (LoopProductionRule p phi r) =>
         LoopProductionRule (RPWRule p phi ixT r t) phi r where
  -- we don't unfold loops, use unfoldLoops for that...
  manyRef idx = RPWRule $ \_ -> manyRef idx 
  many1Ref idx = RPWRule $ \_ -> many1Ref idx 

-- | Unfold recursion in a given contextx-free grammar, replacing
-- calls to
-- 'ref' idx with the non-terminal's production rule. This produces
-- production rules similar to those in traditional parser combinator
-- libraries.
unfoldRecursion ::
  ProcessingContextFreeGrammar phi t r ->
  ProcessingRegularGrammar phi t r
unfoldRecursion gram idx =
  unRPWRule (gram idx) $ unfoldRecursion gram

-- | Unfold recursion in a given extended context-free grammar,
-- replacing calls to
-- 'ref' idx with the non-terminal's production rule. This produces
-- production rules similar to those in traditional parser combinator
-- libraries.
unfoldRecursionE :: 
  ProcessingExtendedContextFreeGrammar phi t r ->
  ProcessingRegularGrammar phi t r
unfoldRecursionE gram = unfoldRecursion (unfoldLoops gram) 

-- instance (ProductionRule p) => SuperProductionRule (RPWRule p) where
--   subref subgram idxb = rpwSubRef (subgram idxb) idxb 
  
-- rpwSubRef :: forall p phi phi' supIxT ixT r ix t .
--              (DomainEmbedding phi phi' supIxT, HFunctor phi (PF phi), ProductionRule p) =>
--              RPWRule p phi' (IxMapSeq ixT supIxT) (SubVal supIxT r) t (PF phi' (SubVal supIxT r) ix) ->
--              phi' ix -> phi (supIxT ix) ->
--              RPWRule p phi ixT r t (PF phi r (supIxT ix)) 
-- rpwSubRef (RPWRule subintrule) idxb idx = RPWRule $ \outgram ->
--   let
--     restrictedGrammar :: forall ix'. phi' ix' -> p ((SubVal supIxT r) ix')
--     restrictedGrammar idx' = epsilon MkSubVal >>> outgram (supIx idx')
--     presult' :: p (PF phi' (SubVal supIxT r) ix)
--     presult' = subintrule restrictedGrammar
--   in epsilon (supPF idxb idx) >>> presult'

-- | A value of type UnfoldDepth defines for each non-terminal in a
-- grammar how many times it should be unfolded by the 'unfoldSelective'
-- or 'unfoldSelectiveE' algorithms.
type UnfoldDepth phi = forall ix. phi ix -> Integer

-- | A value of type 'UnfoldDepth' phi indicating nothing should be
-- unfolded at all. This can be used as a start value and then further
-- modified with the 'selectNT' function.
selectNothing :: UnfoldDepth phi
selectNothing _ = 0

-- | A value of type 'UnfoldDepth' phi indicating every non-terminal
-- should be unfolded once.
selectAllOnce :: UnfoldDepth phi
selectAllOnce _ = 1

-- | A function modifying a given 'UnfoldDepth' phi by applying a given
-- function to the depth for a given non-terminal.
modifyUnfoldDepth :: (EqFam phi) => UnfoldDepth phi -> (Integer -> Integer) -> phi ix -> UnfoldDepth phi
modifyUnfoldDepth base f idx = overrideIdxK base idx $ f $ base idx

-- | A function modifying a given 'UnfoldDepth' phi by increasing 
-- the depth for a given non-terminal by 1.
selectNT :: (EqFam phi) => UnfoldDepth phi -> phi ix -> UnfoldDepth phi
selectNT base = modifyUnfoldDepth base (+1) 

type RPWGrammar p phi ixT r v t =
  forall ix. phi ix -> RPWRule p phi ixT r t (v ix)

unfoldSelective' :: (EqFam phi, RecProductionRule p phi r) =>
  UnfoldDepth phi ->
  RPWGrammar p phi ixT r r t ->
  (forall ix. phi ix -> p (r ix))
unfoldSelective' sel gram idx =
  let
    rg idx' = if sel idx' > 0
              then unfoldSelective' (modifyUnfoldDepth sel (flip (-) 1) idx) gram idx'
             else ref idx'
  in unRPWRule (gram idx) rg

-- | Selectively unfold a given context-free grammar according to a 
-- given 'UnfoldDepth'.
unfoldSelective :: (EqFam phi) =>
  UnfoldDepth phi ->
  ProcessingContextFreeGrammar phi t r ->
  ProcessingContextFreeGrammar phi t r
unfoldSelective sel gram idx = unfoldSelective' sel gram idx

-- | Selectively unfold a given extended context-free grammar
-- according to a given 'UnfoldDepth'.
unfoldSelectiveE :: (EqFam phi) =>
  UnfoldDepth phi ->
  ProcessingExtendedContextFreeGrammar phi t r ->
  ProcessingExtendedContextFreeGrammar phi t r
unfoldSelectiveE sel gram idx =
  let
    rg idx' = if sel idx' > 0
             then unfoldSelective' (modifyUnfoldDepth sel (flip (-) 1) idx') gram idx'
             else ref idx'
  in unRPWRule (gram idx) rg

-- | Unfold a given context-free rule by replacing all references to
-- non-terminals with the production rule for that non-terminal in 
-- a given processing context-free grammar.
unfoldRule :: ContextFreeRule phi r t v -> 
              ProcessingContextFreeGrammar phi t r ->
              ContextFreeRule phi r t v
unfoldRule r g = unRPWRule r g

-- | Unfold a given extended context-free rule by replacing all
-- references to
-- non-terminals with the production rule for that non-terminal in 
-- a given processing extended context-free grammar.
unfoldRuleE :: ExtendedContextFreeRule phi r t v -> 
              ProcessingExtendedContextFreeGrammar phi t r ->
              ExtendedContextFreeRule phi r t v
unfoldRuleE r g = unRPWRule r g