{-#LANGUAGE DeriveFunctor #-}
-- | Implements Ginger's Abstract Syntax Tree.
module Text.Ginger.AST
where

import Data.Text (Text)
import qualified Data.Text as Text
import Text.Ginger.Html
import Data.Scientific (Scientific)
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap


-- | A context variable name.
type VarName = Text

-- | Top-level data structure, representing a fully parsed template.
data Template a
    = Template
        { forall a. Template a -> Statement a
templateBody :: Statement a
        , forall a. Template a -> HashMap VarName (Block a)
templateBlocks :: HashMap VarName (Block a)
        , forall a. Template a -> Maybe (Template a)
templateParent :: Maybe (Template a)
        }
        deriving (Int -> Template a -> ShowS
forall a. Show a => Int -> Template a -> ShowS
forall a. Show a => [Template a] -> ShowS
forall a. Show a => Template a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Template a] -> ShowS
$cshowList :: forall a. Show a => [Template a] -> ShowS
show :: Template a -> String
$cshow :: forall a. Show a => Template a -> String
showsPrec :: Int -> Template a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Template a -> ShowS
Show, forall a b. a -> Template b -> Template a
forall a b. (a -> b) -> Template a -> Template b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Template b -> Template a
$c<$ :: forall a b. a -> Template b -> Template a
fmap :: forall a b. (a -> b) -> Template a -> Template b
$cfmap :: forall a b. (a -> b) -> Template a -> Template b
Functor)

-- | A macro definition ( @{% macro %}@ )
data Macro a
    = Macro { forall a. Macro a -> [VarName]
macroArgs :: [VarName], forall a. Macro a -> Statement a
macroBody :: Statement a }
    deriving (Int -> Macro a -> ShowS
forall a. Show a => Int -> Macro a -> ShowS
forall a. Show a => [Macro a] -> ShowS
forall a. Show a => Macro a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Macro a] -> ShowS
$cshowList :: forall a. Show a => [Macro a] -> ShowS
show :: Macro a -> String
$cshow :: forall a. Show a => Macro a -> String
showsPrec :: Int -> Macro a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Macro a -> ShowS
Show, forall a b. a -> Macro b -> Macro a
forall a b. (a -> b) -> Macro a -> Macro b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Macro b -> Macro a
$c<$ :: forall a b. a -> Macro b -> Macro a
fmap :: forall a b. (a -> b) -> Macro a -> Macro b
$cfmap :: forall a b. (a -> b) -> Macro a -> Macro b
Functor)

-- | A block definition ( @{% block %}@ )
data Block a
    = Block { forall a. Block a -> Statement a
blockBody :: Statement a } -- TODO: scoped blocks
    deriving (Int -> Block a -> ShowS
forall a. Show a => Int -> Block a -> ShowS
forall a. Show a => [Block a] -> ShowS
forall a. Show a => Block a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Block a] -> ShowS
$cshowList :: forall a. Show a => [Block a] -> ShowS
show :: Block a -> String
$cshow :: forall a. Show a => Block a -> String
showsPrec :: Int -> Block a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Block a -> ShowS
Show, forall a b. a -> Block b -> Block a
forall a b. (a -> b) -> Block a -> Block b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Block b -> Block a
$c<$ :: forall a b. a -> Block b -> Block a
fmap :: forall a b. (a -> b) -> Block a -> Block b
$cfmap :: forall a b. (a -> b) -> Block a -> Block b
Functor)

