Date-time tokens were validated by two regular expressions and then tried against up to sixteen time.Parse layouts; the profile named the regexp backtracker among the hottest nodes, and the failed attempts allocated ParseErrors by the million. scanDateTimeShape walks the strict TOML grammar as bytes and dispatches one layout per shape, which time.Parse accepts because parsing takes a fractional second whether the layout signs it or not. The String methods build their output in a single buffer instead of concatenating Format results.
interpres
A TOML 1.1 parser and encoder for Go, written with the standard library
alone. interpres (Latin for interpreter) gives zero-dependency
programs an encoding/json-style API for reading and writing TOML, and passes
the entire official toml-test suite:
214 valid, 467 invalid and 214 encoder cases, zero failures.
Features
- Full TOML 1.1: bare, quoted and dotted keys; tables and arrays of
tables; basic and literal strings including multiline, with the 1.1
\eand\xHHescapes; integers in the four radixes with_separators; floats with exponents,infandnan; booleans; the four date-time kinds, seconds optional as of 1.1; arrays and inline tables, multi-line as of 1.1. - Decoding and encoding:
Parsefor an untyped tree,UnmarshalandMarshalfor structs and maps, mirroringencoding/json. - Strict decoding:
NewDecoder().DisallowUnknownFields()rejects keys that match no destination field, at every struct depth. - Custom types:
MarshalerandUnmarshalerlet a type control its own TOML representation in both directions, andencoding.TextMarshalerandTextUnmarshalerare honoured by default, sonet.IP,time.Durationand user types with text methods need no configuration. - Cancellation: every entry point has a
*Contextsibling that honours acontext.Context. - Ordered documents:
Parsegives a*Documentthat keeps the key order, tells an inline table from a header one, and carries the comments;ParseMapgives the plainmap[string]anytree. - Configurable emission:
Encoderoptions for declaration-order output, omitting empty arrays, literal multiline strings, and inlining small sub-tables.
Install
As a library:
go get sourcedock.dev/petrbalvin/interpres/v2
Requires Go 1.27.1 or newer. The module imports only the standard library.
Quick start
git clone https://sourcedock.dev/petrbalvin/interpres.git
cd interpres
just example
just example runs the tour in examples/basic: it decodes an embedded
document into a struct, prints it, and re-encodes it under both Encoder
layouts.
Usage
Decode into a struct
type Config struct {
Title string `toml:"title"`
Server struct {
Host string `toml:"host"`
Port int `toml:"port"`
} `toml:"server"`
}
var cfg Config
err := interpres.Unmarshal(data, &cfg)
Fields match by the toml:"name" tag, or by the lower-cased field name when no
tag is present; toml:"-" skips a field. Parse returns a *Document that
also carries the key order and the comments, ParseMap returns the plain
map[string]any tree, and UnmarshalContext accepts a context.
Encode from a struct
out, err := interpres.Marshal(cfg)
writes:
title = "example"
[server]
host = "127.0.0.1"
port = 9090
Tables are laid out scalars first, then sub-tables, then arrays of tables, which is the layout that re-parses to the same tree.
Strict decoding
err := interpres.NewDecoder().
DisallowUnknownFields().
Decode(data, &cfg)
A key with no matching field becomes an error instead of a silent drop.
Custom types
type Port int
func (p Port) MarshalTOML() (any, error) {
return int64(p), nil
}
type IP struct{ net.IP }
func (ip *IP) UnmarshalTOML(data any) error {
s, ok := data.(string)
if !ok {
return fmt.Errorf("ip: not a string")
}
ip.IP = net.ParseIP(s)
return nil
}
The value MarshalTOML returns is encoded in place of the receiver;
UnmarshalTOML receives the parsed value verbatim.
Encoder options
out, err := interpres.NewEncoder().
GroupByKind(false). // preserve declaration order
OmitEmptyArrays(). // skip empty scalar arrays
UseLiteralMultiline(80). // long multi-line strings as literal blocks
Marshal(cfg)
Cancellation
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
out, err := interpres.MarshalContext(ctx, cfg)
ParseContext, UnmarshalContext, (*Decoder).DecodeContext and
(*Encoder).MarshalContext follow the same pattern.
The full rules for field matching, numeric conversion and emission live in docs/API.md.
Development
just build # compile bin/interpres-decode
just test # the suite, with the 80 percent coverage floor
just toml-test # the official compliance suite, needs toml-test on PATH
just fmt # gofmt in place
See docs/DEVELOPMENT.md for the full workflow, and CONTRIBUTING.md for how to contribute.
Documentation
- docs/ARCHITECTURE.md: components and data flow
- docs/API.md: the API reference, decoding and encoding rules
- docs/CLI.md: the interpres-decode adapter and validator
Licence
MIT. See LICENSE.
Copyright © 2026 Petr Balvín