From 338358c70e542fd46fda1ca89cdd20a398d15fcf Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Mon, 5 Oct 2026 22:42:30 +0200 Subject: [PATCH] Answer prose round 1: plainer linkTemplates, ownScope, resolveReads, compileArm and settleRecords docs, recordOf naming its three entry points, a plainer engine decision, and item 117 read as a path's last step --- docs/decisions.md | 8 ++++---- format.go | 6 +++--- link.go | 10 +++++----- name.go | 12 ++++++------ namefence.go | 6 +++--- node.go | 2 +- record.go | 8 ++++---- todo.md | 4 ++-- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/decisions.md b/docs/decisions.md index f189d89..2c1c40f 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -827,16 +827,16 @@ Valid while names are read only inside the category binding them. ## comprehension floor: every dimension and the overall at 7.0 or above; item 5 scores the restructure, which ships unscored: every `{…}` drawing afresh, the moves into `internal/`, cutting a template's pointer to its table, the move of a table's rows into `internal/rows`, and linking in steps that each return what they build -2026-10-04, Lilleman auf Larv. Serves goal 3. The nine-seat panels in `todo.md`'s git history, 2026-09-22 to 2026-09-30, held the score between 5.2 and 6.1, with Locality lowest at every seat. The hardest units were the draw fences, which the change making every `{…}` draw afresh deleted. The restructure deletes or moves what the earlier chunks of the below-floor round touched. It is three things: the change making every `{…}` draw afresh; the moves of the draw state, the grammar, `DataType`, what a proof knows of a value, the builtins and the reading of data files into `internal/`; and the untangling of the engine's own structs, which is the change cutting a template's pointer to its table, the move of a table's rows into `internal/rows`, and linking in steps that each return what they build, with a name scope created beside its owner and each record's columns settled at load. The maintainer approved these three, and `AGENTS.md`'s comprehension rule lets them merge below the floor. A scoring run before the link restructure merged would have scored code about to go; for that reason the calc change accepting a name as an operand shipped unscored on 2026-10-05. No scoring run or panel runs until item 5, which runs the nine-seat panel and larv-review's scoring run. The scoring run writes this entry's first scores. Valid until `todo.md` item 5 runs. +2026-10-04, Lilleman auf Larv. Serves goal 3. The nine-seat panels in `todo.md`'s git history, 2026-09-22 to 2026-09-30, held the score between 5.2 and 6.1, with Locality lowest at every seat. The hardest units were the draw fences, which the change making every `{…}` draw afresh deleted. The restructure deletes or moves what the earlier chunks of the below-floor round touched. It is three things: the change making every `{…}` draw afresh; the moves of the draw state, the grammar, `DataType`, what a proof knows of a value, the builtins and the reading of data files into `internal/`; and the untangling of the engine's own structs, which is the change cutting a template's pointer to its table, the move of a table's rows into `internal/rows`, and linking in steps that each return what they build, with a name scope created beside its owner and each record's columns settled at load. The maintainer approved these three, and `AGENTS.md`'s comprehension rule lets them merge below the floor. A scoring run before linking in steps merged would have scored code about to go; for that reason the calc change accepting a name as an operand shipped unscored on 2026-10-05. No scoring run or panel runs until item 5, which runs the nine-seat panel and larv-review's scoring run. The scoring run writes this entry's first scores. Valid until `todo.md` item 5 runs. ## The template engine stays the root package until item 107; what reads no engine type, a table's rows included, sits in `internal/` 2026-10-05, Lilleman auf Larv. Serves goals 3.2 and 3.4. Two architect reviews found the engine's types, `template`, `table`, `arm`, `op` and `nameBinding`, bound in one cycle because several passes each write part of the same structs; the template language's -recursion reads few of those types. Moving the engine before the link gave each value -one writer would have spread that knot across packages, behind forwarding calls from every -public method. The draw state, the grammar, `DataType`, what a proof knows of a +recursion reads few of those types. Moving the engine while several link passes still +wrote each value would have spread that knot across packages, behind forwarding calls from +every public method. The draw state, the grammar, `DataType`, what a proof knows of a value, the builtins, the reading of data files and a table's rows read no engine type, so each sits in a package under `internal/`, behind the import list `imports_test.go` checks. A rows table holds the engine table owning it as a value of a type parameter, so diff --git a/format.go b/format.go index c08d4cd..8b61cf0 100644 --- a/format.go +++ b/format.go @@ -83,7 +83,7 @@ func checkReads(t grammar.Token, fields map[string]node, operands bool) ([]unbou if _, _, err := grammar.RefShape(name); err != nil { return nil, fmt.Errorf("token {%s}: %w", t.Body, err) } - continue // its target is checked at New (see linkCategories) + continue // resolveLink checks its target once the tree is assembled } missing, err := checkArm(name, fields, !operands && len(names) == 1) if err == nil { @@ -316,8 +316,8 @@ func (t *template) compileArms(names []string, targets map[*nameBinding]nameTarg return arms, nil } -// compileArm compiles one read: a path from the head it names, or from what the name it reads -// through binds. +// compileArm compiles one read into a path: from the head it names, or, for a read through a +// name, from what that name binds. func (t *template) compileArm(name string, targets map[*nameBinding]nameTarget) (arm, error) { a := splitArm(name, t.link.refs) if !t.isName(a.head) { diff --git a/link.go b/link.go index 8b0f374..9defa78 100644 --- a/link.go +++ b/link.go @@ -7,7 +7,7 @@ import ( "github.com/larvit/fejkdata/internal/grammar" ) -// linkSite is a template to link, with the folder and category it sits in, "" for an inline +// linkSite is a template to link, with its folder and category, both empty for an inline // template, and eachNode's label for it. type linkSite struct { t *template @@ -61,10 +61,10 @@ type linkedNames struct { uses map[*nameBinding][]nameUse } -// linkTemplates links ts in steps, each over every template before the next starts, each -// returning what it builds: the references and the compiled formats, kept on each template, the -// names' targets, keys and reads, passed to the steps after as arguments, and the column each -// format reads. The bindings take their targets and keys once every check has passed. +// linkTemplates links ts in steps, each over every template before the next starts. Each step +// returns what it builds. The references, the compiled format and the column read are kept on each +// template. The names' targets, keys and reads pass to later steps as arguments, and the bindings +// take their targets and keys only once every check has passed. func linkTemplates(ts []linkSite, root map[string]node) error { for _, s := range ts { link, err := s.t.resolveLink(s.folder, s.label, s.category, root) diff --git a/name.go b/name.go index 87c7071..0dc0af5 100644 --- a/name.go +++ b/name.go @@ -141,8 +141,8 @@ func (sc *nameScope) spelled() string { return "in one repeat" } -// ownScope is the scope t renders a frame of, nil where that scope binds no name: a category's, -// on its root, or a repeat's, per iteration. +// ownScope is the scope t renders a frame of: a category's, once per render of its root, or a +// repeat's, once per iteration. It is nil where t owns no scope, or its scope binds no name. func (t *template) ownScope() *nameScope { if sc := t.nameScope; sc != nil && sc.owner == node(t) && len(sc.order) > 0 { return sc @@ -165,10 +165,10 @@ func eachContained(n node, where string, fn func(c node, where string) error) er return nil } -// resolveReads answers each unbound read under root with a name its template sees, fields before -// the format as compile reports them, returning every binding read, and refuses a field spelling a -// name. scopes are every scope of the category, which a refusal searches for a name bound where -// the read cannot see it. +// resolveReads answers each unbound read under root with a name its template sees, and returns +// the set of bindings some read answers to. It refuses a field spelling a name, fields before the +// format, as compile reports them. A refusal searches scopes, every scope of the category, for a +// name bound where the read cannot see it. func resolveReads(root node, scopes []*nameScope) (map[*nameBinding]bool, error) { read := map[*nameBinding]bool{} var resolve func(n node, where string) error diff --git a/namefence.go b/namefence.go index 6363cc1..e097935 100644 --- a/namefence.go +++ b/namefence.go @@ -64,9 +64,9 @@ func rendersInside(nodes []node, t *template) bool { } // checkUses refuses a binding whose uses read it once at a spot the bound spelling can stand: -// that spelling draws the same way without the name. A bound field can stand in only where the reading -// template reaches the binder through fields. Where the spelling would be a CLI argument or tag -// of one reference alone, that entry point's own refusal then names the bare path. +// that spelling draws the same way without the name. A bound field can stand in only where the +// reading template reaches the binder through fields. Where the spelling would be a CLI argument +// or tag of one reference alone, that entry point's own refusal then names the bare path. func (b *nameBinding) checkUses(uses []nameUse) error { if len(uses) == 0 { panic(invariant.Broken("name %q has no read at link, though its compile found one", b.name)) diff --git a/node.go b/node.go index 26b1a69..fe151c6 100644 --- a/node.go +++ b/node.go @@ -66,7 +66,7 @@ type template struct { // Filled by `linkTemplates`, from the assembled tree: link templateLink compiled formatOps - readsColumn *columnRead // set when the format is one reference or name read alone reading a record's column + readsColumn *columnRead // set when the format only reads one reference or name, and that read is a record's column // Filled by `settleRecords`, once the load refused every cycle: columns []recordColumn // a record's, where it has fields, in name order diff --git a/record.go b/record.go index 33be385..5830488 100644 --- a/record.go +++ b/record.go @@ -214,8 +214,8 @@ func (f *Generator) FakeRecordTemplate(input string) (*Record, error) { // the record to write instead. var ErrNoColumns = errors.New("has no fields, so no columns") -// recordOf is the fence both record entry points pass: the record template n is, whose columns -// settleRecords fixed at load for every draw. +// recordOf is the record template n is, the fence FakeRecord, NewRecordTemplate and FakeStruct +// pass; settleRecords fixed its columns at load. func recordOf(n node) (*template, error) { if tb, isTable := n.(*table); isTable { n = tb.formatTemplate @@ -246,8 +246,8 @@ type recordColumn struct { boundWhole *nameBinding } -// settleRecords fixes the columns of every record in scope, whose datatypes a cycle would walk -// forever, so bind refuses the scope's cycles first. +// settleRecords fixes the columns of every record in scope. bind runs it after checkNoCycles: a +// column's datatype walks the column, and a cycle would walk forever. func settleRecords(scope nodeScope) { _ = scope(func(_ string, n node) error { if t, isTemplate := n.(*template); isTemplate { diff --git a/todo.md b/todo.md index 31b48fd..fa0e830 100644 --- a/todo.md +++ b/todo.md @@ -85,7 +85,7 @@ | 88 | 0.2.0 | | **Add a 12-digit `YYYYMMDDNNNC` form of `sv_SE.personnummer` and `sv_SE.samordningsnummer`, the form Skatteverket's testpersonnummer series and many systems store.** | 2 | 3 | 6 | 6 | 4 | 19.4 | | 41 | 0.2.0 | decision | **Key every table by one key column, as the decision "A table selects by one key column, a code, never a free-form name" states.** | 6 | 7 | 7 | 8 | 5.5 | 19.1 | | 52 | 0.2.0 | | **Name the file a link error comes from beside its category path, `sub.x (/d/a/sub/x.json): …`.** | 1 | 2 | 5 | 7 | 6 | 19.0 | -| 117 | 0.2.0 | | **List the fields a category has when a path names one it lacks: `{/word as a}{a.zz}` reports `no field "zz"; word has w`.** | 1 | 2 | 5 | 7 | 6 | 19.0 | +| 117 | 0.2.0 | | **List the fields a path's last step reached when it names one that step lacks: `{/word as a}{a.zz}` reports `no field "zz"; word has w`.** | 1 | 2 | 5 | 7 | 6 | 19.0 | | 28 | 0.2.0 | decision | **Accept a middle name, and draw a `personnummer` whose sex matches a sex selected through a named pick.** | 4 | 4 | 6 | 7 | 4.1 | 18.9 | | 24 | 0.2.0 | defect | **Stop `misc.territory[EH].tld` rendering `.eh`, the one shipped TLD `misc.tld` does not hold.** | 1 | 1 | 4 | 7 | 4 | 18.6 | | 92 | 0.2.0 | | **Name the hyphenated field in a `calc`'s refusal, and say to rename it: today `{calc(postal-code * 2)}` reports `no field "postal"`.** | 1 | 2 | 5 | 6 | 6 | 17.5 | @@ -304,7 +304,7 @@ A system that stores the 12-digit form never meets the `+`, so it needs a fixtur With several `--data-path` layers the author has to work out which directory won, while a parse error on the same file names it. -### 117. List the fields a category has when a path names one it lacks: `{/word as a}{a.zz}` reports `no field "zz"; word has w`. +### 117. List the fields a path's last step reached when it names one that step lacks: `{/word as a}{a.zz}` reports `no field "zz"; word has w`. Today the refusal names only the missing field, so a data author fixing a typo opens the category to find the spelling. Item 92 covers the hyphenated `calc` operand only.