Skip to content

CyberShadow/dgotemplate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

126 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dgotemplate

dgotemplate is a D implementation of Go text/template.

Initial scope targets text/template behavior only. Go html/template is out of scope.

Compiler Status

  • Primary verified compiler: ldc2
  • Secondary verified compiler: dmd (current local matrix runs with --temp-build)

Test

dub test --compiler=ldc2

Go Fixture Corpus

tests/fixtures/go-template/go1.26.1/ contains fixture data derived from Go standard library tests in text/template/parse and text/template.

The full conformance helper currently passes the generated Go 1.26.1 fixture corpus:

lexTests:   40/40
parseTests: 108/108
errorTests: 32/32
execTests:  356/356

Regenerate fixtures with:

GO111MODULE=off go run ./tools/go-template-fixtures \
  -goroot "$(go env GOROOT)" \
  -out tests/fixtures/go-template/go1.26.1 \
  -package text/template/parse \
  -table lexTests \
  -table parseTests \
  -table errorTests

GO111MODULE=off go run ./tools/go-template-fixtures \
  -goroot "$(go env GOROOT)" \
  -out tests/fixtures/go-template/go1.26.1 \
  -package text/template \
  -table execTests \
  -exec-limit 356

Run the full generated corpus in an isolated helper worktree with:

tools/check-go-template-conformance.sh

Derived fixture data is covered by tests/fixtures/go-template/go1.26.1/LICENSE.go.

Intended API Surface

The public facade exports parse, render, parseFiles, and parseGlob.

import dgotemplate;
import std.string : toLower;

void main()
{
    struct Data
    {
        string Name;
    }

    auto data = Data("D");
    auto output = render("hello {{.Name}}", data);

    auto funcs = FunctionRegistry()
        .add("lower", (string value) => value.toLower);
    auto typed = parse("typed", "{{lower .Name}}");
    typed.funcs(funcs);
    auto lowered = typed.execute(data);

    auto templateValue = parseFiles(["layout.tmpl", "content.tmpl"]);
    auto mergedOutput = templateValue.execute(data);
}

Typed function registration keeps Go template call syntax unchanged while providing D-friendly overload helpers:

auto funcs = FunctionRegistry()
    .add("lower", (string value) => value.toLower) // replaces "lower"
    .addOverload("format", cast(string function(int)) &formatInt)
    .addOverload("format", cast(string function(string)) &formatString)
    .addOverloads!(__traits(getOverloads, mymodule, "formatAuto"))("format");

add registers one typed callable and replaces any prior entry for that name. addOverload appends one typed overload candidate, and addOverloads appends an alias sequence of D overloads. This is a D host-language convenience and does not change Go template syntax.

Limitations

Passing the generated Go fixture corpus does not mean dgotemplate is a full Go runtime or reflection model. The public API accepts native D data, while Go's template executor relies heavily on reflection, method sets, pointer/interface identity, typed nils, callable values, iterators, complex numbers, exact integer types, and fmt behavior.

The conformance fixtures include a small internal schema for Go-only concepts that plain D values do not naturally carry. Fixture decoding translates that schema into internal runtime values before execution. This is test infrastructure, not a supported input format for application code. Examples include projected methods, typed nils, typed integer metadata, complex values, slice capacity, typed maps, display overrides, and iterator/range-function sources.

Current compatibility boundaries:

  • html/template contextual escaping is not implemented.
  • Native data is adapted into template values before execution:
    • Structs and classes expose fields as template maps.
    • Arrays expose template arrays.
    • Associative-array keys are converted to strings for selector lookup.
    • Pointers are dereferenced when possible.
    • Pointer identity, interface metadata, inherited field projection, and arbitrary callable object identity are not preserved as live reflection state.
  • Field lookup:
    • D member names are used directly.
    • A trailing underscore is stripped, so name_ is visible as .name.
    • Custom field rename/ignore attributes are deferred and not currently supported.
    • Fields take precedence over same-visible-name methods and properties.
  • Compiler support:
    • ldc2 is the primary validated compiler and dmd is covered by the local temp-build matrix.
    • Broader D compiler/version range guarantees are deferred until after the pre-1.0 API and behavior surface is finalized.
  • Native methods:
    • Struct and non-null class instance methods are projected as bound template methods.
    • Supported D overloads for one visible name are projected as one template selector and resolved during execution by explicit command arity plus supported template-to-D argument conversions.
    • Niladic methods work in selector position, e.g. {{.displayName}}.
    • Methods with arguments work as command heads, e.g. {{.greet "Ada"}}.
    • Pipeline values are passed as the final method argument.
    • Unsupported overload candidates are omitted without hiding supported overloads for the same visible name.
    • Ambiguous runtime matches fail with a template execution error.
    • Statically typed interface receivers use the same method path.
    • Inherited base-class methods and implemented interface methods are supported when they pass native method filters.
    • Null class/interface references remain nil/null and do not dispatch methods.
  • D method forms not exposed:
    • Static, template, or variadic methods.
    • Methods with ref, out, or lazy parameters.
    • Setter-only properties and getter/setter property overload pairs.
    • Reserved Object names on class/interface receivers: toString, toHash, opCmp, opEquals, and factory.
  • Method conversion rules:
    • Supported method parameters are bool, string, signed/unsigned integers, and floating point values.
    • Unsupported argument or return conversions make the method unavailable or produce a template execution error.
    • Const-qualified aggregate receivers expose only const-callable methods and properties.
    • Template-callable behavior outside this supported method subset should be supplied through registered template functions.
  • print, printf, and println are backed by the standalone dgo.fmt subpackage. The formatter has its own lifted Go fmt fixture corpus and currently covers the scalar subset needed by the template builtins: booleans, strings, signed/unsigned integers, floating point values, complex display values, Go-style type names supplied by internal adapters, quoted strings/runes, selected static width/precision forms, and string byte hexadecimal output.
  • dgo.fmt is not a complete clone of Go's fmt package. Composite Go reflection formatting, dynamic width/precision with *, argument indexing, Formatter/Stringer/GoStringer-style interfaces, broad float flag coverage, and Go pointer/map/slice/struct identity formatting remain outside the supported subset. Unsupported forms produce template or formatter errors rather than approximate output.
  • Fixture-only values model Go runtime details for conformance tests. Their shape and behavior are intentionally not a stable application API; user data should be passed as native D values and registered functions.

About

D implementation of Go text/template

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors