Skip to main content

gracile_core/
ast.rs

1//! Abstract syntax tree types for Gracile templates.
2
3/// A parsed template — an ordered sequence of nodes.
4#[derive(Debug, Clone)]
5pub struct Template {
6    pub nodes: Vec<Node>,
7}
8
9/// A single node inside a template body.
10#[derive(Debug, Clone)]
11pub enum Node {
12    RawText(String),
13    Comment(String),
14    ExprTag(ExprTag),
15    IfBlock(IfBlock),
16    EachBlock(EachBlock),
17    SnippetBlock(SnippetBlock),
18    RawBlock(String),
19    RenderTag(RenderTag),
20    ConstTag(ConstTag),
21    IncludeTag(IncludeTag),
22    DebugTag(DebugTag),
23}
24
25/// `{= expr}` — escaped interpolation; `{~ expr}` — raw/unescaped interpolation.
26#[derive(Debug, Clone)]
27pub struct ExprTag {
28    pub expr: Expr,
29    /// `true` → raw output (`{~ expr}`), `false` → HTML-escaped output (`{= expr}`).
30    pub raw: bool,
31}
32
33/// `{#if cond}...{:else if cond}...{:else}...{/if}`
34#[derive(Debug, Clone)]
35pub struct IfBlock {
36    /// First entry is the `{#if}` branch; subsequent entries are `{:else if}` branches.
37    pub branches: Vec<IfBranch>,
38    /// Body of the `{:else}` branch, if present.
39    pub else_body: Option<Vec<Node>>,
40}
41
42#[derive(Debug, Clone)]
43pub struct IfBranch {
44    pub condition: Expr,
45    pub body: Vec<Node>,
46}
47
48/// `{#each expr as pattern, index, loop}...{:else}...{/each}`
49#[derive(Debug, Clone)]
50pub struct EachBlock {
51    pub iterable: Expr,
52    pub pattern: Pattern,
53    pub index_binding: Option<String>,
54    /// Optional third binding exposing loop metadata: `{ index, length, first, last }`.
55    pub loop_binding: Option<String>,
56    pub body: Vec<Node>,
57    pub else_body: Option<Vec<Node>>,
58}
59
60/// `{#snippet name(p1, p2)}...{/snippet}`
61#[derive(Debug, Clone)]
62pub struct SnippetBlock {
63    pub name: String,
64    pub params: Vec<String>,
65    pub body: Vec<Node>,
66}
67
68/// `{@render name(args)}`
69#[derive(Debug, Clone)]
70pub struct RenderTag {
71    pub name: String,
72    pub args: Vec<Expr>,
73}
74
75/// `{@const name = expr}`
76#[derive(Debug, Clone)]
77pub struct ConstTag {
78    pub name: String,
79    pub expr: Expr,
80}
81
82/// `{@include "path"}`
83#[derive(Debug, Clone)]
84pub struct IncludeTag {
85    pub path: String,
86}
87
88/// `{@debug [expr]}`
89#[derive(Debug, Clone)]
90pub struct DebugTag {
91    pub expr: Option<Expr>,
92}
93
94/// Variable binding pattern used in `{#each}`.
95#[derive(Debug, Clone)]
96pub enum Pattern {
97    /// `{#each items as item}` — binds the whole element.
98    Ident(String),
99    /// `{#each items as { name, email }}` — destructures object keys.
100    Destructure(Vec<String>),
101}
102
103/// A Gracile expression.
104#[derive(Debug, Clone)]
105pub enum Expr {
106    Null,
107    Bool(bool),
108    Int(i64),
109    Float(f64),
110    String(String),
111    Array(Vec<Expr>),
112    Ident(String),
113    /// `object.property`
114    MemberAccess {
115        object: Box<Expr>,
116        property: String,
117    },
118    /// `object[index]`
119    IndexAccess {
120        object: Box<Expr>,
121        index: Box<Expr>,
122    },
123    /// `expr | filter1 | filter2(arg)`
124    Filter {
125        expr: Box<Expr>,
126        filters: Vec<FilterApplication>,
127    },
128    /// `cond ? then : else`
129    Ternary {
130        condition: Box<Expr>,
131        consequent: Box<Expr>,
132        alternate: Box<Expr>,
133    },
134    /// Binary operators.
135    Binary {
136        op: BinaryOp,
137        left: Box<Expr>,
138        right: Box<Expr>,
139    },
140    /// Unary operators.
141    Unary {
142        op: UnaryOp,
143        operand: Box<Expr>,
144    },
145    /// `expr is [not] test_name`
146    Test {
147        expr: Box<Expr>,
148        negated: bool,
149        test_name: String,
150    },
151    /// `expr [not] in collection`
152    Membership {
153        expr: Box<Expr>,
154        negated: bool,
155        collection: Box<Expr>,
156    },
157}
158
159/// A single filter in a filter chain: `name(arg1, arg2)`.
160#[derive(Debug, Clone)]
161pub struct FilterApplication {
162    pub name: String,
163    pub args: Vec<Expr>,
164}
165
166#[derive(Debug, Clone, PartialEq, Eq)]
167pub enum BinaryOp {
168    NullCoalesce, // ??
169    Or,           // ||
170    And,          // &&
171    Eq,           // ==
172    Neq,          // !=
173    Lt,           // <
174    Gt,           // >
175    Lte,          // <=
176    Gte,          // >=
177    Add,          // + (numbers → addition; strings → concatenation)
178    Sub,          // -
179    Mul,          // *
180    Div,          // /
181    Mod,          // %
182}
183
184#[derive(Debug, Clone, PartialEq, Eq)]
185pub enum UnaryOp {
186    Not, // !
187    Neg, // -
188}