Keep a template's table in one field, tableSite

This commit is contained in:
2026-09-29 03:30:19 +02:00
committed by Lillem4n
parent 05bc56fe75
commit e9cdfe9edd
9 changed files with 34 additions and 36 deletions
+1 -4
View File
@@ -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
}
+6 -6
View File
@@ -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)
}
+10 -8
View File
@@ -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.
+3 -3
View File
@@ -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:
+8 -7
View File
@@ -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)
}
+2 -2
View File
@@ -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 {
+2 -2
View File
@@ -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 {
+2 -2
View File
@@ -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
}
-2
View File
@@ -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