-----------------------------------------------------------------------------
-- |
-- Module      :  Language.Haskell.Exts.Build
-- Copyright   :  (c) The GHC Team, 1997-2000,
--                (c) Niklas Broberg 2004
-- License     :  BSD-style (see the file LICENSE.txt)
--
-- Maintainer  :  Niklas Broberg, d00nibro@chalmers.se
-- Stability   :  experimental
-- Portability :  portable
--
-- This module contains combinators to use when building
-- Haskell source trees programmatically, as opposed to
-- parsing them from a string. The contents here are quite
-- experimental and will likely receive a lot of attention
-- when the rest has stabilised.
--
-----------------------------------------------------------------------------

module Language.Haskell.Exts.Build (

    -- * Syntax building functions
    name,       -- :: String -> Name ()
    sym,        -- :: String -> Name ()
    var,        -- :: Name () -> Exp ()
    op,         -- :: Name () -> QOp
    qvar,       -- :: Module -> Name () -> Exp ()
    pvar,       -- :: Name () -> Pat ()
    app,        -- :: Exp () -> Exp () -> Exp ()
    infixApp,   -- :: Exp () -> QOp -> Exp () -> Exp ()
    appFun,     -- :: Exp () -> [Exp] -> Exp ()
    pApp,       -- :: Name () -> [Pat] -> Pat ()
    tuple,      -- :: [Exp] -> Exp ()
    pTuple,     -- :: [Pat] -> Pat ()
    varTuple,   -- :: [Name] -> Exp ()
    pvarTuple,  -- :: [Name] -> Pat ()
    function,   -- :: String -> Exp ()
    strE,       -- :: String -> Exp ()
    charE,      -- :: Char -> Exp ()
    intE,       -- :: Integer -> Exp ()
    strP,       -- :: String -> Pat ()
    charP,      -- :: Char -> Pat ()
    intP,       -- :: Integer -> Pat ()
    doE,        -- :: [Stmt] -> Exp ()
    lamE,       -- :: SrcLoc -> [Pat] -> Exp () -> Exp ()
    letE,       -- :: [Decl] -> Exp () -> Exp ()
    caseE,      -- :: Exp () -> [Alt] -> Exp ()
    alt,        -- :: SrcLoc -> Pat () -> Exp () -> Alt
    altGW,      -- :: SrcLoc -> Pat () -> [Stmt] -> Exp () -> Binds -> Alt
    listE,      -- :: [Exp] -> Exp ()
    eList,      -- :: Exp ()
    peList,     -- :: Pat ()
    paren,      -- :: Exp () -> Exp ()
    pParen,     -- :: Pat () -> Pat ()
    qualStmt,   -- :: Exp () -> Stmt
    genStmt,    -- :: SrcLoc -> Pat () -> Exp () -> Stmt
    letStmt,    -- :: [Decl] -> Stmt
    binds,      -- :: [Decl] -> Binds
    noBinds,    -- :: Binds
    wildcard,   -- :: Pat ()
    genNames,   -- :: String -> Int -> [Name]

    -- * More advanced building
    sfun,           -- :: SrcLoc -> Name () -> [Name] -> Rhs -> Binds -> Decl ()
    simpleFun,      -- :: SrcLoc -> Name () -> Name () -> Exp () -> Decl ()
    patBind,        -- :: SrcLoc -> Pat () -> Exp () -> Decl ()
    patBindWhere,   -- :: SrcLoc -> Pat () -> Exp () -> [Decl] -> Decl ()
    nameBind,       -- :: SrcLoc -> Name () -> Exp () -> Decl ()
    metaFunction,   -- :: String -> [Exp] -> Exp ()
    metaConPat      -- :: String -> [Pat] -> Pat ()
  ) where

import Language.Haskell.Exts.Syntax

-----------------------------------------------------------------------------
-- Help functions for Abstract syntax

-- | An identifier with the given string as its name.
--   The string should be a valid Haskell identifier.
name :: String -> Name ()
name = Ident ()

-- | A symbol identifier. The string should be a valid
--   Haskell symbol identifier.
sym :: String -> Name ()
sym = Symbol ()

-- | A local variable as expression.
var :: Name () -> Exp ()
var = Var () . UnQual ()

-- | Use the given identifier as an operator.
op :: Name () -> QOp ()
op = QVarOp () . UnQual ()

-- | A qualified variable as expression.
qvar :: ModuleName () -> Name () -> Exp ()
qvar m n = Var () $ Qual () m n

-- | A pattern variable.
pvar :: Name () -> Pat ()
pvar = PVar ()

-- | Application of expressions by juxtaposition.
app :: Exp () -> Exp () -> Exp ()
app = App ()

-- | Apply an operator infix.
infixApp :: Exp () -> QOp () -> Exp () -> Exp ()
infixApp = InfixApp ()

-- | Apply a function to a list of arguments.
appFun :: Exp () -> [Exp ()] -> Exp ()
appFun f [] = f
appFun f (a:as) = appFun (app f a) as

-- | A constructor pattern, with argument patterns.
pApp :: Name () -> [Pat ()] -> Pat ()
pApp n ps = PApp () (UnQual () n) ps

-- | A tuple expression.
tuple :: [Exp ()] -> Exp ()
tuple = Tuple () Boxed

-- | A tuple pattern.
pTuple :: [Pat ()] -> Pat ()
pTuple = PTuple () Boxed

-- | A tuple expression consisting of variables only.
varTuple :: [Name ()] -> Exp ()
varTuple ns = tuple $ map var ns

-- | A tuple pattern consisting of variables only.
pvarTuple :: [Name ()] -> Pat ()
pvarTuple ns = pTuple $ map pvar ns

