Brigadier
The command tree, as the GADT it is
Released —
v1.0.0.goforge.dev/gbrigadieris a Go+ reimplementation ofMojang/brigadier— the command parser and dispatcher behind Minecraft — at feature parity across all 44 classes, consumingstd/parsecfor its scanning core. Install withgo get goforge.dev/gbrigadier@v1.0.0.
GoForge Brigadier targets Mojang/brigadier (majorMinor 1.3, pinned at commit
9ba4f13). It registers, parses, executes, and suggests completions for tree-
structured commands, generic over a caller-defined source type S.
Parity
Verified against brigadier’s own JUnit suite, ported to Go: 170 test functions
pass — reader, all six argument types, the command tree, builders, context,
the full dispatcher (execute / parse / redirect / fork / usage / ambiguities),
and suggestions — race-, vet-, and goplus gen -check clean.
One documented ledger (a formatting divergence, not a semantic gap): legacy
Java’s non-shortest Float.toString spells the Integer.MIN_VALUE float bound
-2.14748365E9 where Go/Ryū emits the shortest round-tripping decimal
-2.1474836E9; both parse back to the identical float32. Every other
toString is byte-identical.
The command tree is a GADT
brigadier’s command tree is a genuine GADT, and this rewrite models it as one —
the reason it was chosen to exercise Go+’s GADT and dependent machinery.
ArgumentType[T] indexes the value a node parses; ArgumentCommandNode[S, T]
keeps T yet satisfies the non-generic CommandNode[S] interface, so T
erases exactly at brigadier’s ArgumentCommandNode<S, ?> boundary — no
manual boxing. The context holds a heterogeneous, string-keyed bag of parsed
arguments, and GetArgument[T](ctx, name) recovers the value type-safely — the
dependent retrieval Java performs with an unchecked cast.
Go+’s existing GADT/dependent machinery carried the whole design — the
generic-over-S tree, the T-erasure, and the typed retrieval all generate and
type-check with no compiler extension required. brigadier is thus a
validation that the machinery holds up on a real, industrial GADT, not a toy.
std/parsec, load-bearing
StringReader’s character-run scans — numbers, unquoted strings, whitespace —
run through parsec.TakeWhile, turning brigadier’s imperative
while canRead && pred loops into single combinator applications. The parser
core does real work rather than standing in as decoration.
Faithful adaptations
Everything is generic over S, as upstream. Java checked
CommandSyntaxException becomes Go (T, error) with pointer-identity exception
singletons so error-type comparison matches. CompletableFuture<Suggestions>
becomes a synchronous *Suggestions (all upstream providers are synchronous).
And Suggestions.create’s sort reproduces the JDK’s TimSort binary-insertion
path so brigadier’s inconsistent-comparator ordering of mixed integer/string
suggestions is byte-identical.
Provenance
Upstream: Mojang/brigadier, MIT
(© Microsoft). Independent Go+ implementation, not vendored source;
.upstream/COMMIT pins the exact upstream commit parity was verified against.