* refine the plan
* Fix all staticcheck lint findings (uncapped)
golangci-lint's default max-same-issues=3 was hiding most of the backlog:
the true pre-fix count was 69 staticcheck findings, not 34. This fixes all
of them, driving staticcheck to zero:
- ST1023/QF1011 (37): omit explicit types inferred from the RHS
- S1009/S1031 (15): drop redundant nil checks before len()/range
- SA9003 (9): remove comment-only empty branches, keeping the comments
- QF1007 (3): merge conditional assignment into declaration
- QF1006 (3): lift break conditions into loop conditions
- QF1001 (3): apply De Morgan's law / name the negated predicate
Also updates plans/lintfixes.md with the cap discovery and the corrected
errcheck picture (1202 uncapped, ~949 of them fmt.Fprint*).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Drive errcheck to zero: config for bulk categories, propagate real errors
Adds .golangci.yml with errcheck exclude-functions for fmt.Fprint* (usage
printers), (*bufio.Writer).Write/WriteString (sticky errors, surfaced at the
now-checked final Flush), and (*strings.Builder).WriteString; pins
max-issues-per-linter/max-same-issues to 0 so CI reports true counts.
Real error paths now propagate instead of being dropped:
- Finalize{Reader,Writer}Options in join/put/filter/split/tee and the
repl/script entry points: 'mlr join -i badformat' now errors instead of
silently using wrong separators
- final output-stream Flush in pkg/stream: write failure no longer exits 0
- DSL emit/print/dump redirect writes, matching their sibling branches
- CSV writer WriteCSVRecordMaybeColorized, close-time Flush in file output
handlers, ENV[...] Setenv, REPL record-write and redirect-close errors
- termcvt write-side Close before rename (had "TODO: check return status")
The rest are deliberate ignores, marked with _ = and a comment where the
reason isn't obvious: unset-of-missing-path no-ops, read-side closes,
mid-stream FlushOnEveryRecord, init-time strftime registrations, in-memory
usage-capture pipes, and regtest-harness env/temp-file teardown.
golangci-lint now reports 0 issues on ./cmd/mlr ./pkg/... with all caps off.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|---|---|---|
| .. | ||
| cst | ||
| ast_types.go | ||
| doc.go | ||
| README.md | ||
Parsing a Miller DSL (domain-specific language) expression goes through three representations:
- Source code which is a string of characters.
- Abstract syntax tree (AST)
- Concrete syntax tree (AST)
The job of the PGPG parser is to turn the DSL string into an AST.
The job of the CST builder is to turn the AST into a CST.
The job of the put and filter transformers is to execute the CST statements on each input record.
Source-code representation
For example, the part between the single quotes in
mlr put '$v = $i + $x * 4 + 100.7 * $y' myfile.dat
AST representation
Use put -v to display the AST:
mlr -n put -v '$v = $i + $x * 4 + 100.7 * $y'
RAW AST:
* StatementBlock
* SrecDirectAssignment "=" "="
* DirectFieldName "md_token_field_name" "v"
* Operator "+" "+"
* Operator "+" "+"
* DirectFieldName "md_token_field_name" "i"
* Operator "*" "*"
* DirectFieldName "md_token_field_name" "x"
* IntLiteral "md_token_int_literal" "4"
* Operator "*" "*"
* FloatLiteral "md_token_float_literal" "100.7"
* DirectFieldName "md_token_field_name" "y"
Note the following about the AST:
- Parentheses, commas, semicolons, line endings, whitespace are all stripped away
- Variable names and literal values remain as leaf nodes of the AST
- Operators like
=+-*/**, function names, and so on remain as non-leaf nodes of the AST - Operator precedence is clear from the tree structure
Operator-precedence examples:
$ mlr -n put -v '$x = 1 + 2 * 3'
RAW AST:
* StatementBlock
* SrecDirectAssignment "=" "="
* DirectFieldName "md_token_field_name" "x"
* Operator "+" "+"
* IntLiteral "md_token_int_literal" "1"
* Operator "*" "*"
* IntLiteral "md_token_int_literal" "2"
* IntLiteral "md_token_int_literal" "3"
$ mlr -n put -v '$x = 1 * 2 + 3'
RAW AST:
* StatementBlock
* SrecDirectAssignment "=" "="
* DirectFieldName "md_token_field_name" "x"
* Operator "+" "+"
* Operator "*" "*"
* IntLiteral "md_token_int_literal" "1"
* IntLiteral "md_token_int_literal" "2"
* IntLiteral "md_token_int_literal" "3"
$ mlr -n put -v '$x = 1 * (2 + 3)'
RAW AST:
* StatementBlock
* SrecDirectAssignment "=" "="
* DirectFieldName "md_token_field_name" "x"
* Operator "*" "*"
* IntLiteral "md_token_int_literal" "1"
* Operator "+" "+"
* IntLiteral "md_token_int_literal" "2"
* IntLiteral "md_token_int_literal" "3"
CST representation
There's no -v display for the CST, but it's simply a reshaping of the AST
with pre-processed setup of function pointers to handle each type of statement
on a per-record basis.
The if/else and/or switch statements to decide what to do with each AST node are done at CST-build time, so they don't need to be re-done when the syntax tree is executed once on every data record.
Source directories/files
- The AST logic is in
./ast*.go. I didn't use apkg/dsl/astnaming convention, although that would have been nice, in order to avoid a Go package-dependency cycle. - The CST logic is in
./cst. Please see cst/README.md for more information.