miller/pkg/dsl/cst
John Kerl 71190d3d99
plans/exit.md: record final status; defer phase 5; convert stragglers (#2207)
Phase-5 wrap-up for plans/exit.md. The DSL-runtime cluster (pkg/dsl/cst:
hofs.go, udf.go, evaluable.go) is deferred rather than converted: 58
regression cases pin the current loud-failure behavior (exact stderr plus
exit 1), and the planned error-Mlrval mechanism would make HOF/UDF misuse
fail silently as bare '(error)' with exit 0 -- a debugging-UX regression.
The alternative (typed panic recovered at the Execute* boundaries) preserves
behavior but introduces panic/recover to a codebase that has none. The
decision belongs with the issue-440 strict-mode design, where the
fatal-vs-data error taxonomy gets decided anyway; plans/exit.md now records
the rationale, the phase-by-phase status (#2198, #2202, #2204, #2205), and
the final intentional keep-list.

Also converted here, since they're squarely in prior phases' patterns rather
than the deferred expression-depth cluster:

- RootNode.ProcessEndOfStream (a phase-3 leftover on the put/filter
  Transform path) returns an error instead of printing-and-exiting on
  end-of-stream close failures; first error returned, any others printed at
  the site.
- The three CompileMillerRegexOrDie calls in option_parse.go (inside
  error-returning parser closures since phase 2) use CompileMillerRegex and
  return the error; 'mlr --ifs-regex (' output and exit code are unchanged.

All 4779 regression cases pass; make lint 0 issues.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 16:05:53 -04:00
..
assignments.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
ast_types.go Add a first-class bytes type to the DSL, with b"..." literals and base64/hex codecs (#2122) 2026-07-03 11:58:44 -04:00
block_exit.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
blocks.go Lint round 5+6: staticcheck and errcheck to zero (#2130) 2026-07-03 11:42:08 -04:00
builtin_function_manager.go Add sparkline DSL function and mlr sparkline verb (#166) (#2177) 2026-07-07 15:47:01 -04:00
builtin_function_manager_json.go Switch to --as-json (#2106) 2026-06-28 17:04:10 -04:00
builtin_functions.go Lint round 5+6: staticcheck and errcheck to zero (#2130) 2026-07-03 11:42:08 -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 Lint round 5+6: staticcheck and errcheck to zero (#2130) 2026-07-03 11:42:08 -04:00
emit1.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
emit_emitp.go Lint round 5+6: staticcheck and errcheck to zero (#2130) 2026-07-03 11:42:08 -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 Add a first-class bytes type to the DSL, with b"..." literals and base64/hex codecs (#2122) 2026-07-03 11:58:44 -04:00
filter.go Replace GOCC parser-generator with PGPG (#2015) 2026-03-15 22:28:57 -04:00
for.go Lint round 5+6: staticcheck and errcheck to zero (#2130) 2026-07-03 11:42:08 -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 Convert if/else-if chains to typed switch statements (staticcheck QF1003) (#2112) 2026-06-28 18:27:42 -04:00
keyword_usage.go Document that NF is dynamic within a record (#2192) (#2193) 2026-07-14 17:23:50 -04:00
keyword_usage_json.go Lint round 5+6: staticcheck and errcheck to zero (#2130) 2026-07-03 11:42:08 -04:00
leaves.go Add a first-class bytes type to the DSL, with b"..." literals and base64/hex codecs (#2122) 2026-07-03 11:58:44 -04:00
lvalues.go Lint round 5+6: staticcheck and errcheck to zero (#2130) 2026-07-03 11:42:08 -04:00
print.go Lint round 5+6: staticcheck and errcheck to zero (#2130) 2026-07-03 11:42:08 -04:00
README.md Update performance docs (#1991) 2026-02-22 17:04:35 -05:00
root.go plans/exit.md: record final status; defer phase 5; convert stragglers (#2207) 2026-07-15 16:05:53 -04:00
signature.go Multiple style updates (#1974) 2026-02-16 15:49:21 -05:00
statements.go Next batch of lint fixes (#2108) 2026-06-28 17:36:36 -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 Lint round 5+6: staticcheck and errcheck to zero (#2130) 2026-07-03 11:42:08 -04:00
uds.go Lint round 5+6: staticcheck and errcheck to zero (#2130) 2026-07-03 11:42:08 -04:00
validate.go Next batch of lint fixes (#2108) 2026-06-28 17:36:36 -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.