- Introduction
- Installation
- Quick Start
- Template Syntax
- Pattern Matching
- Functions
- Macros
- Template Extension
- Stateless Parsing
- Code Generation
- Best Practices
GoTTP is a Go implementation of the Template Text Parser (TTP) library. It allows you to parse semi-structured text data using templates, extracting structured information into Go data structures.
- Stateless Design: Compile once, use many times without state resets
- Thread-Safe: Compiled templates are immutable and safe for concurrent use
- High Performance: Leverages Go's compiled nature
- TTP Compatible: Works with existing Python TTP templates
- Multi-Language Macros: Support for Starlark, JavaScript, and Python
go get github.com/roc-ops/gottppackage main
import (
"encoding/json"
"fmt"
"log"
"github.com/roc-ops/gottp"
)
func main() {
// Define a template
template := `
<group name="interfaces">
interface {{ interface }}
ip address {{ ip }}/{{ mask }}
description {{ description }}
</group>
`
// Compile the template
compiled, err := gottp.CompileTemplate(template)
if err != nil {
log.Fatal(err)
}
// Define input data
data := `
interface Loopback0
ip address 192.168.0.1/24
description Router-id-loopback
!
interface Vlan100
ip address 10.0.0.1/24
description Management-VLAN
!
`
// Parse the data
result, err := compiled.Parse(
gottp.Inputs{"Default_Input": data},
nil, // no variables
nil, // default options
)
if err != nil {
log.Fatal(err)
}
// Print results as JSON
jsonData, _ := json.MarshalIndent(result, "", " ")
fmt.Println(string(jsonData))
}Output:
{
"interfaces": [
{
"interface": "Loopback0",
"ip": "192.168.0.1",
"mask": "24",
"description": "Router-id-loopback"
},
{
"interface": "Vlan100",
"ip": "10.0.0.1",
"mask": "24",
"description": "Management-VLAN"
}
]
}Templates are XML-like structures with special tags:
<template>
<group name="my_group">
<!-- patterns here -->
</group>
</template><template>: Root container (optional if only one group)<group>: Pattern matching group<input>: Input data source configuration<output>: Output formatting configuration<vars>: Template variables<macro>: Macro function definitions<extend>: Template extension
Use double curly braces to define variables:
interface {{ interface }}
ip address {{ ip }}/{{ mask }}
Groups can have various attributes:
<group name="interfaces" input="config" output="interfaces.json" method="table">
<!-- patterns -->
</group>Patterns can span multiple lines:
interface {{ interface }}
ip address {{ ip }}/{{ mask }}
description {{ description }}
Groups can be nested:
<group name="device">
hostname {{ hostname }}
<group name="interfaces">
interface {{ interface }}
ip address {{ ip }}
</group>
</group>Functions can be applied to matched variables:
upper: Convert to uppercaselower: Convert to lowercasestrip: Remove whitespacesplit(sep): Split string by separatorjoin(sep): Join list with separatorIP: Validate IP addressMAC: Validate MAC addresscount: Count itemsrecord: Record value for path formationset: Set variable value
Multiple functions can be chained:
{{ value | upper | split(',') }}
Macros allow custom processing logic. Supported languages:
<macro name="process" language="starlark">
def process(data):
return data.upper() + "_processed"
</macro><macro name="process" language="javascript">
function process(data) {
return data.toUpperCase() + "_processed";
}
</macro>{{ value | macro('process') }}
Templates can extend other templates:
<extend template="base_template.txt" groups="common,advanced" />groups: Filter groups to includeinputs: Filter inputs to includeoutputs: Filter outputs to includevars: Filter variables to includelookups: Filter lookups to include
Source maps allow you to track which parts of the input text matched which template patterns. This is particularly useful for editor visualization and debugging. Source maps are optional and have zero overhead when disabled.
To enable source maps, use ParseWithValidation with EnableSourceMap: true:
parseResult, err := compiled.ParseWithValidation(
gottp.Inputs{"Default_Input": data},
nil, // vars
&gottp.ParseOptions{
EnableSourceMap: true,
},
)
if err != nil {
log.Fatal(err)
}
// Access parsed data
result := parseResult.Data
// Access source map
if parseResult.SourceMap != nil {
inputMap := parseResult.SourceMap.Inputs["Default_Input"]
for _, line := range inputMap.Lines {
if line.Matched {
fmt.Printf("Line %d matched\n", line.LineNumber+1)
for _, match := range line.Matches {
fmt.Printf(" Match: %s (cols %d-%d)\n",
match.GroupName, match.StartCol, match.EndCol)
fmt.Printf(" Result path: %s\n", match.ResultPath)
}
}
}
}The source map provides detailed information about matches:
SourceMap.Inputs: Map of input name to input source mapInputSourceMap.Lines: Array of line mappings, one per input lineLineMapping.Matched: Whether the line matched any patternLineMapping.Matches: Array of matches on this lineMatchMapping.StartCol/EndCol: Character range of the match (0-indexed)MatchMapping.GroupName: Name of the group that matchedMatchMapping.ResultPath: Path in result structure (e.g., "interfaces[0]")MatchMapping.Variables: Map of variable names to their character ranges
Source maps are useful for:
- Editor Visualization: Highlight which input lines matched in a text editor
- Debugging: Understand why certain lines matched or didn't match
- Error Reporting: Show users exactly which parts of their input were processed
- Interactive Tools: Enable click-to-navigate between input and output
Source maps have minimal overhead when enabled, but for maximum performance in production, leave them disabled unless needed.
One of GoTTP's key features is stateless parsing. Unlike Python TTP, you don't need to reset state between parses:
// Compile once
compiled, _ := gottp.CompileTemplate(template)
// Parse multiple times with different data
result1, _ := compiled.Parse(gottp.Inputs{"input": data1}, nil, nil)
result2, _ := compiled.Parse(gottp.Inputs{"input": data2}, nil, nil)
result3, _ := compiled.Parse(gottp.Inputs{"input": data3}, nil, nil)
// No reset needed!Compiled templates are thread-safe. For concurrent parsing:
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(data string) {
defer wg.Done()
result, _ := compiled.Parse(
gottp.Inputs{"input": data},
nil,
nil,
)
// process result
}(dataList[i])
}
wg.Wait()Use gottp-gen to embed templates at compile time:
go install github.com/roc-ops/gottp/cmd/gottp-gen@latest- Create a template file (
template.txt):
<group name="test">{{ value }}</group>
- Add a
//go:generatedirective:
//go:generate gottp-gen -template=template.txt -var=MyTemplate -format=gob- Run
go generate:
go generate- Use the embedded template:
result, err := MyTemplate.Parse(inputs, vars, nil)- Templates compiled at build time
- No runtime compilation overhead
- No need to ship template files
- Type-safe access
// Good: Compile once
compiled, _ := gottp.CompileTemplate(template)
for _, data := range dataList {
result, _ := compiled.Parse(gottp.Inputs{"input": data}, nil, nil)
}
// Bad: Compiling repeatedly
for _, data := range dataList {
compiled, _ := gottp.CompileTemplate(template) // Don't do this!
result, _ := compiled.Parse(gottp.Inputs{"input": data}, nil, nil)
}For production applications, use code generation to embed templates:
//go:generate gottp-gen -template=production.template -var=ProdTemplateAlways check for errors:
compiled, err := gottp.CompileTemplate(template)
if err != nil {
log.Fatalf("Failed to compile: %v", err)
}
result, err := compiled.Parse(inputs, vars, nil)
if err != nil {
log.Fatalf("Failed to parse: %v", err)
}vars := gottp.Vars{
"site": "datacenter1",
"region": "us-east",
}
result, _ := compiled.Parse(inputs, vars, nil)// Save compiled template
data, _ := gottp.SaveCompiledTemplate(compiled, "gob")
os.WriteFile("template.gob", data, 0644)
// Load later
data, _ := os.ReadFile("template.gob")
compiled, _ := gottp.LoadCompiledTemplate(data, "gob")See the examples/ directory for complete working examples:
basic/: Basic template parsingserialize/: Template serializationcodegen/: Code generation usagepython-api/: Python-compatible API usage
If you're migrating from Python TTP, see MIGRATION_GUIDE.md for detailed migration instructions.
See godoc for complete API documentation.