From e9cdfe9edda72f87a6ee8b0aefb630e4fec380ef Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Tue, 29 Sep 2026 03:30:19 +0200 Subject: [PATCH] Keep a template's table in one field, tableSite --- drawfence.go | 5 +---- hold.go | 12 ++++++------ node.go | 18 ++++++++++-------- readsurvey.go | 6 +++--- readsurvey_test.go | 15 ++++++++------- record.go | 4 ++-- reference.go | 4 ++-- table.go | 4 ++-- todo.md | 2 -- 9 files changed, 34 insertions(+), 36 deletions(-) diff --git a/drawfence.go b/drawfence.go index 77b1758..078b1bf 100644 --- a/drawfence.go +++ b/drawfence.go @@ -81,10 +81,7 @@ func (f *drawFence) checkDraws(path string, n node) error { // it renders, whichever draws the reaching template holds. // docs/decisions.md#a-table-never-reaches-its-own-family-by-any-route func checkOwnFamily(t *template) error { - own := t.table - if own == nil { - own = t.cell.table - } + own := t.site.table if own == nil { return nil } diff --git a/hold.go b/hold.go index 590ee5b..3bfec8b 100644 --- a/hold.go +++ b/hold.go @@ -65,12 +65,12 @@ type renderTrace func(group, table string, row int, a arm) func traceRead(trace renderTrace, t *template, sc renderScope, a arm) { var table string - var row int - switch { - case t.cell.table != nil: - table, row = t.cell.table.path, t.cell.row - case t.table != nil: - table, row = t.table.path, sc.rowOf(t.table) + row := t.site.row + if t.site.table != nil { + table = t.site.table.path + } + if row == formatRow { + row = sc.rowOf(t.site.table) } trace(strings.Clone(sc.group), strings.Clone(table), row, a) } diff --git a/node.go b/node.go index 5f6f754..28cecfb 100644 --- a/node.go +++ b/node.go @@ -54,20 +54,22 @@ type template struct { drawGroup string // the draw group it draws in, as written; "" keeps its caller's fromString bool // written as a JSON string rather than an object isRecord bool // compiled at the top without a repeat, so its fields are record columns - table *table // the table whose format this is, whose columns are the fields - cell cellSite + site tableSite // Filled by `linkTemplate`, from the assembled tree: link templateLink compiled formatOps } -// cellSite is the table cell a template is compiled from; table is nil for any other template. -type cellSite struct { +// tableSite is the table a template belongs to, as its format or one of its cells; table is nil +// for any other template. +type tableSite struct { table *table - row int + row int // the cell's row, or formatRow for the format, whose columns are the fields } +const formatRow = -1 + // templateLink is what a template resolves to in the assembled tree. type templateLink struct { refs map[string]refBinding // each reference the format reads -> what it resolves to @@ -172,7 +174,7 @@ func compileAt(v any, pos position) (node, error) { func compileItem(v any, pos position) (node, error) { switch v := v.(type) { case string: - t, err := compileString(v, cellSite{}) + t, err := compileString(v, tableSite{}) if err != nil { return nil, err } @@ -203,12 +205,12 @@ func jsonKind(v any) string { return fmt.Sprintf("%T", v) } -func compileString(s string, cell cellSite) (*template, error) { +func compileString(s string, site tableSite) (*template, error) { toks, err := parseChecked(s, nil) if err != nil { return nil, err } - return &template{format: s, tokens: toks, repeat: 1, fromString: true, cell: cell}, nil + return &template{format: s, tokens: toks, repeat: 1, fromString: true, site: site}, nil } // fixedText is one render's output when the format holds no token; repeat is the caller's. diff --git a/readsurvey.go b/readsurvey.go index 0b01e8a..fb43df6 100644 --- a/readsurvey.go +++ b/readsurvey.go @@ -114,9 +114,9 @@ func (s *readSurvey) walk(n node, at surveyAt) { } for _, e := range renderEdges(n) { to, isTemplate := e.to.(*template) - cell := cellSite{} - if isTemplate { - cell = to.cell + cell := tableSite{} + if isTemplate && to.site.row != formatRow { + cell = to.site } switch { case cell.table == nil: diff --git a/readsurvey_test.go b/readsurvey_test.go index 0c39001..07d6186 100644 --- a/readsurvey_test.go +++ b/readsurvey_test.go @@ -80,13 +80,14 @@ func renderRoot(s *session, t *template, trace renderTrace) { set := holdSet{trace: trace} sc := renderScope{set: &set} switch { - case t.table != nil: - render(s, t.table, sc) - for r := 0; r < t.table.rowCount(); r++ { + case t.site.row == formatRow: + tbl := t.site.table + render(s, tbl, sc) + for r := 0; r < tbl.rowCount(); r++ { set := holdSet{trace: trace} sc := renderScope{set: &set} - sc.groupHold().pins.pin(t.table, r) - render(s, t.table.pinnedRow, sc.at(t.table.pinnedRow, &sc.groupHold().pins)) + sc.groupHold().pins.pin(tbl, r) + render(s, tbl.pinnedRow, sc.at(tbl.pinnedRow, &sc.groupHold().pins)) } case t.repeat > 1: expand(s, t, sc) @@ -98,8 +99,8 @@ func renderRoot(s *session, t *template, trace renderTrace) { func renderRecordRoot(s *session, t *template, columns []Column, trace renderTrace) { set := holdSet{trace: trace} sc := renderScope{set: &set} - if t.table != nil { - t.table.drawIn(s, &sc.groupHold().pins) + if t.site.row == formatRow { + t.site.table.drawIn(s, &sc.groupHold().pins) } renderRecord(s, t, columns, sc) } diff --git a/record.go b/record.go index d0b4a79..4b4c666 100644 --- a/record.go +++ b/record.go @@ -263,8 +263,8 @@ func recordOf(n node) (*template, []Column, error) { // over sc's hold; a table's columns read the row pinned there. func renderRecord(s *session, t *template, columns []Column, sc renderScope) *Record { sc = sc.in(t) - if t.table != nil { - sc = sc.at(t.table.pinnedRow, &sc.groupHold().pins) + if t.site.row == formatRow { + sc = sc.at(t.site.table.pinnedRow, &sc.groupHold().pins) } r := &Record{columns: append([]Column(nil), columns...)} for i := range r.columns { diff --git a/reference.go b/reference.go index 0563604..e218229 100644 --- a/reference.go +++ b/reference.go @@ -81,8 +81,8 @@ func linkRefs(root map[string]node) error { // linkTemplate resolves t's references in category, "" for an inline template, and // compiles its format against them. func linkTemplate(folder []string, path, category string, t *template, root map[string]node) error { - if t.cell.table != nil { - path = fmt.Sprintf("%s, line %d", path, t.cell.row+2) + if t.site.table != nil && t.site.row != formatRow { + path = fmt.Sprintf("%s, line %d", path, t.site.row+2) } link, err := t.resolveLink(folder, path, category, root) if err != nil { diff --git a/table.go b/table.go index d407d26..0d41a73 100644 --- a/table.go +++ b/table.go @@ -308,7 +308,7 @@ func (t *table) checkCells() error { if strings.IndexByte(cell, '{') < 0 && strings.IndexByte(cell, '}') < 0 { continue } - n, err := compileString(cell, cellSite{t, row}) + n, err := compileString(cell, tableSite{t, row}) if err != nil { return fmt.Errorf("line %d, %s: %w", row+2, t.header[col], err) } @@ -341,7 +341,7 @@ func (t *table) compileRowFormat(format string) error { if err := checkTokens(toks, t.fields); err != nil { return err } - t.formatTemplate = &template{format: format, tokens: toks, fields: t.fields, repeat: 1, isRecord: true, table: t} + t.formatTemplate = &template{format: format, tokens: toks, fields: t.fields, repeat: 1, isRecord: true, site: tableSite{t, formatRow}} t.pinnedRow = &tableRow{t} return nil } diff --git a/todo.md b/todo.md index 158eaaf..703bb27 100644 --- a/todo.md +++ b/todo.md @@ -26,8 +26,6 @@ least modify. This round, in order: -- Keep a template's table in one field: `template.table` and `template.cell.table` both - answer which table it belongs to, and `checkOwnFamily` and `traceRead` branch on both. - Stop `readField` dereferencing a nil `held`: `renderLeaf` passes nil, safe only because a `readsColumn` arm is a reference path with a tail, which nothing there says. - Test that `disagreements` holds every key `disagreement` can build, and panic through