-- | A function with a given name.
function :: String -> Exp ()
function = var . Ident ()

-- | A literal string expression.
strE :: String -> Exp ()
strE s = Lit () (String () s s)

-- | A literal character expression.
charE :: Char -> Exp ()
charE c = Lit () (Char () c [c])

-- | A literal integer expression.
intE :: Integer -> Exp ()
intE n = Lit () (Int () n (show n))

-- | A literal string pattern.
strP :: String -> Pat ()
strP s = PLit () (Signless ()) (String () s s)

-- | A literal character pattern.
charP :: Char -> Pat ()
charP x = PLit () (Signless ()) (Char () x [x])

-- | A literal integer pattern.
intP :: Integer -> Pat ()
intP x = PLit ()
          (if x >= 0 then Signless () else Negative ())
          (Int () (abs x) (show x))

-- | A do block formed by the given statements.
--   The last statement in the list should be
--   a 'Qualifier' expression.
doE :: [Stmt ()] -> Exp ()
doE = Do ()

-- | Lambda abstraction, given a list of argument
--   patterns and an expression body.
lamE :: [Pat ()] -> Exp () -> Exp ()
lamE = Lambda ()

-- | A @let@ ... @in@ block.
letE :: [Decl ()] -> Exp () -> Exp ()
letE ds e = Let () (binds ds) e

-- | A @case@ expression.
caseE :: Exp () -> [Alt ()] -> Exp ()
caseE = Case ()

-- | An unguarded alternative in a @case@ expression.
alt :: Pat () -> Exp () -> Alt ()
alt p e = Alt () p (unGAlt e) noBinds

-- | An alternative with a single guard in a @case@ expression.
altGW :: Pat () -> [Stmt ()] -> Exp () -> Binds () -> Alt ()
altGW p gs e w = Alt () p (gAlt gs e) (Just w)

-- | An unguarded righthand side of a @case@ alternative.
unGAlt :: Exp () -> Rhs ()
unGAlt = UnGuardedRhs ()

-- | An list of guarded righthand sides for a @case@ alternative.
gAlts :: [([Stmt ()],Exp ())] -> Rhs ()
gAlts as = GuardedRhss () $ map (\(gs,e) -> GuardedRhs () gs e) as

-- | A single guarded righthand side for a @case@ alternative.
gAlt :: [Stmt ()] -> Exp () -> Rhs ()
gAlt gs e = gAlts [(gs,e)]

-- | A list expression.
listE :: [Exp ()] -> Exp ()
listE = List ()

-- | The empty list expression.
eList :: Exp ()
eList = List () []

-- | The empty list pattern.
peList :: Pat ()
peList = PList () []

-- | Put parentheses around an expression.
paren :: Exp () -> Exp ()
paren = Paren ()

-- | Put parentheses around a pattern.
pParen :: Pat () -> Pat ()
pParen = PParen ()

-- | A qualifier expression statement.
qualStmt :: Exp () -> Stmt ()
qualStmt = Qualifier ()

-- | A generator statement: /pat/ @<-@ /exp/
genStmt :: Pat () -> Exp () -> Stmt ()
genStmt = Generator ()

-- | A @let@ binding group as a statement.
letStmt :: [Decl ()] -> Stmt ()
letStmt ds = LetStmt () $ binds ds

-- | Hoist a set of declarations to a binding group.
binds :: [Decl ()] -> Binds ()
binds = BDecls ()

-- | An empty binding group.
noBinds :: Maybe (Binds ())
noBinds = Nothing

-- | The wildcard pattern: @_@
wildcard :: Pat ()
wildcard = PWildCard ()

-- | Generate k names by appending numbers 1 through k to a given string.
genNames :: String -> Int -> [Name ()]
genNames s k = [ Ident () $ s ++ show i | i <- [1..k] ]

-------------------------------------------------------------------------------
-- Some more specialised help functions

-- | A function with a single clause
sfun :: Name () -> [Name ()] -> (Rhs ()) -> Maybe (Binds ()) -> Decl ()
sfun f pvs rhs bs = FunBind () [Match () f (map pvar pvs) rhs bs]

-- | A function with a single clause, a single argument, no guards
-- and no where declarations
simpleFun :: Name () -> Name () -> Exp () -> Decl ()
simpleFun f a e = let rhs = UnGuardedRhs () e
             in sfun f [a] rhs noBinds

-- | A pattern bind where the pattern is a variable, and where
-- there are no guards and no 'where' clause.
patBind :: Pat () -> Exp () -> Decl ()
patBind p e = let rhs = UnGuardedRhs () e
         in PatBind () p rhs noBinds

-- | A pattern bind where the pattern is a variable, and where
-- there are no guards, but with a 'where' clause.
patBindWhere :: Pat () -> Exp () -> [Decl ()] -> Decl ()
patBindWhere p e ds = let rhs = UnGuardedRhs () e
             in PatBind () p rhs (if null ds then Nothing else Just (binds ds))

-- | Bind an identifier to an expression.
nameBind :: Name () -> Exp () -> Decl ()
nameBind n e = patBind (pvar n) e

-- | Apply function of a given name to a list of arguments.
metaFunction :: String -> [Exp ()] -> Exp ()
metaFunction s' es' = mf s' (reverse es')
  where mf s []     = var $ name s
        mf s (e:es) = app (mf s es) e

-- | Apply a constructor of a given name to a list of pattern
--   arguments, forming a constructor pattern.
metaConPat :: String -> [Pat ()] -> Pat ()
metaConPat s ps = pApp (name s) ps