-- | Ginger statements.
data Statement a
    = MultiS a [Statement a] -- ^ A sequence of multiple statements
    | ScopedS a (Statement a) -- ^ Run wrapped statement in a local scope
    | IndentS a (Expression a) (Statement a) -- ^ Establish an indented context around the wrapped statement
    | LiteralS a Html -- ^ Literal output (anything outside of any tag)
    | InterpolationS a (Expression a) -- ^ {{ expression }}
    | ExpressionS a (Expression a) -- ^ Evaluate expression
    | IfS a (Expression a) (Statement a) (Statement a) -- ^ {% if expression %}statement{% else %}statement{% endif %}
    | SwitchS a (Expression a) [((Expression a), (Statement a))] (Statement a) -- ^ {% switch expression %}{% case expression %}statement{% endcase %}...{% default %}statement{% enddefault %}{% endswitch %}
    | ForS a (Maybe VarName) VarName (Expression a) (Statement a) -- ^ {% for index, varname in expression %}statement{% endfor %}
    | SetVarS a VarName (Expression a) -- ^ {% set varname = expr %}
    | DefMacroS a VarName (Macro a) -- ^ {% macro varname %}statements{% endmacro %}
    | BlockRefS a VarName
    | PreprocessedIncludeS a (Template a) -- ^ {% include "template" %}
    | NullS a -- ^ The do-nothing statement (NOP)
    | TryCatchS a (Statement a) [CatchBlock a] (Statement a) -- ^ Try / catch / finally
    deriving (Int -> Statement a -> ShowS
forall a. Show a => Int -> Statement a -> ShowS
forall a. Show a => [Statement a] -> ShowS
forall a. Show a => Statement a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Statement a] -> ShowS
$cshowList :: forall a. Show a => [Statement a] -> ShowS
show :: Statement a -> String
$cshow :: forall a. Show a => Statement a -> String
showsPrec :: Int -> Statement a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Statement a -> ShowS
Show, forall a b. a -> Statement b -> Statement a
forall a b. (a -> b) -> Statement a -> Statement b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Statement b -> Statement a
$c<$ :: forall a b. a -> Statement b -> Statement a
fmap :: forall a b. (a -> b) -> Statement a -> Statement b
$cfmap :: forall a b. (a -> b) -> Statement a -> Statement b
Functor)

stmtAnnotation :: Statement a -> a
stmtAnnotation (MultiS a
a [Statement a]
_) = a
a
stmtAnnotation (ScopedS a
a Statement a
_) = a
a
stmtAnnotation (IndentS a
a Expression a
_ Statement a
_) = a
a
stmtAnnotation (LiteralS a
a Html
_) = a
a
stmtAnnotation (InterpolationS a
a Expression a
_) = a
a
stmtAnnotation (ExpressionS a
a Expression a
_) = a
a
stmtAnnotation (IfS a
a Expression a
_ Statement a
_ Statement a
_) = a
a
stmtAnnotation (SwitchS a
a Expression a
_ [(Expression a, Statement a)]
_ Statement a
_) = a
a
stmtAnnotation (ForS a
a Maybe VarName
_ VarName
_ Expression a
_ Statement a
_) = a
a
stmtAnnotation (SetVarS a
a VarName
_ Expression a
_) = a
a
stmtAnnotation (DefMacroS a
a VarName
_ Macro a
_) = a
a
stmtAnnotation (BlockRefS a
a VarName
_) = a
a
stmtAnnotation (PreprocessedIncludeS a
a Template a
_) = a
a
stmtAnnotation (NullS a
a) = a
a
stmtAnnotation (TryCatchS a
a Statement a
_ [CatchBlock a]
_ Statement a
_) = a
a

-- | A @catch@ block
data CatchBlock a =
    Catch
        { forall a. CatchBlock a -> Maybe VarName
catchWhat :: Maybe Text
        , forall a. CatchBlock a -> Maybe VarName
catchCaptureAs :: Maybe VarName
        , forall a. CatchBlock a -> Statement a
catchBody :: Statement a
        }
        deriving (Int -> CatchBlock a -> ShowS
forall a. Show a => Int -> CatchBlock a -> ShowS
forall a. Show a => [CatchBlock a] -> ShowS
forall a. Show a => CatchBlock a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CatchBlock a] -> ShowS
$cshowList :: forall a. Show a => [CatchBlock a] -> ShowS
show :: CatchBlock a -> String
$cshow :: forall a. Show a => CatchBlock a -> String
showsPrec :: Int -> CatchBlock a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> CatchBlock a -> ShowS
Show, forall a b. a -> CatchBlock b -> CatchBlock a
forall a b. (a -> b) -> CatchBlock a -> CatchBlock b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> CatchBlock b -> CatchBlock a
$c<$ :: forall a b. a -> CatchBlock b -> CatchBlock a
fmap :: forall a b. (a -> b) -> CatchBlock a -> CatchBlock b
$cfmap :: forall a b. (a -> b) -> CatchBlock a -> CatchBlock b
Functor)

