{-# LANGUAGE TypeFamilies #-}

-- | Desugaring Source Tetra guards to simple case-expressions.
module DDC.Source.Tetra.Transform.Guards
        ( desugarGuards )
where
import DDC.Source.Tetra.Transform.BoundX
import DDC.Source.Tetra.Compounds
import DDC.Source.Tetra.Exp.Annot
import DDC.Type.Exp


-- | Desugar some guards to a case-expression.
--   At runtime, if none of the guards match then run the provided fail action.
desugarGuards
        :: forall a
        .  GAnnot (Annot a)         -- ^ Annotation.
        -> [GGuardedExp (Annot a)]  -- ^ Guarded expressions to desugar.
        -> GExp (Annot a)           -- ^ Failure action.
        -> GExp (Annot a)

desugarGuards a gs0 fail0
 = go gs0 fail0
 where
        -- Desugar list of guarded expressions.
        go [] cont
         = cont

        go [g]   cont
         = go1 g cont

        go (g : gs) cont
         = go1 g (go gs cont)

        -- Desugar single guarded expression.
        go1 (GExp x1) _
         = x1

        go1 (GGuard GDefault   gs) cont
         = go1 gs cont

        -- Simple cases where we can avoid introducing the continuation.
        go1 (GGuard (GPred g1)   (GExp x1)) cont
         = XCase a g1
                [ AAlt pTrue     [GExp x1]
                , AAlt PDefault  [GExp cont] ]

        go1 (GGuard (GPat p1 g1) (GExp x1)) cont
         = XCase a g1
                [ AAlt p1        [GExp x1]
                , AAlt PDefault  [GExp cont]]

        -- Cases that use a continuation function as a join point.
        -- We need this when desugaring general pattern alternatives,
        -- as each group of guards can be reached from multiple places.
        go1 (GGuard (GPred x1) gs) cont
         = XLet  a (LLet (BAnon (tBot kData)) (xBox a cont))
         $ XCase a (liftX 1 x1)
                [ AAlt pTrue     [GExp (go1 (liftX 1 gs) (xRun a (XVar a (UIx 0))))]
                , AAlt PDefault  [GExp                   (xRun a (XVar a (UIx 0))) ]]

        go1 (GGuard (GPat p1 x1) gs) cont
         = XLet a (LLet (BAnon (tBot kData)) (xBox a cont))
         $ XCase a (liftX 1 x1)
                [ AAlt p1        [GExp (go1 (liftX 1 gs) (xRun a (XVar a (UIx 0))))]
                , AAlt PDefault  [GExp                   (xRun a (XVar a (UIx 0))) ]]