{-#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 { templateBody :: Statement a , templateBlocks :: HashMap VarName (Block a) , templateParent :: Maybe (Template a) } deriving (Show, Functor) -- | A macro definition ( @{% macro %}@ ) data Macro a = Macro { macroArgs :: [VarName], macroBody :: Statement a } deriving (Show, Functor) -- | A block definition ( @{% block %}@ ) data Block a = Block { blockBody :: Statement a } -- TODO: scoped blocks deriving (Show, 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 (Show, Functor) stmtAnnotation (MultiS a _) = a stmtAnnotation (ScopedS a _) = a stmtAnnotation (IndentS a _ _) = a stmtAnnotation (LiteralS a _) = a stmtAnnotation (InterpolationS a _) = a stmtAnnotation (ExpressionS a _) = a stmtAnnotation (IfS a _ _ _) = a stmtAnnotation (SwitchS a _ _ _) = a stmtAnnotation (ForS a _ _ _ _) = a stmtAnnotation (SetVarS a _ _) = a stmtAnnotation (DefMacroS a _ _) = a stmtAnnotation (BlockRefS a _) = a stmtAnnotation (PreprocessedIncludeS a _) = a stmtAnnotation (NullS a) = a stmtAnnotation (TryCatchS a _ _ _) = a -- | A @catch@ block data CatchBlock a = Catch { catchWhat :: Maybe Text , catchCaptureAs :: Maybe VarName , catchBody :: Statement a } deriving (Show, 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 (Show, Functor) exprAnnotation (StringLiteralE a _) = a exprAnnotation (NumberLiteralE a _) = a exprAnnotation (BoolLiteralE a _) = a exprAnnotation (NullLiteralE a) = a exprAnnotation (VarE a _) = a exprAnnotation (ListE a _) = a exprAnnotation (ObjectE a _) = a exprAnnotation (MemberLookupE a _ _) = a exprAnnotation (CallE a _ _) = a exprAnnotation (LambdaE a _ _) = a exprAnnotation (TernaryE a _ _ _) = a exprAnnotation (DoE a _) = a class Annotated f where annotation :: f p -> p instance Annotated Expression where annotation = exprAnnotation instance Annotated Statement where annotation = stmtAnnotation instance Annotated Block where annotation = annotation . blockBody instance Annotated Macro where annotation = annotation . macroBody instance Annotated Template where annotation = annotation . templateBody