-- | Expressions, building blocks for the expression minilanguage.
data Expression a
    = StringLiteralE a Text -- ^ String literal expression: "foobar"
    | NumberLiteralE a Scientific -- ^ Numeric literal expression: 123.4
    | BoolLiteralE a Bool -- ^ Boolean literal expression: true
    | NullLiteralE a -- ^ Literal null
    | VarE a VarName -- ^ Variable reference: foobar
    | ListE a [(Expression a)] -- ^ List construct: [ expr, expr, expr ]
    | ObjectE a [((Expression a), (Expression a))] -- ^ Object construct: { expr: expr, expr: expr, ... }
    | MemberLookupE a (Expression a) (Expression a) -- ^ foo[bar] (also dot access)
    | CallE a (Expression a) [(Maybe Text, (Expression a))] -- ^ foo(bar=baz, quux)
    | LambdaE a [Text] (Expression a) -- ^ (foo, bar) -> expr
    | TernaryE a (Expression a) (Expression a) (Expression a) -- ^ expr ? expr : expr
    | DoE a (Statement a) -- ^ do { statement; }
    deriving (Int -> Expression a -> ShowS
forall a. Show a => Int -> Expression a -> ShowS
forall a. Show a => [Expression a] -> ShowS
forall a. Show a => Expression a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Expression a] -> ShowS
$cshowList :: forall a. Show a => [Expression a] -> ShowS
show :: Expression a -> String
$cshow :: forall a. Show a => Expression a -> String
showsPrec :: Int -> Expression a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Expression a -> ShowS
Show, forall a b. a -> Expression b -> Expression a
forall a b. (a -> b) -> Expression a -> Expression b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Expression b -> Expression a
$c<$ :: forall a b. a -> Expression b -> Expression a
fmap :: forall a b. (a -> b) -> Expression a -> Expression b
$cfmap :: forall a b. (a -> b) -> Expression a -> Expression b
Functor)

exprAnnotation :: Expression a -> a
exprAnnotation (StringLiteralE a
a VarName
_) = a
a
exprAnnotation (NumberLiteralE a
a Scientific
_) = a
a
exprAnnotation (BoolLiteralE a
a Bool
_) = a
a
exprAnnotation (NullLiteralE a
a) = a
a
exprAnnotation (VarE a
a VarName
_) = a
a
exprAnnotation (ListE a
a [Expression a]
_) = a
a
exprAnnotation (ObjectE a
a [(Expression a, Expression a)]
_) = a
a
exprAnnotation (MemberLookupE a
a Expression a
_ Expression a
_) = a
a
exprAnnotation (CallE a
a Expression a
_ [(Maybe VarName, Expression a)]
_) = a
a
exprAnnotation (LambdaE a
a [VarName]
_ Expression a
_) = a
a
exprAnnotation (TernaryE a
a Expression a
_ Expression a
_ Expression a
_) = a
a
exprAnnotation (DoE a
a Statement a
_) = a
a

class Annotated f where
    annotation :: f p -> p

instance Annotated Expression where
    annotation :: forall p. Expression p -> p
annotation = forall p. Expression p -> p
exprAnnotation

instance Annotated Statement where
    annotation :: forall p. Statement p -> p
annotation = forall p. Statement p -> p
stmtAnnotation

instance Annotated Block where
    annotation :: forall p. Block p -> p
annotation = forall (f :: * -> *) p. Annotated f => f p -> p
annotation forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Block a -> Statement a
blockBody

instance Annotated Macro where
    annotation :: forall p. Macro p -> p
annotation = forall (f :: * -> *) p. Annotated f => f p -> p
annotation forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Macro a -> Statement a
macroBody

instance Annotated Template where
    annotation :: forall p. Template p -> p
annotation = forall (f :: * -> *) p. Annotated f => f p -> p
annotation forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Template a -> Statement a
templateBody