{-# LANGUAGE Arrows, RankNTypes #-}

module Text.GRead.Transformations.LeftCorner (leftcorner) where

import Language.AbstractSyntax.TTTAS
import Text.GRead.Grammar
import Text.GRead.Transformations.GramTrafo
import Control.Arrow


leftcorner :: forall a . Grammar a -> Grammar a
leftcorner (Grammar start productions)
      = case runTrafo (lctrafo productions) Unit () of
            Result _ (T tt) gram -> 
                 Grammar (tt start) gram

lctrafo :: Env Productions env env 
        -> Trafo Unit Productions s () (T env s)
lctrafo productions = proc _ ->
            do   rec  let tenv_s = map2trans menv_s
                      menv_s <- (rules1 productions productions) -< tenv_s
                 returnA -< tenv_s


rules1  ::  Env Productions env env
        ->  Env Productions env env' 
        ->  Trafo  Unit Productions s (T env s) 
                                      (Mapping env' s)
rules1 _ Empty          
     = proc _ ->
        returnA -< Mapping Empty  

rules1 productions (Ext ps (PS prods)) 
     = proc tenv_s ->
        do  p <- app_rule1 productions prods -< tenv_s
            r <- newSRef -< p   
            Mapping e <- rules1 productions ps -< tenv_s
            returnA -< Mapping (Ext e r)
 
app_rule1 :: forall env a s. Env Productions env env
          -> [Prod a env]
          -> Trafo Unit Productions s (T env s) (Productions a s)
app_rule1 productions prods =  initMap 
          ( proc tenv_s -> 
                do pss <- sequenceA  (map  (rule1 productions) prods) -< tenv_s 
                   returnA -< PS (concatMap unPS pss)
          )

rule1  :: Env Productions env env -> Prod a env 
       -> GramTrafo env a s (T env s) (Productions a s)
rule1 gram (Seq x beta) 
     = proc tenv_s ->
        do  insert gram x -< (tenv_s, mapProd tenv_s beta)
rule1 _ _ = error "Error: the impossible haṕpened in LeftCorner::rule1"                       

rule2  :: Env Productions env env 
       -> Symbol x env 
       -> GramTrafo env a s (T env s, Ref (x -> a) s) 
                            (Productions a s)
rule2 _    (Term a) 
     = proc (_, a_x) ->
        do  returnA -< PS [rule2a a a_x]
rule2 gram (Nont b) 
     = case lookupEnv b gram of
          PS ps ->  proc (tenv_s, a_x) ->
                      do pss <- sequenceA  
                                (map  (rule2b gram) ps) -< (tenv_s, a_x)
                         returnA -< PS (concatMap unPS pss)


rule2a :: Token -> Ref (Token -> a) s -> Prod a s
rule2a a refA_a
     =  Term a .*. Nont refA_a .*. End ($) 

rule2b  :: Env Productions env env 
        -> Prod b env 
        -> GramTrafo env a s (T env s, Ref (b -> a) s) 
                             (Productions a s)
rule2b gram (Seq x beta) 
     = proc (tenv_s, a_b) ->
         do insert gram x -< (tenv_s, append  (flip (.)) 
                                              (mapProd tenv_s beta) 
                                              (Nont a_b))
rule2b _ _ = error "Error: the impossible haṕpened in LeftCorner::rule2b"                       

insert ::  forall env s a x
       .   Env Productions env env 
       ->  Symbol x env
       ->  GramTrafo env a s  (T env s, Prod (x->a) s)
                              (Productions a s)
insert gram x = 
     Trafo (
           \(MapA_X m) -> case m x of
                       Just r   -> extendA_X (MapA_X m) r
                       Nothing  -> let  Trafo step = insertNewA_X 
                                   in   step (MapA_X m)
           )
     where
       insertNewA_X = proc (tenv_s,p) ->
                     do  r <- newNontR x -< PS [p]
                         rule2 gram x    -< (tenv_s,r)


extendA_X :: m env2-> Ref (x->a) env2-> TrafoE m Productions s env2 (t, Prod (x->a) s) (Productions a env)
extendA_X m r = fmap  (const $ PS []) $ 
                      updateSRef m r (\(_,p) (PS ps) -> PS (p:ps))