Skip to content

Instantly share code, notes, and snippets.

@yetanotherchris
Last active June 15, 2025 18:48
Show Gist options
  • Save yetanotherchris/c80d0fadb5a2ee5b4beb0a4384020dbf to your computer and use it in GitHub Desktop.
Save yetanotherchris/c80d0fadb5a2ee5b4beb0a4384020dbf to your computer and use it in GitHub Desktop.
Claude's response for improvements to a C# implementation of Go's text templates - https://github.com/yetanotherchris/text-template

Go Text/Template Parser Implementation with ANTLR

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.

Core Requirements

1. Variable Interpolation

  • 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 {{ -}}

2. Conditional Statements

  • 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 }}

3. Loop Statements

  • 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 }}

4. Built-in Functions (Basic Set)

  • eq: equality comparison {{ if eq .Status "active" }}
  • ne: not equal {{ if ne .Count 0 }}
  • lt, le, gt, ge: numeric comparisons
  • and, or: logical operations {{ if and .IsActive .IsValid }}
  • not: logical negation {{ if not .IsHidden }}

5. Comments

  • Support {{/* comment */}} syntax
  • Comments should be completely ignored in output

Grammar Structure (ANTLR4)

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

Edge Cases to Handle

Variable Access Edge Cases

// 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 }}

Conditional Edge Cases

// 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 }}

Loop Edge Cases

// 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 }}

Whitespace Control Edge Cases

// Trimming leading whitespace
  {{- .Field }}

// Trimming trailing whitespace
{{ .Field -}}  

// Trimming both
  {{- .Field -}}  

// In loops and conditions
{{ range .Items -}}
{{- .Name }}
{{- end }}

Test Cases for Implementation

Basic Variable Tests

// 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"

Conditional Tests

"{{ 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"

Loop Tests

"{{ 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"

Error Handling Tests

// Should not panic, should render empty or default
"{{ .NonExistent }}"""
"{{ .User.NonExistent }}"""
"{{ .NilPointer.Field }}"""

Implementation Guidelines

  1. Error Handling: The parser should gracefully handle missing fields and nil pointers without panicking
  2. Type Safety: Implement proper type checking for comparisons and operations
  3. Performance: Cache field lookups and compiled templates where possible
  4. Memory Safety: Avoid infinite recursion in nested templates
  5. Whitespace: Preserve original whitespace unless explicitly trimmed with {{- -}}

Output Format

Provide:

  1. ANTLR4 grammar file (.g4)
  2. Go code for the template engine
  3. Unit tests covering all edge cases mentioned above
  4. Example usage showing how to parse and execute templates
  5. Error handling examples

The implementation should be production-ready for basic use cases while remaining simple enough to understand and extend.

@yetanotherchris
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment