Support multiple directives per row in CSV import - #141
Merged
Conversation
A CSV row sometimes needs to produce more than one directive — a transaction plus a running-balance assertion — or none at all when a row should be conditionally suppressed. The previous (emit X) accepted exactly one directive, so these shapes were inexpressible even though the underlying csvbase pipeline already returns a slice of directives per row. Make emit variadic: (emit d1 ... dn) emits one directive per non-nil argument, in order. To gate individual slots without an else branch, add (when cond X) / (unless cond X), which yield X's directive or a nil directive (a skipped slot). Together these cover the realized cases — one directive (unchanged), an optional second, a conditional skip, or a fixed group — while keeping the common one-directive form identical. Design intent: the directive count stays statically determined, which fits this DSL's compile-time, fixed-shape-per-row model. A first-class directive-list value or Lisp-style cons cells were considered and rejected: with static counts they add machinery without expressive gain, and cons does not fit the strongly-typed value model. A soft-fail in any emit slot drops the whole row rather than emitting a partial set, since a half-built multi-directive group could leave the ledger inconsistent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HCxDfeymPeAmaqg4KpuoEu
EmitDirective emitted exactly one directive — the single-key case of EmitDirectives, which now exists. Keeping both is redundant surface area for the same terminal, so drop the singular form and let callers that emit one directive pass one key. EmitTx is retained: it accepts a transaction key directly and stays the ergonomic terminal for the common single-transaction shape without an AsDirective lift. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HCxDfeymPeAmaqg4KpuoEu
main added the (cond ...) multi-way conditional to csvsexp (#140); this branch added variadic (emit ...) plus (when ...)/(unless ...). Both extend the same dispatch, doc sections, and golden shape list, so the resolution keeps every new form and testdata case side by side. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HCxDfeymPeAmaqg4KpuoEu
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Enable CSV import programs to emit multiple directives from a single row, and conditionally emit directives based on row data.
Summary
This change extends the
emitform in csv-sexp to accept zero or more directive arguments (previously exactly one), allowing a single CSV row to produce multiple directives in order. It also introduceswhenandunlessforms for conditional directive emission, enabling optional balance assertions, multi-entry rows, and row-level filtering without explicitifbranches.Key Changes
Variadic emit:
(emit X Y Z)now emits directives from X, Y, and Z in order. Nil-valued directives (skipped slots) contribute nothing. If any argument soft-fails, the entire row is dropped with diagnostics, preventing partial directive sets that could leave the ledger inconsistent.Conditional directives: New
(when cond X)and(unless cond X)forms gate a single directive, yielding X's directive when the condition holds (or fails forunless) and a nil directive otherwise. These compose directly with variadic emit for optional secondary entries or row suppression.NilDirective and EmitDirectives: Added
csvbase.NilDirective()to represent a skipped emit slot andcsvbase.EmitDirectives(keys...)to handle variadic emission with atomic row semantics (all-or-nothing on soft-fail).Compiler support: Updated
csvsexpcompiler to parse multiple emit arguments, evaluate them as directive keys, and compilewhen/unlesswith both literal and dynamic conditions. Literal conditions fold at compile time; dynamic conditions usecsvbase.Ifto select between the directive and a nil directive.Testing
Added comprehensive test coverage:
TestEmitMultiple: Two directives from one row (transaction + balance)TestEmitWhenOptionalSecond: Optional balance gated by conditionTestEmitWhenSkipsRow: Row suppression when condition failsTestEmitUnless: Inverted condition semanticsTestEmitSoftFailDropsRow: Atomic row semantics on soft-failTestEmitDirectives_*: Builder-level tests for variadic emission and nil handlingmultidirective: Real-world example with optional balance assertionsDocumentation
Updated doc.go to clarify emit's new variadic behavior, nil semantics, and the role of
when/unlessin conditional emission without explicit else branches.https://claude.ai/code/session_01HCxDfeymPeAmaqg4KpuoEu