-----------------------------------------------------------------------------
-- |
-- Module      :  Language.Haskell.HSX.Tranform
-- Copyright   :  (c) Niklas Broberg 2004-2012
-- License     :  BSD-style (see the file LICENSE.txt)
--
-- Maintainer  :  Niklas Broberg, niklas.broberg@gmail.com
-- Stability   :  experimental
-- Portability :  portable
--
-- Functions for transforming abstract Haskell code extended with regular
-- patterns into semantically equivalent normal abstract Haskell code. In
-- other words, we transform away regular patterns.
-----------------------------------------------------------------------------

{-# LANGUAGE CPP #-}

module Language.Haskell.HSX.Transform (
      transform       -- :: HsModule -> HsModule
    , transformExp
    ) where

import Language.Haskell.Exts.Syntax
import Language.Haskell.Exts.Build
import Control.Applicative (Applicative(pure, (<*>)))
import Control.Monad       (ap)
#if MIN_VERSION_base(4,9,0)
import qualified Control.Monad.Fail as Fail
#endif
import Data.List (union)

import Debug.Trace (trace)

-----------------------------------------------------------------------------
-- A monad for threading a boolean value through the boilerplate code,
-- to signal whether a transformation has taken place or not.

newtype HsxM a = MkHsxM (HsxState -> (a, HsxState))

instance Applicative HsxM where
  pure  = return
  (<*>) = ap

instance Monad HsxM where
 return x = MkHsxM (\s -> (x,s))
 (MkHsxM f) >>= k = MkHsxM (\s -> let (a, s') = f s
                                      (MkHsxM f') = k a
                                   in f' s')

#if MIN_VERSION_base(4,9,0)
-- this is probably wrong, but should never be called anyway.
instance Fail.MonadFail HsxM where
    fail str = error str
    {-# INLINE fail #-}
#endif

getHsxState :: HsxM HsxState
getHsxState = MkHsxM (\s -> (s, s))

setHsxState :: HsxState -> HsxM ()
setHsxState s = MkHsxM (\_ -> ((),s))

instance Functor HsxM where
 fmap f hma = do a <- hma
                 return $ f a

-----

type HsxState = (Bool, Bool)

initHsxState :: HsxState
initHsxState = (False, False)

setHarpTransformed :: HsxM ()
setHarpTransformed =
    do (_,x) <- getHsxState
       setHsxState (True,x)

setXmlTransformed :: HsxM ()
setXmlTransformed =
    do (h,_) <- getHsxState
       setHsxState (h,True)

runHsxM :: HsxM a -> (a, (Bool, Bool))
runHsxM (MkHsxM f) = f initHsxState

-----------------------------------------------------------------------------
-- Traversing and transforming the syntax tree


-- | Transform away occurences of regular patterns from an abstract
-- Haskell module, preserving semantics.
transform :: Module () -> Module ()
transform (Module l m pragmas is decls) =
    let (decls', (harp, hsx)) = runHsxM $ mapM transformDecl decls
        -- We may need to add an import for Match.hs that defines the matcher monad
        imps1 = if harp
             then (:) $ ImportDecl () match_mod True False False Nothing
                            (Just match_qual_mod)
                            Nothing
             else id
        imps2 = {- if hsx
                 then (:) $ ImportDecl s hsx_data_mod False
                         Nothing
                         Nothing
                 else -} id     -- we no longer want to import HSP.Data
     in Module l m pragmas (imps1 $ imps2 is) decls'

-----------------------------------------------------------------------------
-- Declarations

-- | Transform a declaration by transforming subterms that could
-- contain regular patterns.
transformDecl :: Decl () -> HsxM (Decl ())
transformDecl d = case d of
    -- Pattern binds can contain regular patterns in the pattern being bound
    -- as well as on the right-hand side and in declarations in a where clause
    PatBind l pat rhs decls -> do
        -- Preserve semantics of irrefutable regular patterns by postponing
        -- their evaluation to a let-expression on the right-hand side
        let ([pat'], rnpss) = unzip $ renameIrrPats [pat]
        -- Transform the pattern itself
        ([pat''], attrGuards, guards, decls'') <- transformPatterns [pat']
        -- Transform the right-hand side, and add any generated guards
        -- and let expressions to it
        rhs' <- mkRhs (attrGuards ++ guards) (concat rnpss) rhs
        -- Transform declarations in the where clause, adding any generated
        -- declarations to it
        decls' <- case decls of
               Nothing -> return Nothing
               Just (BDecls l ds)
                         -> do ds' <- transformLetDecls ds
                               return $ Just $ BDecls l $ decls'' ++ ds'
               _           -> error "Cannot bind implicit parameters in the \
                        \ \'where\' clause of a function using regular patterns."
        return $ PatBind l pat'' rhs' decls'

    -- Function binds can contain regular patterns in their matches
    FunBind l ms -> fmap (FunBind l) $ mapM transformMatch ms
    -- Instance declarations can contain regular patterns in the
    -- declarations of functions inside it
    InstDecl l mo irule Nothing -> pure d
    InstDecl l mo irule (Just idecls) ->
      fmap (InstDecl l mo irule . Just) $ mapM transformInstDecl idecls
    -- Class declarations can contain regular patterns in the
    -- declarations of automatically instantiated functions
    ClassDecl l c dh fd Nothing -> pure d
    ClassDecl l c dh fd (Just cdecls) ->
        fmap (ClassDecl l c dh fd . Just) $ mapM transformClassDecl cdecls
    -- TH splices are expressions and can contain regular patterns
    SpliceDecl l e ->
        fmap (SpliceDecl l) $ transformExpM e
    -- Type signatures, type, newtype or data declarations, infix declarations,
    -- type and data families and instances, foreign imports and exports,
    -- and default declarations; none can contain regular patterns.
    -- Note that we don't transform inside rules pragmas!
    _ -> return d

transformInstDecl :: InstDecl () -> HsxM (InstDecl ())
transformInstDecl d = case d of
    InsDecl l decl -> fmap (InsDecl l) $ transformDecl decl
    _ -> return d


transformClassDecl :: ClassDecl () -> HsxM (ClassDecl ())
transformClassDecl d = case d of
    ClsDecl l decl -> fmap (ClsDecl l) $ transformDecl decl
    _ -> return d

-- | Transform a function "match" by generating pattern guards and
-- declarations representing regular patterns in the argument list.
-- Subterms, such as guards and the right-hand side, are also traversed
-- transformed.
transformMatch :: Match () -> HsxM (Match ())
transformMatch (Match l name pats rhs decls) = do
    -- Preserve semantics of irrefutable regular patterns by postponing
    -- their evaluation to a let-expression on the right-hand side
    let (pats', rnpss) = unzip $ renameIrrPats pats
    -- Transform the patterns that stand as arguments to the function
    (pats'', attrGuards, guards, decls'') <- transformPatterns pats'
    -- Transform the right-hand side, and add any generated guards
    -- and let expressions to it
    rhs' <- mkRhs (attrGuards ++ guards) (concat rnpss) rhs
    -- Transform declarations in the where clause, adding any generated
    -- declarations to it
    decls' <- case decls of
           Nothing -> return Nothing
           Just (BDecls l ds)
                     -> do ds' <- transformLetDecls ds
                           return $ Just $ BDecls l $ decls'' ++ ds'
           _           -> error "Cannot bind implicit parameters in the \
                     \ \'where\' clause of a function using regular patterns."

    return $ Match l name pats'' rhs' decls'


-- | Transform and update guards and right-hand side of a function or
-- pattern binding. The supplied list of guards is prepended to the
-- original guards, and subterms are traversed and transformed.
mkRhs :: [Guard ()] -> [(Name (), Pat ())] -> Rhs () -> HsxM (Rhs ())
mkRhs guards rnps (UnGuardedRhs l rhs) = do
    -- Add the postponed patterns to the right-hand side by placing
    -- them in a let-expression to make them lazily evaluated.
    -- Then transform the whole right-hand side as an expression.
    rhs' <- transformExpM $ addLetDecls rnps rhs
    case guards of
     -- There were no guards before, and none should be added,
     -- so we still have an unguarded right-hand side
     [] -> return $ UnGuardedRhs l rhs'
     -- There are guards to add. These should be added as pattern
     -- guards, i.e. as statements.
     _  -> return $ GuardedRhss l [GuardedRhs l (map mkStmtGuard guards) rhs']
mkRhs guards rnps (GuardedRhss l gdrhss) = fmap (GuardedRhss l) $ mapM (mkGRhs guards rnps) gdrhss
  where mkGRhs :: [Guard ()] -> [(Name (), Pat ())] -> GuardedRhs () -> HsxM (GuardedRhs ())
        mkGRhs gs rnps (GuardedRhs l oldgs rhs) = do
            -- Add the postponed patterns to the right-hand side by placing
            -- them in a let-expression to make them lazily evaluated.
            -- Then transform the whole right-hand side as an expression.
            rhs' <- transformExpM $ addLetDecls rnps rhs
            -- Now there are guards, so first we need to transform those
            oldgs' <- fmap concat $ mapM (transformStmt GuardStmt) oldgs
            -- ... and then prepend the newly generated ones, as statements
            return $ GuardedRhs l ((map mkStmtGuard gs) ++ oldgs') rhs'

-- | Place declarations of postponed regular patterns in a let-expression to
-- make them lazy, in order to make them behave as irrefutable patterns.
addLetDecls :: [(Name (), Pat ())] -> Exp () -> Exp ()
addLetDecls []   e = e    -- no declarations to add
addLetDecls rnps e =
    -- Place all postponed patterns in the same let-expression
    letE (map mkDecl rnps) e

-- | Make pattern binds from postponed regular patterns
mkDecl :: (Name (), Pat ()) -> Decl ()
mkDecl (n,p) = patBind p (var n)

------------------------------------------------------------------------------------
-- Expressions

-- | Transform expressions by traversing subterms.
-- Of special interest are expressions that contain patterns as subterms,
-- i.e. @let@, @case@ and lambda expressions, and also list comprehensions
-- and @do@-expressions. All other expressions simply transform their
-- sub-expressions, if any.
-- Of special interest are of course also any xml expressions.
transformExp :: Exp () -> Exp ()
transformExp e =
    let (e', _) = runHsxM $ transformExpM e
    in e'

-- | Transform expressions by traversing subterms.
-- Of special interest are expressions that contain patterns as subterms,
-- i.e. @let@, @case@ and lambda expressions, and also list comprehensions
-- and @do@-expressions. All other expressions simply transform their
-- sub-expressions, if any.
-- Of special interest are of course also any xml expressions.
transformExpM :: Exp () -> HsxM (Exp ())
transformExpM e = case e of
    -- A standard xml tag should be transformed into an element of the
    -- XML datatype. Attributes should be made into a set of mappings,
    -- and children should be transformed.
    XTag _ name attrs mattr cs -> do
        -- Hey Pluto, look, we have XML in our syntax tree!
        setXmlTransformed
        let -- ... make tuples of the attributes
            as = map mkAttr attrs
        -- ... transform the children
        cs' <- mapM transformChild cs
        -- ... and lift the values into the XML datatype.
        return $ paren $ metaGenElement name as mattr cs'

    -- An empty xml tag should be transformed just as a standard tag,
    -- only that there are no children,
    XETag _ name attrs mattr -> do
        -- ... 'tis the season to be jolly, falalalalaaaa....
        setXmlTransformed
        let -- ... make tuples of the attributes
            as = map mkAttr attrs
            -- ... and lift the values into the XML datatype.
        return $ paren $ metaGenEElement name as mattr

    -- A child tag should be transformed into an application
    -- of asChild to a list of children.
    XChildTag _ cs  -> do
        -- After all, it IS christmas!
        setXmlTransformed
        -- ... transform the children
        cs' <- mapM transformChild cs
        -- ... and make them into a list
        return $ paren $ metaAsChild $ listE cs'

    -- PCDATA should be lifted as a string into the XML datatype.
    XPcdata _ pcdata    -> do setXmlTransformed
                              return $ metaFromStringLit $ strE pcdata
--                            return $ ExpTypeSig noLoc (strE pcdata) (TyCon (UnQual (Ident "Text")))
    -- Escaped expressions should be treated as just expressions.
    XExpTag _ e     -> do setXmlTransformed
                          e' <- transformExpM e
                          return $ paren $ metaAsChild e'

    -- Patterns as arguments to a lambda expression could be regular,
    -- but we cannot put the evaluation here since a lambda expression
    -- can have neither guards nor a where clause. Thus we must postpone
    -- them to a case expressions on the right-hand side.
    Lambda l pats rhs -> do
        let -- First rename regular patterns
            (ps, rnpss)  = unzip $ renameRPats pats
            -- ... group them up to one big tuple
            (rns, rps) = unzip (concat rnpss)
            alt1 = alt (pTuple rps) rhs
            texp = varTuple rns
            -- ... and put it all in a case expression, which
            -- can then be transformed in the normal way.
            e = if null rns then rhs else caseE texp [alt1]
        rhs' <- transformExpM e
        return $ Lambda l ps rhs'
    -- A let expression can contain regular patterns in the declarations,
    -- or in the expression that makes up the body of the let.
    Let _ (BDecls _ ds) e -> do
        -- Declarations appearing in a let expression must be transformed
        -- in a special way due to scoping, see later documentation.
        -- The body is transformed as a normal expression.
        ds' <- transformLetDecls ds
        e'  <- transformExpM e
        return $ letE ds' e'
    -- Bindings of implicit parameters can appear either in ordinary let
    -- expressions (GHC), in dlet expressions (Hugs) or in a with clause
    -- (both). Such bindings are transformed in a special way. The body
    -- is transformed as a normal expression in all cases.
    Let l (IPBinds l' is) e -> do
        is' <- mapM transformIPBind is
        e'  <- transformExpM e
        return $ Let l (IPBinds l' is') e'
    -- A case expression can contain regular patterns in the expression
    -- that is the subject of the casing, or in either of the alternatives.
    Case l e alts -> do
        e'    <- transformExpM e
        alts' <- mapM transformAlt alts
        return $ Case l e' alts'
    -- A do expression can contain regular patterns in its statements.
    Do l stmts -> do
        stmts' <- fmap concat $ mapM (transformStmt DoStmt) stmts
        return $ Do l stmts'
    MDo l stmts -> do
        stmts' <- fmap concat $ mapM (transformStmt DoStmt) stmts
        return $ MDo l stmts'
    -- A list comprehension can contain regular patterns in the result
    -- expression, or in any of its statements.
    ListComp l e stmts  -> do
        e'     <- transformExpM e
        stmts' <- fmap concat $ mapM transformQualStmt stmts
        return $ ListComp l e' stmts'
    ParComp l e stmtss  -> do
        e'      <- transformExpM e
        stmtss' <- fmap (map concat) $ mapM (mapM transformQualStmt) stmtss
        return $ ParComp l e' stmtss'
    Proc l pat rhs          -> do
        let -- First rename regular patterns
            ([p], [rnps])  = unzip $ renameRPats [pat]
            -- ... group them up to one big tuple
            (rns, rps) = unzip rnps
            alt1 = alt (pTuple rps) rhs
            texp = varTuple rns
            -- ... and put it all in a case expression, which
            -- can then be transformed in the normal way.
            e = if null rns then rhs else caseE texp [alt1]
        rhs' <- transformExpM e
        return $ Proc l p rhs'

    -- All other expressions simply transform their immediate subterms.
    InfixApp l e1 op e2 -> transform2exp e1 e2
                                (\e1 e2 -> InfixApp l e1 op e2)
    App l e1 e2         -> transform2exp e1 e2 (App l)
    NegApp l e          -> fmap (NegApp l) $ transformExpM e
    If l e1 e2 e3       -> transform3exp e1 e2 e3 (If l)
    Tuple l bx es       -> fmap (Tuple l bx) $ mapM transformExpM es
    List l es           -> fmap (List l) $ mapM transformExpM es
    Paren l e           -> fmap (Paren l) $ transformExpM e
    LeftSection l e op  -> do e' <- transformExpM e
                              return $ LeftSection l e' op
    RightSection l op e -> fmap (RightSection l op) $ transformExpM e
    RecConstr l n fus   -> fmap (RecConstr l n) $ mapM transformFieldUpdate fus
    RecUpdate l e fus   -> do e'   <- transformExpM e
                              fus' <- mapM transformFieldUpdate fus
                              return $ RecUpdate l e' fus'
    EnumFrom l e        -> fmap (EnumFrom l) $ transformExpM e
    EnumFromTo l e1 e2  -> transform2exp e1 e2 (EnumFromTo l)
    EnumFromThen l e1 e2      -> transform2exp e1 e2 (EnumFromThen l)
    EnumFromThenTo l e1 e2 e3 -> transform3exp e1 e2 e3 (EnumFromThenTo l)
    ExpTypeSig l e t  -> do e' <- transformExpM e
                            return $ ExpTypeSig l e' t
    SpliceExp l s       -> fmap (SpliceExp l) $ transformSplice s
    LeftArrApp l e1 e2        -> transform2exp e1 e2 (LeftArrApp l)
    RightArrApp l e1 e2       -> transform2exp e1 e2 (RightArrApp l)
    LeftArrHighApp l e1 e2    -> transform2exp e1 e2 (LeftArrHighApp l)
    RightArrHighApp l e1 e2   -> transform2exp e1 e2 (RightArrHighApp l)
    CorePragma l s e      -> fmap (CorePragma l s) $ transformExpM e
    SCCPragma  l s e      -> fmap (SCCPragma  l s) $ transformExpM e
    GenPragma  l s a b e  -> fmap (GenPragma  l s a b) $ transformExpM e
    _           -> return e     -- Warning - will not work inside TH brackets!
  where
    -- | Transform expressions appearing in child position of an xml tag.
    -- Expressions are first transformed, then wrapped in a call to
    -- @toXml@.
    transformChild :: Exp () -> HsxM (Exp ())
    transformChild e = do
        -- Transform the expression
        te <- transformExpM e
        -- ... and apply the overloaded toXMLs to it
        return $ metaAsChild te

transformFieldUpdate :: FieldUpdate () -> HsxM (FieldUpdate ())
transformFieldUpdate (FieldUpdate l n e) =
        fmap (FieldUpdate l n) $ transformExpM e
transformFieldUpdate fup = return fup

transformSplice :: Splice () -> HsxM (Splice ())
transformSplice s = case s of
    ParenSplice l e     -> fmap (ParenSplice l) $ transformExpM e
    _                   -> return s

transform2exp :: Exp () -> Exp () -> (Exp () -> Exp () -> a) -> HsxM a
transform2exp e1 e2 f = do e1' <- transformExpM e1
                           e2' <- transformExpM e2
                           return $ f e1' e2'

transform3exp :: Exp () -> Exp () -> Exp () -> (Exp () -> Exp () -> Exp () -> a) -> HsxM a
transform3exp e1 e2 e3 f = do e1' <- transformExpM e1
                              e2' <- transformExpM e2
                              e3' <- transformExpM e3
                              return $ f e1' e2' e3'

mkAttr :: XAttr () -> Exp ()
mkAttr (XAttr _ name e) =
    paren (metaMkName name `metaAssign` (textTypeSig e))
    where
      textTypeSig e@(Lit _ (String _ _ _)) = metaFromStringLit e
--      textTypeSig e@(Lit (String _)) = ExpTypeSig noLoc e (TyCon (UnQual (Ident "Text")))
      textTypeSig e                  = e

-- | Transform pattern bind declarations inside a @let@-expression by transforming
-- subterms that could appear as regular patterns, as well as transforming the bound
-- pattern itself. The reason we need to do this in a special way is scoping, i.e.
-- in the expression @let a | Just b <- match a = list in b@ the variable b will not
-- be in scope after the @in@. And besides, we would be on thin ice even if it was in
-- scope since we are referring to the pattern being bound in the guard that will
-- decide if the pattern will be bound... yikes, why does Haskell allow guards on
-- pattern binds to refer to the patterns being bound, could that ever lead to anything
-- but an infinite loop??
transformLetDecls :: [Decl ()] -> HsxM [Decl ()]
transformLetDecls ds = do
    -- We need to rename regular patterns in pattern bindings, since we need to
    -- separate the generated declaration sets. This since we need to add them not
    -- to the actual binding but rather to the declaration that will be the guard
    -- of the binding.
    let ds' = renameLetDecls ds
    transformLDs 0 0 ds'
  where transformLDs :: Int -> Int -> [Decl ()] -> HsxM [Decl ()]
        transformLDs k l ds = case ds of
            []     -> return []
            (d:ds) -> case d of
                PatBind l'' pat rhs decls -> do
                    -- We need to transform all pattern bindings in a set of
                    -- declarations in the same context w.r.t. generating fresh
                    -- variable names, since they will all be in scope at the same time.
                    ([pat'], ags, gs, ws, k', l') <- runTrFromTo k l (trPatterns [pat])
                    decls' <- case decls of
                        -- Any declarations already in place should be left where they
                        -- are since they probably refer to the generating right-hand
                        -- side of the pattern bind. If they don't, we're in trouble...
                        Nothing -> return Nothing
                        Just (BDecls l'' decls) -> fmap (Just . BDecls l'') $ transformLetDecls decls
                        -- If they are implicit parameter bindings we simply transform
                        -- them as such.
                        Just (IPBinds l'' decls) -> fmap (Just . IPBinds l'') $ mapM transformIPBind decls
                    -- The generated guard, if any, should be a declaration, and the
                    -- generated declarations should be associated with it.
                    let gs' = case gs of
                           []  -> []
                           [g] -> [mkDeclGuard g ws]
                           _   -> error "This should not happen since we have called renameLetDecls already!"
                        -- Generated attribute guards should also be added as declarations,
                        -- but with no where clauses.
                        ags' = map (flip mkDeclGuard $ []) ags
                    -- We must transform the right-hand side as well, but there are
                    -- no new guards, nor any postponed patterns, to supply at this time.
                    rhs' <- mkRhs [] [] rhs
                    -- ... and then we should recurse with the new gensym argument.
                    ds' <- transformLDs k' l' ds
                    -- The generated guards, which should be at most one, should be
                    -- added as declarations rather than as guards due to the
                    -- scoping issue described above.
                    return $ (PatBind l'' pat' rhs' decls') : ags' ++ gs' ++ ds'

                    -- We only need to treat pattern binds separately, other declarations
                    -- can be transformed normally.
                d -> do d'  <- transformDecl d
                        ds' <- transformLDs k l ds
                        return $ d':ds'



-- | Transform binding of implicit parameters by transforming the expression on the
-- right-hand side. The left-hand side can only be an implicit parameter, so no
-- regular patterns there...
transformIPBind :: IPBind () -> HsxM (IPBind ())
transformIPBind (IPBind l n e) =
    fmap (IPBind l n) $ transformExpM e

------------------------------------------------------------------------------------
-- Statements of various kinds

-- | A simple annotation datatype for statement contexts.
data StmtType = DoStmt | GuardStmt | ListCompStmt

-- | Transform statements by traversing and transforming subterms.
-- Since generator statements have slightly different semantics
-- depending on their context, statements are annotated with their
-- context to ensure that the semantics of the resulting statement
-- sequence is correct. The return type is a list since generated
-- guards will be added as statements on the same level as the
-- statement to be transformed.
transformStmt :: StmtType -> Stmt () -> HsxM [Stmt ()]
transformStmt t s = case s of
    -- Generators can have regular patterns in the result pattern on the
    -- left-hand side and in the generating expression.
    Generator s p e -> do
        let -- We need to treat generated guards differently depending
            -- on the context of the statement.
            guardFun = case t of
                DoStmt       -> monadify
                ListCompStmt -> monadify
                GuardStmt    -> mkStmtGuard
            -- Preserve semantics of irrefutable regular patterns by postponing
            -- their evaluation to a let-expression on the right-hand side
            ([p'], rnpss) = unzip $ renameIrrPats [p]
        -- Transform the pattern itself
        ([p''], ags, gs, ds) <- transformPatterns [p']
        -- Put the generated declarations in a let-statement
        let lt  = case ds of
               [] -> []
               _  -> [letStmt ds]
            -- Perform the designated trick on the generated guards.
            gs' = map guardFun (ags ++ gs)
        -- Add the postponed patterns to the right-hand side by placing
        -- them in a let-expression to make them lazily evaluated.
        -- Then transform the whole right-hand side as an expression.
        e' <- transformExpM $ addLetDecls (concat rnpss) e
        return $ Generator s p'' e':lt ++ gs'
      where monadify :: Guard () -> Stmt ()
            -- To monadify is to create a statement guard, only that the
            -- generation must take place in a monad, so we need to "return"
            -- the value gotten from the guard.
            monadify (p,e) = genStmt p (metaReturn $ paren e)
    -- Qualifiers are simply wrapped expressions and are treated as such.
    Qualifier l e -> fmap (\e -> [Qualifier l $ e]) $ transformExpM e
    -- Let statements suffer from the same problem as let expressions, so
    -- the declarations should be treated in the same special way.
    LetStmt _ (BDecls _ ds)  ->
        fmap (\ds -> [letStmt ds]) $ transformLetDecls ds
    -- If the bindings are of implicit parameters we simply transform them as such.
    LetStmt l (IPBinds l' is) ->
        fmap (\is -> [LetStmt l (IPBinds l' is)]) $ mapM transformIPBind is
    RecStmt l stmts   ->
        fmap (return . RecStmt l . concat) $ mapM (transformStmt t) stmts

transformQualStmt :: QualStmt () -> HsxM [QualStmt ()]
transformQualStmt qs = case qs of
    -- For qual statments in list comprehensions we just pass on the baton
    QualStmt     l s      -> fmap (map (QualStmt l)) $ transformStmt ListCompStmt s
    ThenTrans    l e      -> fmap (return . ThenTrans l) $ transformExpM e
    ThenBy       l e f    -> fmap return $ transform2exp e f (ThenBy l)
    GroupBy      l e      -> fmap (return . GroupBy l) $ transformExpM e
    GroupUsing   l f      -> fmap (return . GroupUsing l) $ transformExpM f
    GroupByUsing l e f    -> fmap return $ transform2exp e f (GroupByUsing l)

------------------------------------------------------------------------------------------
-- Case alternatives

-- | Transform alternatives in a @case@-expression. Patterns are
-- transformed, while other subterms are traversed further.
transformAlt :: Alt () -> HsxM (Alt ())
transformAlt (Alt l pat rhs decls) = do
    -- Preserve semantics of irrefutable regular patterns by postponing
    -- their evaluation to a let-expression on the right-hand side
    let ([pat'], rnpss) = unzip $ renameIrrPats [pat]
    -- Transform the pattern itself
    ([pat''], attrGuards, guards, decls'') <- transformPatterns [pat']
    -- Transform the right-hand side, and add any generated guards
    -- and let expressions to it.
    rhs' <- mkRhs (attrGuards ++ guards) (concat rnpss) rhs
    -- Transform declarations in the where clause, adding any generated
    -- declarations to it.
    decls' <- case decls of
           Nothing -> return Nothing
           Just (BDecls l' ds) -> do ds' <- mapM transformDecl ds
                                     return $ Just $ BDecls l' $ decls'' ++ ds
           _           -> error "Cannot bind implicit parameters in the \
                     \ \'where\' clause of a function using regular patterns."

    return $ Alt l pat'' rhs' decls'

----------------------------------------------------------------------------------
-- Guards

-- In some places, a guard will be a declaration instead of the
-- normal statement, so we represent it in a generic fashion.
type Guard l = (Pat l, Exp l)

mkStmtGuard :: Guard () -> Stmt ()
mkStmtGuard (p, e) = genStmt p e

mkDeclGuard :: Guard () -> [Decl ()] -> Decl ()
mkDeclGuard (p, e) ds = patBindWhere p e ds

----------------------------------------------------------------------------------
-- Rewriting expressions before transformation.
-- Done in a monad for gensym capability.

newtype RN a = RN (RNState -> (a, RNState))

type RNState = Int

initRNState = 0

instance Applicative RN where
  pure  = return
  (<*>) = ap

instance Monad RN where
 return a = RN $ \s -> (a,s)
 (RN f) >>= k = RN $ \s -> let (a,s') = f s
                               (RN g) = k a
                            in g s'

instance Functor RN where
 fmap f rna = do a <- rna
                 return $ f a


runRename :: RN a -> a
runRename (RN f) = let (a,_) = f initRNState
                    in a

getRNState :: RN RNState
getRNState = RN $ \s -> (s,s)

setRNState :: RNState -> RN ()
setRNState s = RN $ \_ -> ((), s)

genVarName :: RN (Name ())
genVarName = do
    k <- getRNState
    setRNState $ k+1
    return $ name $ "harp_rnvar" ++ show k


type NameBind l = (Name l, Pat l)

-- Some generic functions on monads for traversing subterms

rename1pat :: a -> (b -> c) -> (a -> RN (b, [d])) -> RN (c, [d])
rename1pat p f rn = do (q, ms) <- rn p
                       return (f q, ms)

rename2pat :: a -> a -> (b -> b -> c) -> (a -> RN (b, [d])) -> RN (c, [d])
rename2pat p1 p2 f rn = do (q1, ms1) <- rn p1
                           (q2, ms2) <- rn p2
                           return $ (f q1 q2, ms1 ++ ms2)

renameNpat :: [a] -> ([b] -> c) -> (a -> RN (b, [d])) -> RN (c, [d])
renameNpat ps f rn = do (qs, mss) <- fmap unzip $ mapM rn ps
                        return (f qs, concat mss)




-- | Generate variables as placeholders for any regular patterns, in order
-- to place their evaluation elsewhere. We must likewise move the evaluation
-- of Tags because attribute lookups are force evaluation.
renameRPats :: [Pat ()] -> [(Pat (), [NameBind ()])]
renameRPats ps = runRename $ mapM renameRP ps

renameRP :: Pat () -> RN (Pat (), [NameBind ()])
renameRP p = case p of
    -- We must rename regular patterns and Tag expressions
    PRPat _ _           -> rename p
    PXTag _ _ _ _ _   -> rename p
    PXETag _ _ _ _    -> rename p
    -- The rest of the rules simply try to rename regular patterns in
    -- their immediate subpatterns.
    PInfixApp l p1 n p2 -> rename2pat p1 p2
                                (\p1 p2 -> PInfixApp l p1 n p2)
                                renameRP
    PApp l n ps         -> renameNpat ps (PApp l n) renameRP
    PTuple l bx ps      -> renameNpat ps (PTuple l bx) renameRP
    PList l ps          -> renameNpat ps (PList l) renameRP
    PParen l p          -> rename1pat p (PParen l) renameRP
    PRec l n pfs        -> renameNpat pfs (PRec l n) renameRPf
    PAsPat l n p        -> rename1pat p (PAsPat l n) renameRP
    PIrrPat l p         -> rename1pat p (PIrrPat l) renameRP
    PXPatTag l p        -> rename1pat p (PXPatTag l) renameRP
    PatTypeSig l p t  -> rename1pat p (\p -> PatTypeSig l p t) renameRP
    _                   -> return (p, [])
  where renameRPf :: PatField () -> RN (PatField (), [NameBind ()])
        renameRPf (PFieldPat l n p) = rename1pat p (PFieldPat l n) renameRP
        renameRPf pf              = return (pf, [])

        renameAttr :: PXAttr () -> RN (PXAttr (), [NameBind ()])
        renameAttr (PXAttr l s p) = rename1pat p (PXAttr l s) renameRP

        rename :: Pat () -> RN (Pat (), [NameBind ()])
        rename p = do -- Generate a fresh variable
              n <- genVarName
              -- ... and return that, along with the association of
              -- the variable with the old pattern
              return (pvar n, [(n,p)])

-- | Rename declarations appearing in @let@s or @where@ clauses.
renameLetDecls :: [Decl ()] -> [Decl ()]
renameLetDecls ds =
    let -- Rename all regular patterns bound in pattern bindings.
        (ds', smss) = unzip $ runRename $ mapM renameLetDecl ds
        -- ... and then generate declarations for the associations
        gs = map (\(n,p) -> mkDecl (n,p)) (concat smss)
        -- ... which should be added to the original list of declarations.
     in ds' ++ gs

  where renameLetDecl :: Decl () -> RN (Decl (), [(Name (), Pat ())])
        renameLetDecl d = case d of
            -- We need only bother about pattern bindings.
            PatBind l pat rhs decls -> do
                -- Rename any regular patterns that appear in the
                -- pattern being bound.
                (p, ms) <- renameRP pat
                let sms = map (\(n,p) -> (n, p)) ms
                return $ (PatBind l p rhs decls, sms)
            _ -> return (d, [])

-- | Move irrefutable regular patterns into a @let@-expression instead,
-- to make sure that the semantics of @~@ are preserved.
renameIrrPats :: [Pat ()] -> [(Pat (), [NameBind ()])]
renameIrrPats ps = runRename (mapM renameIrrP ps)

renameIrrP :: Pat () -> RN (Pat (), [(Name (), Pat ())])
renameIrrP p = case p of
    -- We should rename any regular pattern appearing
    -- inside an irrefutable pattern.
    PIrrPat l p     -> do (q, ms) <- renameRP p
                          return $ (PIrrPat l q, ms)
    -- The rest of the rules simply try to rename regular patterns in
    -- irrefutable patterns in their immediate subpatterns.
    PInfixApp l p1 n p2 -> rename2pat p1 p2
                                (\p1 p2 -> PInfixApp l p1 n p2)
                                renameIrrP
    PApp l n ps         -> renameNpat ps (PApp l n) renameIrrP
    PTuple l bx ps      -> renameNpat ps (PTuple l bx) renameIrrP
    PList l ps          -> renameNpat ps (PList l) renameIrrP
    PParen l p          -> rename1pat p (PParen l) renameIrrP
    PRec l n pfs        -> renameNpat pfs (PRec l n) renameIrrPf
    PAsPat l n p        -> rename1pat p (PAsPat l n) renameIrrP
    PatTypeSig l p t  -> rename1pat p (\p -> PatTypeSig l p t) renameIrrP
    -- Hsx
    PXTag l n attrs mat ps -> do (attrs', nss) <- fmap unzip $ mapM renameIrrAttr attrs
                                 (mat', ns1) <- case mat of
                                                   Nothing -> return (Nothing, [])
                                                   Just at -> do (at', ns) <- renameIrrP at
                                                                 return (Just at', ns)
                                 (q, ns) <- renameNpat ps (PXTag l n attrs' mat') renameIrrP
                                 return (q, concat nss ++ ns1 ++ ns)
    PXETag l n attrs mat  -> do (as, nss) <- fmap unzip $ mapM renameIrrAttr attrs
                                (mat', ns1) <- case mat of
                                                  Nothing -> return (Nothing, [])
                                                  Just at -> do (at', ns) <- renameIrrP at
                                                                return (Just at', ns)
                                return $ (PXETag l n as mat', concat nss ++ ns1)
    PXPatTag l p            -> rename1pat p (PXPatTag l) renameIrrP
    -- End Hsx

    _                       -> return (p, [])

  where renameIrrPf :: PatField () -> RN (PatField (), [NameBind ()])
        renameIrrPf (PFieldPat l n p) = rename1pat p (PFieldPat l n) renameIrrP
        renameIrrPf pf = return (pf, [])

        renameIrrAttr :: PXAttr () -> RN (PXAttr (), [NameBind ()])
        renameIrrAttr (PXAttr l s p) = rename1pat p (PXAttr l s) renameIrrP

-----------------------------------------------------------------------------------
-- Transforming Patterns: the real stuff

-- | Transform several patterns in the same context, thereby
-- generating any code for matching regular patterns.
transformPatterns :: [Pat ()] -> HsxM ([Pat ()], [Guard ()], [Guard ()], [Decl ()])
transformPatterns ps = runTr (trPatterns ps)

---------------------------------------------------
-- The transformation monad

type State = (Int, Int, Int, [Guard ()], [Guard ()], [Decl ()])

newtype Tr a = Tr (State -> HsxM (a, State))

instance Applicative Tr where
  pure  = return
  (<*>) = ap

instance Monad Tr where
 return a = Tr $ \s -> return (a, s)
 (Tr f) >>= k = Tr $ \s ->
          do (a, s') <- f s
             let (Tr f') = k a
             f' s'

instance Functor Tr where
 fmap f tra = tra >>= (return . f)

liftTr :: HsxM a -> Tr a
liftTr hma = Tr $ \s -> do a <- hma
                           return (a, s)

initState = initStateFrom 0 0

initStateFrom k l = (0, k, l, [], [], [])

runTr :: Tr a -> HsxM (a, [Guard ()], [Guard ()], [Decl ()])
runTr (Tr f) = do (a, (_,_,_,gs1,gs2,ds)) <- f initState
                  return (a, reverse gs1, reverse gs2, reverse ds)


runTrFromTo :: Int -> Int -> Tr a -> HsxM (a, [Guard ()], [Guard ()], [Decl ()], Int, Int)
runTrFromTo k l (Tr f) = do (a, (_,k',l',gs1,gs2,ds)) <- f $ initStateFrom k l
                            return (a, reverse gs1, reverse gs2, reverse ds, k', l')


-- manipulating the state
getState :: Tr State
getState = Tr $ \s -> return (s,s)

setState :: State -> Tr ()
setState s = Tr $ \_ -> return ((),s)

updateState :: (State -> (a,State)) -> Tr a
updateState f = do s <- getState
                   let (a,s') = f s
                   setState s'
                   return a

-- specific state manipulating functions
pushGuard :: Pat () -> Exp () -> Tr ()
pushGuard p e = updateState $ \(n,m,a,gs1,gs2,ds) -> ((),(n,m,a,gs1,(p,e):gs2,ds))

pushDecl :: Decl () -> Tr ()
pushDecl d = updateState $ \(n,m,a,gs1,gs2,ds) -> ((),(n,m,a,gs1,gs2,d:ds))

pushAttrGuard :: Pat () -> Exp () -> Tr ()
pushAttrGuard p e = updateState $ \(n,m,a,gs1,gs2,ds) -> ((),(n,m,a,(p,e):gs1,gs2,ds))

genMatchName :: Tr (Name ())
genMatchName = do k <- updateState $ \(n,m,a,gs1,gs2,ds) -> (n,(n+1,m,a,gs1,gs2,ds))
                  return $ Ident () $ "harp_match" ++ show k

genPatName :: Tr (Name ())
genPatName = do k <- updateState $ \(n,m,a,gs1,gs2,ds) -> (m,(n,m+1,a,gs1,gs2,ds))
                return $ Ident () $ "harp_pat" ++ show k

genAttrName :: Tr (Name ())
genAttrName = do k <- updateState $ \(n,m,a,gs1,gs2,ds) -> (m,(n,m,a+1,gs1,gs2,ds))
                 return $ Ident ()  $ "hsx_attrs" ++ show k


setHarpTransformedT, setXmlTransformedT :: Tr ()
setHarpTransformedT = liftTr setHarpTransformed
setXmlTransformedT  = liftTr setXmlTransformed


-------------------------------------------------------------------
-- Some generic functions for computations in the Tr monad. Could
-- be made even more general, but there's really no point right now...

tr1pat :: a -> (b -> c) -> (a -> Tr b) -> Tr c
tr1pat p f tr = do q <- tr p
                   return $ f q

tr2pat :: a -> a -> (b -> b -> c) -> (a -> Tr b) -> Tr c
tr2pat p1 p2 f tr = do q1 <- tr p1
                       q2 <- tr p2
                       return $ f q1 q2

trNpat :: [a] -> ([b] -> c) -> (a -> Tr b) -> Tr c
trNpat ps f tr = do qs <- mapM tr ps
                    return $ f qs

-----------------------------------------------------------------------------
-- The *real* transformations
-- Transforming patterns

-- | Transform several patterns in the same context
trPatterns :: [Pat ()] -> Tr [Pat ()]
trPatterns = mapM trPattern

-- | Transform a pattern by traversing the syntax tree.
-- A regular pattern is translated, other patterns are
-- simply left as is.
trPattern :: Pat () -> Tr (Pat ())
trPattern p = case p of
    -- This is where the fun starts. =)
    -- Regular patterns must be transformed of course.
    PRPat _ rps -> do
        -- First we need a name for the placeholder pattern.
        n <- genPatName
        -- A top-level regular pattern is a sequence in linear
        -- context, so we can simply translate it as if it was one.
        (mname, vars, _) <- trRPat True (RPSeq () rps)
        -- Generate a top level declaration.
        topmname <- mkTopDecl mname vars
        -- Generate a pattern guard for this regular pattern,
        -- that will match the generated declaration to the
        -- value of the placeholder, and bind all variables.
        mkGuard vars topmname n
        -- And indeed, we have made a transformation!
        setHarpTransformedT
        -- Return the placeholder pattern.
        return $ pvar n
    -- Tag patterns should be transformed
    PXTag _ name attrs mattr cpats -> do
        -- We need a name for the attribute list, if there are lookups
        an <- case (mattr, attrs) of
                -- ... if there is one already, and there are no lookups
                -- we can just return that
                (Just ap, []) -> return $ ap
                      -- ... if there are none, we dont' care
                (_, []) -> return wildcard
                (_, _)  -> do -- ... but if there are, we want a name for that list
                              n <- genAttrName
                              -- ... we must turn attribute lookups into guards
                              mkAttrGuards n attrs mattr
                              -- ... and we return the pattern
                              return $ pvar n
        -- ... the pattern representing children should be transformed
        cpat' <- case cpats of
                  -- ... it's a regular pattern, so we can just go ahead and transform it
                  (p@(PXRPats _ _)):[] -> trPattern p
                  -- ... it's an ordinary list, so we first wrap it up as such
                  _                    -> trPattern (PList () cpats)
        -- ...  we have made a transformation and should report that
        setHarpTransformedT
        -- ... and we return a Tag pattern.
        let (dom, n) = xNameParts name
        return $ metaTag dom n an cpat'
    -- ... as should empty Tag patterns
    PXETag _ name attrs mattr -> do
        -- We need a name for the attribute list, if there are lookups
        an <- case (mattr, attrs) of
                -- ... if there is a pattern already, and there are no lookups
                -- we can just return that
                (Just ap, []) -> return $ ap
                      -- ... if there are none, we dont' care
                (_, []) -> return wildcard
                (_, _)  -> do -- ... but if there are, we want a name for that list
                              n <- genAttrName
                              -- ... we must turn attribute lookups into guards
                              mkAttrGuards n attrs mattr
                              -- ... and we return the pattern
                              return $ pvar n
        -- ...  we have made a transformation and should report that
        setHarpTransformedT
        -- ... and we return an ETag pattern.
        let (dom, n) = xNameParts name
        return $ metaTag dom n an peList
    -- PCDATA patterns are strings in the xml datatype.
    PXPcdata _ st -> setHarpTransformedT >> (return $ metaPcdata st)
    -- XML comments are likewise just treated as strings.
    PXPatTag _ p -> setHarpTransformedT >> trPattern p
    -- Regular expression patterns over children should be translated
    -- just like PRPat.

    PXRPats l rps -> trPattern $ PRPat l rps
    -- Transforming any other patterns simply means transforming
    -- their subparts.
    PViewPat l e p       -> do
        e' <- liftTr $ transformExpM e
        tr1pat p (PViewPat l e') trPattern
    PVar _ _             -> return p
    PLit _ _ _           -> return p
    PInfixApp l p1 op p2 -> tr2pat p1 p2 (\p1 p2 -> PInfixApp l p1 op p2) trPattern
    PApp l n ps          -> trNpat ps (PApp l n) trPattern
    PTuple l bx ps       -> trNpat ps (PTuple l bx) trPattern
    PList l ps           -> trNpat ps (PList l) trPattern
    PParen l p           -> tr1pat p (PParen l) trPattern
    PRec l n pfs         -> trNpat pfs (PRec l n) trPatternField
    PAsPat l n p         -> tr1pat p (PAsPat l n) trPattern
    PWildCard l          -> return p
    PIrrPat l p          -> tr1pat p (PIrrPat l) trPattern
    PatTypeSig l p t   -> tr1pat p (\p -> PatTypeSig l p t) trPattern
    PQuasiQuote _ _ _    -> return p
    PBangPat l p         -> tr1pat p (PBangPat l) trPattern
    PNPlusK _ _ _        -> return p

  where -- Transform a pattern field.
    trPatternField :: PatField () -> Tr (PatField ())
    trPatternField (PFieldPat l n p) =
        tr1pat p (PFieldPat l n) trPattern
    trPatternField p = return p

    -- | Generate a guard for looking up xml attributes.
    mkAttrGuards :: Name () -> [PXAttr ()] -> Maybe (Pat ()) -> Tr ()
    mkAttrGuards attrs [PXAttr _ n q] mattr = do
        -- Apply lookupAttr to the attribute name and
        -- attribute set
        let rhs = metaExtract n attrs
            -- ... catch the result
            pat = metaPJust q
            -- ... catch the remainder list
            rml = case mattr of
                   Nothing -> wildcard
                   Just ap -> ap
        -- ... and add the generated guard to the store.
        pushAttrGuard (pTuple [pat, rml]) rhs

    mkAttrGuards attrs ((PXAttr _ a q):xs) mattr = do
        -- Apply lookupAttr to the attribute name and
        -- attribute set
        let rhs = metaExtract a attrs
            -- ... catch the result
            pat = metaPJust q
        -- ... catch the remainder list
        newAttrs <- genAttrName
        -- ... and add the generated guard to the store.
        pushAttrGuard (pTuple [pat, pvar newAttrs]) rhs
        -- ... and finally recurse
        mkAttrGuards newAttrs xs mattr

    -- | Generate a declaration at top level that will finalise all
    -- variable continuations, and then return all bound variables.
    mkTopDecl :: Name () -> [Name ()] -> Tr (Name ())
    mkTopDecl mname vars =
        do -- Give the match function a name
           n <- genMatchName
           -- Create the declaration and add it to the store.
           pushDecl $ topDecl n mname vars
           -- Return the name of the match function so that the
           -- guard that will be generated can call it.
           return n

    topDecl :: Name () -> Name () -> [Name ()] -> Decl ()
    topDecl n mname vs =
        let pat  = pTuple [wildcard, pvarTuple vs]      -- (_, (foo, bar, ...))
            g    = var mname                            -- harp_matchX
            a    = genStmt pat g                        -- (_, (foo, ...)) <- harp_matchX
            vars = map (\v -> app (var v) eList) vs     -- (foo [], bar [], ...)
            b    = qualStmt $ metaReturn $ tuple vars   -- return (foo [], bar [], ...)
            e    = doE [a,b]                            -- do (...) <- harp_matchX
                                                        --    return (foo [], bar [], ...)
         in nameBind n e                                -- harp_matchY = do ....

    -- | Generate a pattern guard that will apply the @runMatch@
    -- function on the top-level match function and the input list,
    -- thereby binding all variables.
    mkGuard :: [Name ()] -> Name () -> Name () -> Tr ()
    mkGuard vars mname n = do
        let tvs = pvarTuple vars                        -- (foo, bar, ...)
            ge  = appFun runMatchFun [var mname, var n] -- runMatch harp_matchX harp_patY
        pushGuard (pApp just_name [tvs]) ge             -- Just (foo, bar, ...) , runMatch ...

--------------------------------------------------------------------------------
-- Transforming regular patterns

-- | A simple datatype to annotate return values from sub-patterns
data MType = S         -- Single element
           | L MType       -- List of ... , (/  /), *, +
           | E MType MType -- Either ... or ... , (  |  )
           | M MType       -- Maybe ... , ?


-- When transforming a regular sub-pattern, we need to know the
-- name of the function generated to match it, the names of all
-- variables it binds, and the type of its returned value.
type MFunMetaInfo l = (Name l, [Name l], MType)


-- | Transform away a regular pattern, generating code
-- to replace it.
trRPat :: Bool -> RPat () -> Tr (MFunMetaInfo ())
trRPat linear rp = case rp of
    -- For an ordinary Haskell pattern we need to generate a
    -- base match function for the pattern, and a declaration
    -- that lifts that function into the matcher monad.
    RPPat _ p -> mkBaseDecl linear p

      where
        -- | Generate declarations for matching ordinary Haskell patterns
        mkBaseDecl :: Bool -> Pat () -> Tr (MFunMetaInfo ())
        mkBaseDecl linear p = case p of
            -- We can simplify a lot if the pattern is a wildcard or a variable
            PWildCard _ -> mkWCMatch
            PVar _ v    -> mkVarMatch linear v
            -- ... and if it is an embedded pattern tag, we can just skip it
            PXPatTag _ q -> mkBaseDecl linear q

            -- ... otherwise we'll have to take the long way...
            p           -> do -- First do a case match on a single element
                              (name, vars, _) <- mkBasePat linear p
                              -- ... apply baseMatch to the case matcher to
                              -- lift it into the matcher monad.
                              newname <- mkBaseMatch name
                              -- ... and return the meta-info gathered.
                              return (newname, vars, S)

        -- | Generate a basic function that cases on a single element,
        -- returning Just (all bound variables) on a match, and
        -- Nothing on a mismatch.
        mkBasePat :: Bool -> Pat () -> Tr (MFunMetaInfo ())
        mkBasePat b p =
         do -- First we need a name...
           n <- genMatchName
           -- ... and then we need to know what variables that
           -- will be bound by this match.
           let vs = gatherPVars p
           -- ... and then we can create and store away a casing function.
           basePatDecl b n vs p >>= pushDecl
           return (n, vs, S)

        -- | Generate a basic casing function for a given pattern.
        basePatDecl :: Bool -> Name () -> [Name ()] -> Pat () -> Tr (Decl ())
        basePatDecl linear f vs p = do
         -- We can use the magic variable harp_a since nothing else needs to
         -- be in scope at this time (we could use just a, or foo, or whatever)
         let a = Ident () $ "harp_a"
         -- ... and we should case on that variable on the right-hand side.
         rhs <- baseCaseE linear p a vs    -- case harp_a of ...
         -- The result is a simple function with one paramenter and
         -- the right-hand side we just generated.
         return $ simpleFun f a rhs
           where baseCaseE :: Bool -> Pat () -> Name () -> [Name ()] -> Tr (Exp ())
                 baseCaseE b p a vs = do
                    -- First the alternative if we actually
                    -- match the given pattern
                    let alt1 = alt p                  -- foo -> Just (mf foo)
                                (app (con just_name) $
                                 tuple (map (retVar b) vs))
                        -- .. and finally an alternative for not matching the pattern.
                        alt2 = alt wildcard (con nothing_name)        -- _ -> Nothing
                        -- ... and that pattern could itself contain regular patterns
                        -- so we must transform away these.
                    alt1' <- liftTr $ transformAlt alt1
                    return $ caseE (var a) [alt1', alt2]
                 retVar :: Bool -> Name () -> Exp ()
                 retVar linear v
                    -- if bound in linear context, apply const
                    | linear    = metaConst (var v)
                    -- if bound in non-linear context, apply (:)
                    | otherwise = app consFun (var v)

    -- For guarded base patterns, we want to do the same as for unguarded base patterns,
    -- only with guards (doh).
    RPGuard _ p gs -> mkGuardDecl linear p gs

     where mkGuardDecl :: Bool -> Pat () -> [Stmt ()] -> Tr (MFunMetaInfo ())
           mkGuardDecl linear p gs = case p of
                -- If it is an embedded pattern tag, we want to skip it
                PXPatTag _ q -> mkGuardDecl linear q gs

                -- ... otherwise we'll want to make a base pattern
                p           -> do -- First do a case match on a single element
                      (name, vars, _) <- mkGuardPat linear p gs
                      -- ... apply baseMatch to the case matcher to
                      -- lift it into the matcher monad.
                      newname <- mkBaseMatch name
                      -- ... and return the meta-info gathered.
                      return (newname, vars, S)

           -- | Generate a basic function that cases on a single element,
           -- returning Just (all bound variables) on a match, and
           -- Nothing on a mismatch.
           mkGuardPat :: Bool -> Pat () -> [Stmt ()] -> Tr (MFunMetaInfo ())
           mkGuardPat b p gs =
                do -- First we need a name...
                   n <- genMatchName
                   -- ... and then we need to know what variables that
                   -- will be bound by this match.
                   let vs = gatherPVars p ++ concatMap gatherStmtVars gs
                   -- ... and then we can create and store away a casing function.
                   guardPatDecl b n vs p gs >>= pushDecl
                   return (n, vs, S)

           -- | Generate a basic casing function for a given pattern.
           guardPatDecl :: Bool -> Name () -> [Name ()] -> Pat () -> [Stmt ()] -> Tr (Decl ())
           guardPatDecl linear f vs p gs = do
                -- We can use the magic variable harp_a since nothing else needs to
                -- be in scope at this time (we could use just a, or foo, or whatever)
                let a = Ident () $ "harp_a"
                -- ... and we should case on that variable on the right-hand side.
                rhs <- guardedCaseE linear p gs a vs  -- case harp_a of ...
                -- The result is a simple function with one parameter and
                -- the right-hand side we just generated.
                return $ simpleFun f a rhs
              where guardedCaseE :: Bool -> Pat () -> [Stmt ()] -> Name () -> [Name ()] -> Tr (Exp ())
                    guardedCaseE b p gs a vs = do
                        -- First the alternative if we actually
                        -- match the given pattern
                        let alt1 = altGW p gs                 -- foo -> Just (mf foo)
                                    (app (con just_name) $
                                    tuple (map (retVar b) vs)) (binds [])
                            -- .. and finally an alternative for not matching the pattern.
                            alt2 = alt wildcard (con nothing_name)        -- _ -> Nothing
                            -- ... and that pattern could itself contain regular patterns
                            -- so we must transform away these.
                        alt1' <- liftTr $ transformAlt alt1
                        return $ caseE (var a) [alt1', alt2]
                    retVar :: Bool -> Name () -> Exp ()
                    retVar linear v
                        -- if bound in linear context, apply const
                        | linear    = metaConst (var v)
                        -- if bound in non-linear context, apply (:)
                        | otherwise = app consFun (var v)

    -- For a sequence of regular patterns, we should transform all
    -- sub-patterns and then generate a function for sequencing them.
    RPSeq _ rps -> do
        nvts <- mapM (trRPat linear) rps
        mkSeqDecl nvts

      where
        -- | Generate a match function for a sequence of regular patterns,
        -- flattening any special sub-patterns into normal elements of the list
        mkSeqDecl :: [MFunMetaInfo ()] -> Tr (MFunMetaInfo ())
        mkSeqDecl nvts = do
            -- First, as always, we need a name...
            name <- genMatchName
            let -- We need a generating statement for each sub-pattern.
                (gs, vals) = unzip $ mkGenExps 0 nvts     -- (harp_valX, (foo, ...)) <- harp_matchY
                -- Gather up all variables from all sub-patterns.
                vars    = concatMap (\(_,vars,_) -> vars) nvts
                -- ... flatten all values to simple lists, and concatenate
                -- the lists to a new return value
                fldecls = flattenVals vals                  -- harp_valXf = $flatten harp_valX
                                                            -- harp_ret = foldComp [harp_val1f, ...]
                -- ... return the value along with all variables
                ret     = qualStmt $ metaReturn $           -- return (harp_ret, (foo, .....))
                            tuple [var retname, varTuple vars]
                -- ... do all these steps in a do expression
                rhs     = doE $ gs ++                       -- do (harp_valX, (foo, ...)) <- harpMatchY
                            [letStmt fldecls, ret]          --    let harp_valXf = $flatten harp_valX
                                                            --    return (harp_ret, (foo, .....))
            -- ... bind it to its name, and add the declaration
            -- to the store.
            pushDecl $ nameBind name rhs                    -- harp_matchZ = do ....
            -- The return value of a sequence is always a list of elements.
            return (name, vars, L S)

        -- | Flatten values of all sub-patterns into normal elements of the list
        flattenVals :: [(Name (), MType)] -> [Decl ()]
        flattenVals nts =
            let -- Flatten the values of all sub-patterns to
                -- lists of elements
                (nns, ds) = unzip $ map flVal nts
                -- ... and concatenate their results.
                ret       = nameBind retname $ app
                              (paren $ app foldCompFun
                                (listE $ map var nns)) $ eList
             in ds ++ [ret]

        flVal :: (Name (), MType) -> (Name (), Decl ())
        flVal (name, mt) =
            let -- We reuse the old names, we just extend them a bit.
                newname = extendVar name "f"    -- harp_valXf
                -- Create the appropriate flattening function depending
                -- on the type of the value
                f       = flatten mt
                -- ... apply it to the value and bind it to its new name.
             in (newname, nameBind newname $  -- harp_valXf = $flatten harp_valX
                    app f (var name))

        -- | Generate a flattening function for a given type structure.
        flatten :: MType -> Exp ()
        flatten S = consFun                         -- (:)
        flatten (L mt) =
            let f = flatten mt
                r = paren $ metaMap [f]
             in paren $ foldCompFun `metaComp` r    -- (foldComp . (map $flatten))
        flatten (E mt1 mt2) =
            let f1 = flatten mt1
                f2 = flatten mt2
             in paren $ metaEither f1 f2            -- (either $flatten $flatten)
        flatten (M mt) =
            let f = flatten mt
             in paren $ metaMaybe idFun f           -- (maybe id $flatten)

    -- For accumulating as-patterns we should transform the subpattern, and then generate
    -- a declaration that supplies the value to be bound to the variable in question.
    -- The variable should be bound non-linearly.
    RPCAs _ v rp -> do
        -- Transform the subpattern
        nvt@(name, vs, mt) <- trRPat linear rp
        -- ... and create a declaration to bind its value.
        n <- mkCAsDecl nvt
        -- The type of the value is unchanged.
        return (n, (v:vs), mt)

      where
        -- | Generate a declaration for a \@: binding.
        mkCAsDecl :: MFunMetaInfo () -> Tr (Name ())
        mkCAsDecl = asDecl $ app consFun    -- should become lists when applied to []

    -- For ordinary as-patterns we should transform the subpattern, and then generate
    -- a declaration that supplies the value to be bound to the variable in question.
    -- The variable should be bound linearly.
    RPAs _ v rp
        | linear ->
             do -- Transform the subpattern
                nvt@(name, vs, mt) <- trRPat linear rp
                -- ... and create a declaration to bind its value
                n <- mkAsDecl nvt
                -- The type of the value is unchanged.
                return (n, (v:vs), mt)
        -- We may not use an @ bind in non-linear context
        | otherwise -> case v of
                Ident () n -> fail $ "Attempting to bind variable "++n++
                      " inside the context of a numerable regular pattern"
                _         -> fail $ "This should never ever ever happen... how the #% did you do it??!?"

      where
        -- | Generate a declaration for a \@ binding.
        mkAsDecl :: MFunMetaInfo () -> Tr (Name ())
        mkAsDecl = asDecl metaConst     -- should be constant when applied to []

    -- For regular patterns, parentheses have no real meaning
    -- so at this point we can just skip them.
    RPParen _ rp -> trRPat linear rp

    -- For (possibly non-greedy) optional regular patterns we need to
    -- transform the subpattern, and the generate a function that can
    -- choose to match or not to match, that is the question...
    RPOp _ rp (RPOpt _)->
        do -- Transform the subpattern
           nvt <- trRPat False rp
           -- ... and create a declaration that can optionally match it.
           mkOptDecl False nvt
    -- ... similarly for the non-greedy version.
    RPOp _ rp (RPOptG _) ->
        do -- Transform the subpattern
           nvt <- trRPat False rp
           -- ... and create a declaration that can optionally match it.
           mkOptDecl True nvt

    -- For union patterns, we should transform both subexpressions,
    -- and generate a function that chooses between them.
    RPEither _ rp1 rp2 ->
        do -- Transform the subpatterns
           nvt1 <- trRPat False rp1
           nvt2 <- trRPat False rp2
           -- ... and create a declaration that can choose between them.
           mkEitherDecl nvt1 nvt2
        -- Generate declarations for either patterns, i.e. ( | )
      where mkEitherDecl :: MFunMetaInfo () -> MFunMetaInfo () -> Tr (MFunMetaInfo ())
            mkEitherDecl nvt1@(_, vs1, t1) nvt2@(_, vs2, t2) = do
                -- Eine namen, bitte!
                n <- genMatchName
                let -- Generate generators for the subpatterns
                    (g1, v1) = mkGenExp nvt1
                    (g2, v2) = mkGenExp nvt2          -- (harp_valX, (foo, bar, ...)) <- harp_matchY
                    -- ... gather all variables from both sides
                    allvs = vs1 `union` vs2
                    -- ... some may be bound on both sides, so we
                    -- need to check which ones are bound on each,
                    -- supplying empty value for those that are not
                    vals1 = map (varOrId vs1) allvs
                    vals2 = map (varOrId vs2) allvs
                    -- ... apply either Left or Right to the returned value
                    ret1  = metaReturn $ tuple          -- return (Left harp_val1, (foo, id, ...))
                                [app (con left_name)
                                 (var v1), tuple vals1]
                    ret2  = metaReturn $ tuple          -- return (Right harp_val2, (id, bar, ...))
                                [app (con right_name)
                                 (var v2), tuple vals2]
                    -- ... and do all these things in do-expressions
                    exp1  = doE [g1, qualStmt ret1]
                    exp2  = doE [g2, qualStmt ret2]
                    -- ... and choose between them using the choice (+++) operator.
                    rhs   = (paren exp1) `metaChoice`       -- (do ...) +++
                            (paren exp2)            --  (do ...)
                -- Finally we create a declaration for this function and
                -- add it to the store.
                pushDecl $ nameBind n rhs         -- harp_matchZ = (do ...) ...
                -- The type of the returned value is Either the type of the first
                -- or the second subpattern.
                return (n, allvs, E t1 t2)

            varOrId :: [Name ()] -> Name () -> Exp ()
            varOrId vs v = if v `elem` vs   -- the variable is indeed bound in this branch
                            then var v      -- ... so it should be added to the result
                            else idFun      -- ... else it should be empty.

    -- For (possibly non-greedy) repeating regular patterns we need to transform the subpattern,
    -- and then generate a function to handle many matches of it.
    RPOp _ rp (RPStar _) ->
        do -- Transform the subpattern
           nvt <- trRPat False rp
           -- ... and create a declaration that can match it many times.
           mkStarDecl False nvt
    -- ... and similarly for the non-greedy version.

    RPOp _ rp (RPStarG _) ->
        do -- Transform the subpattern
           nvt <- trRPat False rp
           -- ... and create a declaration that can match it many times.
           mkStarDecl True nvt

    -- For (possibly non-greedy) non-empty repeating patterns we need to transform the subpattern,
    -- and then generate a function to handle one or more matches of it.
    RPOp _ rp (RPPlus _) ->
        do -- Transform the subpattern
           nvt <- trRPat False rp
           -- ... and create a declaration that can match it one or more times.
           mkPlusDecl False nvt

    -- ... and similarly for the non-greedy version.
    RPOp _ rp (RPPlusG _) ->
        do -- Transform the subpattern
           nvt <- trRPat False rp
           -- ... and create a declaration that can match it one or more times.
           mkPlusDecl True nvt

  where -- These are the functions that must be in scope for more than one case alternative above.

    -- | Generate a declaration for matching a variable.
    mkVarMatch :: Bool -> Name () -> Tr (MFunMetaInfo ())
    mkVarMatch linear v = do
            -- First we need a name for the new match function.
            n <- genMatchName
            -- Then we need a basic matching function that always matches,
            -- and that binds the value matched to the variable in question.
            let e = paren $ lamE [pvar v] $       -- (\v -> Just (mf v))
                              app (con just_name)
                              (paren $ retVar linear v)
            -- Lift the function into the matcher monad, and bind it to its name,
            -- then add it the declaration to the store.
            pushDecl $ nameBind n $
                          app baseMatchFun e    -- harp_matchX = baseMatch (\v -> Just (mf v))
            return (n, [v], S)          -- always binds v and only v

          where retVar :: Bool -> Name () -> Exp ()
                retVar linear v
                    -- if bound in linear context, apply const
                    | linear    = metaConst (var v)
                    -- if bound in non-linear context, apply (:)
                    | otherwise = app consFun (var v)

    -- | Generate a declaration for matching a wildcard
    mkWCMatch :: Tr (MFunMetaInfo ())
    mkWCMatch = do
            -- First we need a name...
            n <- genMatchName
            -- ... and then a function that always matches, discarding the result
            let e = paren $ lamE [wildcard] $     -- (\_ -> Just ())
                                app (con just_name) (unit_con ())
            -- ... which we lift, bind, and add to the store.
            pushDecl $ nameBind n $       -- harp_matchX = baseMatch (\_ -> Just ())
                         app baseMatchFun e
            return (n, [], S)   -- no variables bound, hence []

    -- | Gather up the names of all variables in a pattern,
    -- using a simple fold over the syntax structure.
    gatherPVars :: Pat () -> [Name ()]
    gatherPVars p = case p of
            PVar _ v             -> [v]
            PInfixApp _ p1 _ p2  -> gatherPVars p1 ++
                                         gatherPVars p2
            PApp _ _ ps          -> concatMap gatherPVars ps
            PTuple _ _ ps        -> concatMap gatherPVars ps
            PList _ ps           -> concatMap gatherPVars ps
            PParen _ p           -> gatherPVars p
            PRec _ _ pfs         -> concatMap help pfs
                where help (PFieldPat _ _ p) = gatherPVars p
                      help _                 = []
            PAsPat _ n p         -> n : gatherPVars p
            PWildCard _          -> []
            PIrrPat _ p          -> gatherPVars p
            PatTypeSig _ p _     -> gatherPVars p
            PRPat _ rps          -> concatMap gatherRPVars rps
            PXTag _ _ attrs mattr cps ->
                concatMap gatherAttrVars attrs ++ concatMap gatherPVars cps ++
                    case mattr of
                     Nothing -> []
                     Just ap -> gatherPVars ap
            PXETag _ _ attrs mattr ->
                concatMap gatherAttrVars attrs ++
                    case mattr of
                     Nothing -> []
                     Just ap -> gatherPVars ap
            PXPatTag _ p         -> gatherPVars p
            _                    -> []

    gatherRPVars :: RPat () -> [Name ()]
    gatherRPVars rp = case rp of
            RPOp _ rq _        -> gatherRPVars rq
            RPEither _ rq1 rq2 -> gatherRPVars rq1 ++ gatherRPVars rq2
            RPSeq _ rqs        -> concatMap gatherRPVars rqs
            RPCAs _ n rq       -> n : gatherRPVars rq
            RPAs _ n rq        -> n : gatherRPVars rq
            RPParen _ rq       -> gatherRPVars rq
            RPGuard _ q gs     -> gatherPVars q ++ concatMap gatherStmtVars gs
            RPPat _ q          -> gatherPVars q

    gatherAttrVars :: PXAttr () -> [Name ()]
    gatherAttrVars (PXAttr _ _ p) = gatherPVars p

    gatherStmtVars :: Stmt () -> [Name ()]
    gatherStmtVars gs = case gs of
            Generator _ p _ -> gatherPVars p
            _               -> []

    -- | Generate a match function that lift the result of the
    -- basic casing function into the matcher monad.
    mkBaseMatch :: Name () -> Tr (Name ())
    mkBaseMatch name =
            do -- First we need a name...
               n <- genMatchName
               -- ... to which we bind the lifting function
               pushDecl $ baseMatchDecl n name
               -- and then return for others to use.
               return n

    -- | Generate a declaration for the function that lifts a simple
    -- casing function into the matcher monad.
    baseMatchDecl :: Name () -> Name () -> Decl ()
    baseMatchDecl newname oldname =
            -- Apply the lifting function "baseMatch" to the casing function
            let e = app baseMatchFun (var oldname)
                -- ... and bind it to the new name.
             in nameBind newname e        -- harp_matchX = baseMatch harp_matchY

    -- | Generate the generators that call sub-matching functions, and
    -- annotate names with types for future flattening of values.
    -- Iterate to enable gensym-like behavior.
    mkGenExps :: Int -> [MFunMetaInfo ()] -> [(Stmt (), (Name (), MType))]
    mkGenExps _ [] = []
    mkGenExps k ((name, vars, t):nvs) =
        let valname = mkValName k                           -- harp_valX
            pat     = pTuple [pvar valname, pvarTuple vars] -- (harp_valX, (foo, bar, ...))
            g       = var name
         in (genStmt pat g, (valname, t)) :               -- (harp_valX, (foo, ...)) <- harp_matchY
                mkGenExps (k+1) nvs

    -- | Create a single generator.
    mkGenExp :: MFunMetaInfo () -> (Stmt (), Name ())
    mkGenExp nvt = let [(g, (name, _t))] = mkGenExps 0 [nvt]
                   in (g, name)

    -- | Generate a single generator with a call to (ng)manyMatch,
    -- and an extra variable name to use after unzipping.
    mkManyGen :: Bool -> Name () -> Stmt ()
    mkManyGen greedy mname =
        -- Choose which repeater function to use, determined by greed
        let mf  = if greedy then gManyMatchFun else manyMatchFun
         -- ... and create a generator that applies it to the
         -- matching function in question.
         in genStmt (pvar valsvarsname) $
            app mf (var mname)

    -- | Generate declarations for @: and @ bindings.
    asDecl :: (Exp () -> Exp ()) -> MFunMetaInfo () -> Tr (Name ())
    asDecl mf nvt@(_, vs, _) = do
        -- A name, if you would
        n <- genMatchName                                -- harp_matchX
        let -- Generate a generator for matching the subpattern
            (g, val) = mkGenExp nvt                      -- (harp_valY, (foo, ...)) <- harp_matchZ
            -- ... fix the old variables
            vars     = map var vs                        -- (apa, bepa, ...)
            -- ... and return the generated value, along with the
            -- new set of variables which is the old set prepended
            -- by the variable currently being bound.
            ret = qualStmt $ metaReturn $ tuple          -- return (harp_valY, ($mf harp_valY, apa, ...))
                [var val, tuple $ mf (var val) : vars]   -- mf in the line above is what separates
                                                         -- @: ((:)) from @ (const)
        -- Finally we create a declaration for this function and
        -- add it to the store.
        pushDecl $ nameBind n $ doE [g, ret]             -- harp_matchX = do ...
        return n

    -- | Generate declarations for optional patterns, ? and #?.
    -- (Unfortunally we must place this function here since both variations
    -- of transformations of optional patterns should be able to call it...)
    mkOptDecl :: Bool -> MFunMetaInfo () -> Tr (MFunMetaInfo ())
    mkOptDecl greedy nvt@(_, vs, t) = do
        -- Un nome, s'il vouz plaît.
        n <- genMatchName
        let -- Generate a generator for matching the subpattern
            (g, val) = mkGenExp nvt                 -- (harp_valX, (foo, bar, ...)) <- harp_matchY
            -- ... and apply a Just to its value
            ret1 = metaReturn $ tuple               -- return (Just harp_val1, (foo, bar, ...))
                    [app (con just_name)
                     (var val), varTuple vs]
            -- ... and do those two steps in a do-expression
            exp1 = doE [g, qualStmt ret1]           -- do ....
            -- For the non-matching branch, all the variables should be empty
            ids  = map (const idFun) vs             -- (id, id, ...)
            -- ... and the value should be Nothing.
            ret2 = metaReturn $ tuple               -- return (Nothing, (id, id, ...))
                    [con nothing_name, tuple ids]   -- i.e. no vars were bound
            -- The order of the arguments to the choice (+++) operator
            -- is determined by greed...
            mc   = if greedy
                    then metaChoice        -- standard order
                    else (flip metaChoice) -- reversed order
            -- ... and then apply it to the branches.
            rhs  = (paren exp1) `mc`                -- (do ....) +++
                    (paren ret2)                    --  (return (Nothing, .....))
        -- Finally we create a declaration for this function and
        -- add it to the store.
        pushDecl $ nameBind n rhs                   -- harp_matchZ = (do ....) +++ (return ....)
        -- The type of the returned value will be Maybe the type
        -- of the value of the subpattern.
        return (n, vs, M t)

    -- | Generate declarations for star patterns, * and #*
    -- (Unfortunally we must place this function here since both variations
    -- of transformations of repeating patterns should be able to call it...)
    mkStarDecl :: Bool -> MFunMetaInfo () -> Tr (MFunMetaInfo ())
    mkStarDecl greedy (mname, vs, t) = do
        -- Ett namn, tack!
        n <- genMatchName
        let -- Create a generator that matches the subpattern
            -- many times, either greedily or non-greedily
            g = mkManyGen greedy mname
            -- ... and unzip the result, choosing the proper unzip
            -- function depending on the number of variables returned.
            metaUnzipK = mkMetaUnzip (length vs)
            -- ... first unzip values from variables
            dec1    = patBind (pvarTuple [valname, varsname])
                    (metaUnzip $ var valsvarsname)
            -- ... and then unzip the variables
            dec2    = patBind (pvarTuple vs)
                    (metaUnzipK $ var varsname)
            -- ... fold all the values for variables
            retExps = map ((app foldCompFun) . var) vs
            -- ... and return value and variables
            ret     = metaReturn $ tuple $
                    [var valname, tuple retExps]
        -- Finally we need to generate a function that does all this,
        -- using a let-statement for the non-monadic stuff and a
        -- do-expression to wrap it all in.
        pushDecl $ nameBind n $
            doE [g, letStmt [dec1, dec2], qualStmt ret]
        -- The type of the returned value is a list ([]) of the
        -- type of the subpattern.
        return (n, vs, L t)

    -- | Generate declarations for plus patterns, + and #+
    -- (Unfortunally we must place this function here since both variations
    -- of transformations of non-empty repeating patterns should be able to call it...)
    mkPlusDecl :: Bool -> MFunMetaInfo () -> Tr (MFunMetaInfo ())
    mkPlusDecl greedy nvt@(mname, vs, t) = do
        -- and now I've run out of languages...
        n <- genMatchName
        let k = length vs
            -- First we want a generator to match the
            -- subpattern exactly one time
            (g1, val1) = mkGenExp nvt                       -- (harp_valX, (foo, ...)) <- harpMatchY
            -- ... and then one that matches it many times.
            g2         = mkManyGen greedy mname             -- harp_vvs <- manyMatch harpMatchY
            -- ... we want to unzip the result, using
            -- the proper unzip function
            metaUnzipK = mkMetaUnzip k
            -- ... first unzip values from variables
            dec1    = patBind                               -- (harp_vals, harp_vars) = unzip harp_vvs
                        (pvarTuple [valsname, varsname])
                        (metaUnzip $ var valsvarsname)
            -- .. now we need new fresh names for variables
            -- since the ordinary ones are already taken.
            vlvars  = genNames "harp_vl" k
            -- ... and then we can unzip the variables
            dec2    = patBind (pvarTuple vlvars)            -- (harp_vl1, ...) = unzipK harp_vars
                        (metaUnzipK $ var varsname)
            -- .. and do the unzipping in a let-statement
            letSt   = letStmt [dec1, dec2]
            -- ... fold variables from the many-match,
            -- prepending the variables from the single match
            retExps = map mkRetFormat $ zip vs vlvars       -- foo . (foldComp harp_vl1), ...
            -- ... prepend values from the single match to
            -- those of the many-match.
            retVal  = (var val1) `metaCons`
                        (var valsname)                      -- harp_valX : harp_vals
            -- ... return all values and variables
            ret     = metaReturn $ tuple $                  -- return (harp_valX:harpVals,
                        [retVal, tuple retExps]             --   (foo . (...), ...))
            -- ... and wrap all of it in a do-expression.
            rhs     = doE [g1, g2, letSt, qualStmt ret]
        -- Finally we create a declaration for this function and
        -- add it to the store.
        pushDecl $ nameBind n rhs
        -- The type of the returned value is a list ([]) of the
        -- type of the subpattern.
        return (n, vs, L t)

      where mkRetFormat :: (Name (), Name ()) -> Exp ()
            mkRetFormat (v, vl) =
                -- Prepend variables using function composition.
                (var v) `metaComp`
                  (paren $ (app foldCompFun) $ var vl)

--------------------------------------------------------------------------
-- HaRP-specific functions and ids

-- | Functions and ids from the @Match@ module,
-- used in the generated matching functions
runMatchFun, baseMatchFun, manyMatchFun, gManyMatchFun :: Exp ()
runMatchFun = match_qual runMatch_name
baseMatchFun = match_qual baseMatch_name
manyMatchFun = match_qual manyMatch_name
gManyMatchFun = match_qual gManyMatch_name

runMatch_name, baseMatch_name, manyMatch_name, gManyMatch_name :: Name ()
runMatch_name   = Ident () "runMatch"
baseMatch_name  = Ident () "baseMatch"
manyMatch_name  = Ident () "manyMatch"
gManyMatch_name = Ident () "gManyMatch"

match_mod, match_qual_mod :: ModuleName ()
match_mod      = ModuleName () "Harp.Match"
match_qual_mod = ModuleName () "HaRPMatch"

match_qual :: Name () -> Exp ()
match_qual = qvar match_qual_mod

choiceOp :: QOp ()
choiceOp = QVarOp () $ Qual () match_qual_mod choice

appendOp :: QOp ()
appendOp = QVarOp () $ UnQual () append

-- foldComp = foldl (.) id, i.e. fold by composing
foldCompFun :: Exp ()
foldCompFun = match_qual $ Ident () "foldComp"

mkMetaUnzip :: Int -> Exp () -> Exp ()
mkMetaUnzip k | k <= 7 = let n = "unzip" ++ show k
                          in (\e -> matchFunction n [e])
              | otherwise =
                   let vs      = genNames "x" k
                       lvs     = genNames "xs" k
                       uz      = name $ "unzip" ++ show k
                       ys      = name "ys"
                       xs      = name "xs"
                       alt1    = alt peList $ tuple $ replicate k eList   -- [] -> ([], [], ...)
                       pat2    = (pvarTuple vs) `metaPCons` (pvar xs)       -- (x1, x2, ...)
                       ret2    = tuple $ map appCons $ zip vs lvs           -- (x1:xs1, x2:xs2, ...)
                       rhs2    = app (var uz) (var xs)                      -- unzipK xs
                       dec2    = patBind (pvarTuple lvs) rhs2             -- (xs1, xs2, ...) = unzipK xs
                       exp2    = letE [dec2] ret2
                       alt2    = alt pat2 exp2
                       topexp  = lamE [pvar ys] $ caseE (var ys) [alt1, alt2]
                       topbind = nameBind uz topexp
                    in app (paren $ letE [topbind] (var uz))
  where appCons :: (Name (), Name ()) -> Exp ()
        appCons (x, xs) = metaCons (var x) (var xs)

matchFunction :: String -> [Exp ()] -> Exp ()
matchFunction s es = mf s (reverse es)
  where mf s []     = match_qual $ Ident () s
        mf s (e:es) = app (mf s es) e

-- | Some 'magic' gensym-like functions, and functions
-- with related functionality.
retname :: Name ()
retname = name "harp_ret"

varsname :: Name ()
varsname = name "harp_vars"

valname :: Name ()
valname = name "harp_val"

valsname :: Name ()
valsname = name "harp_vals"

valsvarsname :: Name ()
valsvarsname = name "harp_vvs"

mkValName :: Int -> Name ()
mkValName k = name $ "harp_val" ++ show k

extendVar :: Name () -> String -> Name ()
extendVar (Ident l n) s = Ident l $ n ++ s
extendVar n _ = n

xNameParts :: XName () -> (Maybe String, String)
xNameParts n = case n of
                XName _ s      -> (Nothing, s)
                XDomName _ d s -> (Just d, s)

---------------------------------------------------------
-- meta-level functions, i.e. functions that represent functions,
-- and that take arguments representing arguments... whew!

metaReturn, metaConst, metaUnzip :: Exp () -> Exp ()
metaReturn e = metaFunction "return" [e]
metaConst e  = metaFunction "const" [e]
metaUnzip e  = metaFunction "unzip" [e]

metaEither, metaMaybe :: Exp () -> Exp () -> Exp ()
metaEither e1 e2 = metaFunction "either" [e1,e2]
metaMaybe e1 e2 = metaFunction "maybe" [e1,e2]

metaConcat, metaMap :: [Exp ()] -> Exp ()
metaConcat es = metaFunction "concat" [listE es]
metaMap       = metaFunction "map"

metaAppend :: Exp () -> Exp () -> Exp ()
metaAppend l1 l2 = infixApp l1 appendOp l2

-- the +++ choice operator
metaChoice :: Exp () -> Exp () -> Exp ()
metaChoice e1 e2 = infixApp e1 choiceOp e2

metaPCons :: Pat () -> Pat () -> Pat ()
metaPCons p1 p2 = PInfixApp () p1 cons p2

metaCons, metaComp :: Exp () -> Exp () -> Exp ()
metaCons e1 e2 = infixApp e1 (QConOp () cons) e2
metaComp e1 e2 = infixApp e1 (op fcomp) e2

metaPJust :: Pat () -> Pat ()
metaPJust p = pApp just_name [p]

metaPNothing :: Pat ()
metaPNothing = pvar nothing_name

metaPMkMaybe :: Maybe (Pat ()) -> Pat ()
metaPMkMaybe mp = case mp of
    Nothing -> metaPNothing
    Just p  -> pParen $ metaPJust p

metaJust :: Exp () -> Exp ()
metaJust e = app (con just_name) e

metaNothing :: Exp ()
metaNothing = con nothing_name

metaMkMaybe :: Maybe (Exp ()) -> Exp ()
metaMkMaybe me = case me of
    Nothing -> metaNothing
    Just e  -> paren $ metaJust e

---------------------------------------------------
-- some other useful functions at abstract level
consFun, idFun :: Exp ()
consFun = Con () cons
idFun = function "id"

con :: Name () -> Exp ()
con = Con () . UnQual ()

cons :: QName ()
cons = Special () (Cons ())

fcomp, choice, append :: Name ()
fcomp = Symbol () "."
choice = Symbol () "+++"
append = Symbol () "++"

just_name, nothing_name, left_name, right_name :: Name ()
just_name    = Ident () "Just"
nothing_name = Ident () "Nothing"
left_name    = Ident () "Left"
right_name   = Ident () "Right"

------------------------------------------------------------------------
-- Help functions for meta programming xml

{- No longer used.
hsx_data_mod :: ModuleName
hsx_data_mod = ModuleName "HSP.Data"

-- Also no longer used, literal PCDATA should be considered a string.
-- | Create an xml PCDATA value
metaMkPcdata :: String -> Exp
metaMkPcdata s = metaFunction "pcdata" [strE s]
-}

-- | Create an xml tag, given its domain, name, attributes and
-- children.
metaGenElement :: XName () -> [Exp ()] -> Maybe (Exp ()) -> [Exp ()] -> Exp ()
metaGenElement name ats mat cs =
    let (d,n) = xNameParts name
        ne    = tuple [metaMkMaybe $ fmap (metaFromStringLit . strE) d, metaFromStringLit $ strE n]
        m = maybe id (\x y -> paren $ y `metaAppend` (metaMap [argAsAttr, x])) mat
        attrs = m $ listE $ map metaAsAttr ats
     in metaFunction "genElement" [ne, attrs, listE cs]

-- | Create an empty xml tag, given its domain, name and attributes.
metaGenEElement :: XName () -> [Exp ()] -> Maybe (Exp ()) -> Exp ()
metaGenEElement name ats mat =
    let (d,n) = xNameParts name
        ne    = tuple [metaMkMaybe $ fmap (metaFromStringLit . strE) d, metaFromStringLit $ strE n]
        m = maybe id (\x y -> paren $ y `metaAppend` (metaMap [argAsAttr, x])) mat
        attrs = m $ listE $ map metaAsAttr ats
     in metaFunction "genEElement" [ne, attrs]

-- | Create an attribute by applying the overloaded @asAttr@
metaAsAttr :: Exp () -> Exp ()
metaAsAttr e@(Lit _ (String _ _ _)) = metaFunction "asAttr" [metaFromStringLit e] -- [ExpTypeSig noLoc e (TyCon (UnQual (Ident "Text")))]
metaAsAttr e = metaFunction "asAttr" [e]

argAsAttr :: Exp ()
argAsAttr = var $ name "asAttr"

-- | Create a property from an attribute and a value.
metaAssign :: Exp () -> Exp () -> Exp ()
metaAssign e1 e2 = infixApp e1 assignOp e2
  where assignOp = QConOp () $ UnQual () $ Symbol () ":="

-- | Make xml out of some expression by applying the overloaded function
-- @asChild@.
metaAsChild :: Exp () -> Exp ()
metaAsChild e = metaFunction "asChild" [paren e]

-- | convert a 'String' literal to lazy 'Text' by calling a function named 'fromStringLit'
metaFromStringLit :: Exp () -> Exp ()
metaFromStringLit e = metaFunction "fromStringLit" [e]

-- TODO: We need to fix the stuff below so pattern matching on XML could also be overloaded.
-- Right now it only works on HSP XML, or anything that is syntactically identical to it.

-- | Lookup an attribute in the set of attributes.
metaExtract :: XName () -> Name () -> Exp ()
metaExtract name attrs =
    let (d,n) = xNameParts name
        np    = tuple [metaMkMaybe $ fmap strE d, strE n]
     in metaFunction "extract" [np, var attrs]

-- | Generate a pattern under the Tag data constructor.
metaTag :: (Maybe String) -> String -> Pat () -> Pat () -> Pat ()
metaTag dom name ats cpat =
    let d = metaPMkMaybe $ fmap strP dom
        n = pTuple [d, strP name]
     in metaConPat "Element" [n, ats, cpat]

-- | Generate a pattern under the PCDATA data constructor.
metaPcdata :: String -> Pat ()
metaPcdata s = metaConPat "CDATA" [strP s]

metaMkName :: XName () -> Exp ()
metaMkName n = case n of
    XName _ s      -> metaFromStringLit (strE s)
    XDomName _ d s -> tuple [metaFromStringLit $ strE d, metaFromStringLit $ strE s]

--    XName s      -> textTypeSig (strE s)
--    XDomName d s -> tuple [textTypeSig $ strE d, textTypeSig $ strE s]
--    where
--      textTypeSig e = ExpTypeSig noLoc e (TyCon (UnQual (Ident "Text")))


