A go template renderer based on Perl's Template Toolkit
at main 254 lines 7.0 kB view raw
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() {}