A go template renderer based on Perl's Template Toolkit
1package gott
2
3// Node is the interface implemented by all AST nodes
4type Node interface {
5 Pos() Position // position of the first token of this node
6 node() // marker method to ensure only AST types implement Node
7}
8
9// Expr is the interface implemented by all expression nodes
10type Expr interface {
11 Node
12 expr() // marker method
13}
14
15// Stmt is the interface implemented by all statement nodes
16type Stmt interface {
17 Node
18 stmt() // marker method
19}
20
21// ---- Template (root node) ----
22
23// Template is the root node containing all parsed nodes
24type Template struct {
25 Position Position
26 Nodes []Node // mix of TextNode, statements, and output expressions
27}
28
29func (t *Template) Pos() Position { return t.Position }
30func (t *Template) node() {}
31
32// ---- Literal/Text Nodes ----
33
34// TextNode holds literal text outside of tags
35type TextNode struct {
36 Position Position
37 Text string
38}
39
40func (n *TextNode) Pos() Position { return n.Position }
41func (n *TextNode) node() {}
42
43// ---- Expression Nodes ----
44
45// IdentExpr represents a variable reference with optional dot notation
46// e.g., "foo", "foo.bar", "foo.bar.baz"
47type IdentExpr struct {
48 Position Position
49 Parts []string // ["foo", "bar", "baz"] for foo.bar.baz
50}
51
52func (e *IdentExpr) Pos() Position { return e.Position }
53func (e *IdentExpr) node() {}
54func (e *IdentExpr) expr() {}
55
56// LiteralExpr holds a string or number literal
57type LiteralExpr struct {
58 Position Position
59 Value any // string or float64
60}
61
62func (e *LiteralExpr) Pos() Position { return e.Position }
63func (e *LiteralExpr) node() {}
64func (e *LiteralExpr) expr() {}
65
66// BinaryExpr represents a binary operation: left op right
67type BinaryExpr struct {
68 Position Position
69 Op TokenType
70 Left Expr
71 Right Expr
72}
73
74func (e *BinaryExpr) Pos() Position { return e.Position }
75func (e *BinaryExpr) node() {}
76func (e *BinaryExpr) expr() {}
77
78// UnaryExpr represents a unary operation: op expr
79type UnaryExpr struct {
80 Position Position
81 Op TokenType
82 X Expr
83}
84
85func (e *UnaryExpr) Pos() Position { return e.Position }
86func (e *UnaryExpr) node() {}
87func (e *UnaryExpr) expr() {}
88
89// CallExpr represents a function call: func(args...)
90type CallExpr struct {
91 Position Position
92 Func string
93 Args []Expr
94}
95
96func (e *CallExpr) Pos() Position { return e.Position }
97func (e *CallExpr) node() {}
98func (e *CallExpr) expr() {}
99
100// MethodCallExpr represents a virtual method call: obj.method(args...)
101type MethodCallExpr struct {
102 Position Position
103 Receiver Expr
104 Method string
105 Args []Expr
106}
107
108func (e *MethodCallExpr) Pos() Position { return e.Position }
109func (e *MethodCallExpr) node() {}
110func (e *MethodCallExpr) expr() {}
111
112// FilterExpr represents a filter application: expr | filter or expr | filter(args)
113type FilterExpr struct {
114 Position Position
115 Input Expr
116 Filter string
117 Args []Expr
118}
119
120func (e *FilterExpr) Pos() Position { return e.Position }
121func (e *FilterExpr) node() {}
122func (e *FilterExpr) expr() {}
123
124// DefaultExpr represents a default value expression: expr || default
125type DefaultExpr struct {
126 Position Position
127 Expr Expr
128 Default Expr
129}
130
131func (e *DefaultExpr) Pos() Position { return e.Position }
132func (e *DefaultExpr) node() {}
133func (e *DefaultExpr) expr() {}
134
135// ---- Statement Nodes ----
136
137// OutputStmt represents an expression to be output: [% expr %]
138type OutputStmt struct {
139 Position Position
140 Expr Expr
141}
142
143func (s *OutputStmt) Pos() Position { return s.Position }
144func (s *OutputStmt) node() {}
145func (s *OutputStmt) stmt() {}
146
147// IfStmt represents an IF/ELSIF/ELSE chain
148type IfStmt struct {
149 Position Position
150 Condition Expr
151 Body []Node
152 ElsIf []*ElsIfClause // zero or more ELSIF clauses
153 Else []Node // optional ELSE body
154}
155
156func (s *IfStmt) Pos() Position { return s.Position }
157func (s *IfStmt) node() {}
158func (s *IfStmt) stmt() {}
159
160// ElsIfClause represents an ELSIF branch
161type ElsIfClause struct {
162 Position Position
163 Condition Expr
164 Body []Node
165}
166
167// UnlessStmt represents an UNLESS conditional
168type UnlessStmt struct {
169 Position Position
170 Condition Expr
171 Body []Node
172 Else []Node // optional ELSE body
173}
174
175func (s *UnlessStmt) Pos() Position { return s.Position }
176func (s *UnlessStmt) node() {}
177func (s *UnlessStmt) stmt() {}
178
179// ForeachStmt represents a FOREACH loop
180type ForeachStmt struct {
181 Position Position
182 ItemVar string // loop variable name
183 ListExpr Expr // expression for the list/map to iterate
184 Body []Node
185}
186
187func (s *ForeachStmt) Pos() Position { return s.Position }
188func (s *ForeachStmt) node() {}
189func (s *ForeachStmt) stmt() {}
190
191// BlockStmt represents a named block definition
192type BlockStmt struct {
193 Position Position
194 Name string
195 Body []Node
196}
197
198func (s *BlockStmt) Pos() Position { return s.Position }
199func (s *BlockStmt) node() {}
200func (s *BlockStmt) stmt() {}
201
202// PathPart represents a segment of a dynamic path for INCLUDE/WRAPPER.
203// A path like "templates/$category/page.html" becomes:
204// [{IsVariable: false, Value: "templates/"}, {IsVariable: true, Parts: ["category"]}, {IsVariable: false, Value: "/page.html"}]
205type PathPart struct {
206 IsVariable bool // true if this is a $variable reference
207 Value string // literal text (when IsVariable is false)
208 Parts []string // variable parts for dot notation: $user.name -> ["user", "name"]
209}
210
211// IncludeStmt represents an INCLUDE directive
212type IncludeStmt struct {
213 Position Position
214 Name string // static path (used when PathParts is empty)
215 PathParts []PathPart // dynamic path parts (used when path contains $variables)
216}
217
218func (s *IncludeStmt) Pos() Position { return s.Position }
219func (s *IncludeStmt) node() {}
220func (s *IncludeStmt) stmt() {}
221
222// WrapperStmt represents a WRAPPER directive
223type WrapperStmt struct {
224 Position Position
225 Name string // static path (used when PathParts is empty)
226 PathParts []PathPart // dynamic path parts (used when path contains $variables)
227 Content []Node // content to be wrapped
228}
229
230func (s *WrapperStmt) Pos() Position { return s.Position }
231func (s *WrapperStmt) node() {}
232func (s *WrapperStmt) stmt() {}
233
234// SetStmt represents a SET variable assignment
235type SetStmt struct {
236 Position Position
237 Var string
238 Value Expr
239}
240
241func (s *SetStmt) Pos() Position { return s.Position }
242func (s *SetStmt) node() {}
243func (s *SetStmt) stmt() {}
244
245// TryStmt represents a TRY/CATCH error handling block
246type TryStmt struct {
247 Position Position
248 Try []Node // content to try
249 Catch []Node // fallback content if error occurs
250}
251
252func (s *TryStmt) Pos() Position { return s.Position }
253func (s *TryStmt) node() {}
254func (s *TryStmt) stmt() {}