content: {-#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
  