miller/pkg/dsl/cst
John Kerl 299636038c
Shell tab-completion for bash and zsh (#2096)
* initial attempt

* fix bash

* fix zsh

* Add shell-completion docs page

Documents the new 'mlr completion {bash,zsh}' feature: the then-chain
context model, install instructions for bash and zsh (including the macOS
bash-3.2 'eval' caveat and zsh compinit self-init), and examples of
context-aware completion. Added to the nav under "Miller in more detail".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Add enum value completion for format and separator flags

Completes the argument value for arg-taking main flags whose values are a
known set: file-format names for -i/-o/--io, separator aliases for
--ifs/--ofs/--ips/etc., and regex-separator aliases for --ifs-regex/--ips-regex.
Other arg-taking flags continue to fall back to filename completion.

Candidate sets come from new cli getters (GetFileFormatNames,
GetSeparatorAliasNames, GetSeparatorRegexAliasNames) that read the same maps
Miller uses at runtime, so there is no separate list to keep in sync. The
command-line walk now records which flag a value position belongs to.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Include format-conversion keystroke-savers in bare-dash completion

Reverts the suppression of --c2j/--x2y-style flags from 'mlr -<TAB>'. The full
set of main flags (297) is now offered, matching what is valid on the command
line. GetFlagNames no longer takes an includeSuppressed argument.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Complete terminal subcommands and top-level help/version flags

'mlr <TAB>' now offers subcommand names (help, version, repl, regtest, script,
completion, terminal-list) alongside verb names, and 'mlr -<TAB>' offers the
top-level terminal flags (-h, --help, --version, --bare-version, and the help
shorthands -g/-l/-L/-f/-F/-k/-K). Subcommand names are offered only as the
first non-flag token, where they are valid.

To let the completion engine know these names without an import cycle
(pkg/terminals imports pkg/terminals/completion), the canonical terminal names
and version-flag spellings are factored into a new leaf package
pkg/terminals/registry, imported by pkg/terminals, pkg/climain, and completion.
The help-flag spellings come from a new help.GetTerminalFlagNames derived from
the existing shorthand table, so nothing drifts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Complete 'mlr help' topics and topic arguments

'mlr help <TAB>' now completes help topics (flags, verb, function, keyword,
list-verbs, ...), and topics that take a name argument complete it too:
'mlr help verb <TAB>' -> verb names, 'mlr help function <TAB>' -> function
names, 'mlr help keyword <TAB>' -> keyword names, 'mlr help flag <TAB>' ->
flag names. 'mlr completion <TAB>' completes bash/zsh.

A terminal subcommand consumes the rest of the command line, so the walk now
returns a ctxTerminalArgs context carrying the terminal name and the words
typed after it. New getters supply the candidate names without drift:
help.GetTopicNames, help.GetFunctionNames/GetKeywordNames (wrapping new
cst.BuiltinFunctionManager.GetBuiltinFunctionNames and cst.GetKeywordNames).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs-neaten

* Move flag-value-candidate logic into pkg/cli; fix verb-flag collision

The mapping of which flags take a format/separator/regex-separator argument is
flag metadata, so it now lives with the flags in pkg/cli as
cli.FlagValueCandidates, alongside the existing GetFileFormatNames /
GetSeparatorAliasNames getters, replacing the maps that were in
pkg/terminals/completion/value_completion.go (now removed).

This also fixes a bug: value completion now applies only to main flags, not to
identically-spelled verb flags. Previously 'mlr uniq -o <TAB>' offered file
formats because uniq's -o (an output field name) collided with the main -o
format flag; it now correctly falls back to filename completion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 12:47:54 -04:00
..
assignments.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
ast_types.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
block_exit.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
blocks.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
builtin_function_manager.go Shell tab-completion for bash and zsh (#2096) 2026-06-21 12:47:54 -04:00
builtin_functions.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
collections.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
cond.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
doc.go Export library code in pkg/ (#1391) 2023-09-10 17:15:13 -04:00
dump.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
emit1.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
emit_emitp.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
emitf.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
env.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
evaluable.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
filter.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
for.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
functions.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
hofs.go Experiment with mlr script (#2009) 2026-03-05 20:14:32 -05:00
if.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
keyword_usage.go Shell tab-completion for bash and zsh (#2096) 2026-06-21 12:47:54 -04:00
leaves.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
lvalues.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
print.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
README.md Update performance docs (#1991) 2026-02-22 17:04:35 -05:00
root.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
signature.go Multiple style updates (#1974) 2026-02-16 15:49:21 -05:00
statements.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
subroutines.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
tee.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
types.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
udf.go Drop redundant deep-copy of UDF return values (~3-16% perf on UDFs in mlr put) (#2089) 2026-06-19 17:05:16 -04:00
uds.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
validate.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
warn.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
while.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00

See ../dsl/README.md for more information about Miller's use of abstract syntax trees (ASTs) and concrete syntax trees (CSTs) within the Miller put/filter domain-specific language (DSL).

Files

  • types.go is a starting point for seeing datatypes involved in the concrete syntax tree.
    • IExecutable is the interface for executable nodes, such as assignment statements, or statement blocks (if-bodies, etc.).
    • IEvaluable is the interface for evaluable expressions (e.g. right-hand sides of assignment statements).
  • root.go contains the top-level logic for building a CST from an AST at parse time (cstRoot, err := cst.Build(astRoot)), as well as executing the CST on a per-record basis (cstRoot.Execute(runtimeState)). See also the put transformer.

Notes

Go is a strongly typed language, but the AST is polymorphic. This results in if/else or switch statements as an AST is walked.

Also, when we modify code, there can be changes in the BNF grammar not yet reflected in the AST. Likewise, there can be AST changes not yet reflected here. (Example: you are partway through adding a new binary operator to the grammar.)

As a result, throughout the code, there are error checks which may seem redundant but which are in place to make incremental development more pleasant and robust.

During CST build from an AST, one starts from the AST root and walks down through the nodes of the AST. Within a caller method, there is an if/else or switch statement on the AST node type. (Example: is this a leaf node, like the string literal "abcd", int literal 3, field-name $x? Or a binary operator like +, or function call like cos?).

Different builder methods are invoked for leaves, operators, etc. There is also, redundantly, a precondition assertion within each builder method: the leaf-builder method checks to make sure it's given an AST leaf node to build from; the operator-builder method checks to make sure it's given an AST operator node to build from; etc. The caller and the callee are doing the same kinds of check, which might seem redundant. The callees return Go error in case there is something new in the caller which has not yet been implemented in the callee.

This is all done to make development more happy: when you see things like CST build: AST unary operator node unhandled you can check the code here and see what you need to do next to continue development.