Implement a basic Go text/template parser using ANTLR that supports core template functionality. The parser should handle variable interpolation, conditional statements, and loops while maintaining compatibility with Go's template syntax.
- Support
{{ .Field }}syntax for accessing struct fields - Support nested field access:
{{ .User.Name }} - Support array/slice indexing:
{{ .Items[0] }} - Support map access:
{{ .Data.key }} - Handle whitespace control with
{{- }}and{{ -}}
- Support
{{ if condition }}...{{ end }}blocks - Support
{{ if condition }}...{{ else }}...{{ end }} - Support
{{ if condition }}...{{ else if condition2 }}...{{ end }} - Conditions should support:
- Boolean fields:
{{ if .IsActive }} - Comparisons:
{{ if eq .Status "active" }} - Existence checks:
{{ if .User }}
- Boolean fields:
- Support
{{ range .Items }}...{{ end }}for iterating over slices/arrays - Support
{{ range $index, $value := .Items }}...{{ end }}with variables - Support
{{ range .Map }}...{{ end }}for iterating over maps - Support
{{ range $key, $value := .Map }}...{{ end }}with key-value variables - Handle empty collections with
{{ range .Items }}...{{ else }}...{{ end }}
eq: equality comparison{{ if eq .Status "active" }}ne: not equal{{ if ne .Count 0 }}lt,le,gt,ge: numeric comparisonsand,or: logical operations{{ if and .IsActive .IsValid }}not: logical negation{{ if not .IsHidden }}
- Support
{{/* comment */}}syntax - Comments should be completely ignored in output
Your ANTLR grammar should define:
- Template root with mixed text and actions
- Action blocks (
{{ ... }}) - Field access with dot notation
- Conditional blocks with proper nesting
- Range blocks with optional variable assignments
- Function calls with arguments
- String and numeric literals
- Whitespace control tokens
// Nested field access
{{ .User.Profile.Avatar.URL }}
// Array indexing with variables
{{ .Items[.CurrentIndex] }}
// Map access with string keys containing special characters
{{ .Data["key-with-dashes"] }}
// Accessing fields that might not exist (should not panic)
{{ .MaybeNil.Field }}// Nested conditions
{{ if .User }}
{{ if .User.IsActive }}
Active user: {{ .User.Name }}
{{ else }}
Inactive user
{{ end }}
{{ end }}
// Complex boolean expressions
{{ if and (eq .Status "active") (gt .Count 0) }}
Status is active and count is positive
{{ end }}
// Zero value checks
{{ if .Count }}Count: {{ .Count }}{{ else }}No items{{ end }}// Empty slice handling
{{ range .Items }}
Item: {{ . }}
{{ else }}
No items found
{{ end }}
// Nested loops
{{ range .Categories }}
Category: {{ .Name }}
{{ range .Items }}
- {{ .Title }}
{{ end }}
{{ end }}
// Range with index over empty slice
{{ range $i, $item := .EmptySlice }}
Never executed
{{ else }}
Empty slice message
{{ end }}// Trimming leading whitespace
{{- .Field }}
// Trimming trailing whitespace
{{ .Field -}}
// Trimming both
{{- .Field -}}
// In loops and conditions
{{ range .Items -}}
{{- .Name }}
{{- end }}// Test data
data := struct {
Name string
Count int
Active bool
Items []string
User *User
}{
Name: "Test",
Count: 42,
Active: true,
Items: []string{"a", "b", "c"},
User: &User{Name: "John", Age: 30},
}
// Test cases
"Hello {{ .Name }}" → "Hello Test"
"Count: {{ .Count }}" → "Count: 42"
"User: {{ .User.Name }}" → "User: John"
"First: {{ .Items[0] }}" → "First: a""{{ if .Active }}Active{{ else }}Inactive{{ end }}" → "Active"
"{{ if .Count }}Has items{{ end }}" → "Has items"
"{{ if eq .Name \"Test\" }}Match{{ end }}" → "Match"
"{{ if and .Active (gt .Count 10) }}Both true{{ end }}" → "Both true""{{ range .Items }}{{ . }} {{ end }}" → "a b c "
"{{ range $i, $v := .Items }}{{ $i }}:{{ $v }} {{ end }}" → "0:a 1:b 2:c "
"{{ range .EmptySlice }}Never{{ else }}Empty{{ end }}" → "Empty"// Should not panic, should render empty or default
"{{ .NonExistent }}" → ""
"{{ .User.NonExistent }}" → ""
"{{ .NilPointer.Field }}" → ""- Error Handling: The parser should gracefully handle missing fields and nil pointers without panicking
- Type Safety: Implement proper type checking for comparisons and operations
- Performance: Cache field lookups and compiled templates where possible
- Memory Safety: Avoid infinite recursion in nested templates
- Whitespace: Preserve original whitespace unless explicitly trimmed with
{{- -}}
Provide:
- ANTLR4 grammar file (.g4)
- Go code for the template engine
- Unit tests covering all edge cases mentioned above
- Example usage showing how to parse and execute templates
- Error handling examples
The implementation should be production-ready for basic use cases while remaining simple enough to understand and extend.
https://github.com/yetanotherchris/text-template/