Keep a template's table in one field, tableSite
This commit is contained in:
+1
-4
@@ -81,10 +81,7 @@ func (f *drawFence) checkDraws(path string, n node) error {
|
|||||||
// it renders, whichever draws the reaching template holds.
|
// it renders, whichever draws the reaching template holds.
|
||||||
// docs/decisions.md#a-table-never-reaches-its-own-family-by-any-route
|
// docs/decisions.md#a-table-never-reaches-its-own-family-by-any-route
|
||||||
func checkOwnFamily(t *template) error {
|
func checkOwnFamily(t *template) error {
|
||||||
own := t.table
|
own := t.site.table
|
||||||
if own == nil {
|
|
||||||
own = t.cell.table
|
|
||||||
}
|
|
||||||
if own == nil {
|
if own == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,12 +65,12 @@ type renderTrace func(group, table string, row int, a arm)
|
|||||||
|
|
||||||
func traceRead(trace renderTrace, t *template, sc renderScope, a arm) {
|
func traceRead(trace renderTrace, t *template, sc renderScope, a arm) {
|
||||||
var table string
|
var table string
|
||||||
var row int
|
row := t.site.row
|
||||||
switch {
|
if t.site.table != nil {
|
||||||
case t.cell.table != nil:
|
table = t.site.table.path
|
||||||
table, row = t.cell.table.path, t.cell.row
|
}
|
||||||
case t.table != nil:
|
if row == formatRow {
|
||||||
table, row = t.table.path, sc.rowOf(t.table)
|
row = sc.rowOf(t.site.table)
|
||||||
}
|
}
|
||||||
trace(strings.Clone(sc.group), strings.Clone(table), row, a)
|
trace(strings.Clone(sc.group), strings.Clone(table), row, a)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,20 +54,22 @@ type template struct {
|
|||||||
drawGroup string // the draw group it draws in, as written; "" keeps its caller's
|
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
|
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
|
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
|
site tableSite
|
||||||
cell cellSite
|
|
||||||
|
|
||||||
// Filled by `linkTemplate`, from the assembled tree:
|
// Filled by `linkTemplate`, from the assembled tree:
|
||||||
link templateLink
|
link templateLink
|
||||||
compiled formatOps
|
compiled formatOps
|
||||||
}
|
}
|
||||||
|
|
||||||
// cellSite is the table cell a template is compiled from; table is nil for any other template.
|
// tableSite is the table a template belongs to, as its format or one of its cells; table is nil
|
||||||
type cellSite struct {
|
// for any other template.
|
||||||
|
type tableSite struct {
|
||||||
table *table
|
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.
|
// templateLink is what a template resolves to in the assembled tree.
|
||||||
type templateLink struct {
|
type templateLink struct {
|
||||||
refs map[string]refBinding // each reference the format reads -> what it resolves to
|
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) {
|
func compileItem(v any, pos position) (node, error) {
|
||||||
switch v := v.(type) {
|
switch v := v.(type) {
|
||||||
case string:
|
case string:
|
||||||
t, err := compileString(v, cellSite{})
|
t, err := compileString(v, tableSite{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -203,12 +205,12 @@ func jsonKind(v any) string {
|
|||||||
return fmt.Sprintf("%T", v)
|
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)
|
toks, err := parseChecked(s, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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.
|
// fixedText is one render's output when the format holds no token; repeat is the caller's.
|
||||||
|
|||||||
+3
-3
@@ -114,9 +114,9 @@ func (s *readSurvey) walk(n node, at surveyAt) {
|
|||||||
}
|
}
|
||||||
for _, e := range renderEdges(n) {
|
for _, e := range renderEdges(n) {
|
||||||
to, isTemplate := e.to.(*template)
|
to, isTemplate := e.to.(*template)
|
||||||
cell := cellSite{}
|
cell := tableSite{}
|
||||||
if isTemplate {
|
if isTemplate && to.site.row != formatRow {
|
||||||
cell = to.cell
|
cell = to.site
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case cell.table == nil:
|
case cell.table == nil:
|
||||||
|
|||||||
+8
-7
@@ -80,13 +80,14 @@ func renderRoot(s *session, t *template, trace renderTrace) {
|
|||||||
set := holdSet{trace: trace}
|
set := holdSet{trace: trace}
|
||||||
sc := renderScope{set: &set}
|
sc := renderScope{set: &set}
|
||||||
switch {
|
switch {
|
||||||
case t.table != nil:
|
case t.site.row == formatRow:
|
||||||
render(s, t.table, sc)
|
tbl := t.site.table
|
||||||
for r := 0; r < t.table.rowCount(); r++ {
|
render(s, tbl, sc)
|
||||||
|
for r := 0; r < tbl.rowCount(); r++ {
|
||||||
set := holdSet{trace: trace}
|
set := holdSet{trace: trace}
|
||||||
sc := renderScope{set: &set}
|
sc := renderScope{set: &set}
|
||||||
sc.groupHold().pins.pin(t.table, r)
|
sc.groupHold().pins.pin(tbl, r)
|
||||||
render(s, t.table.pinnedRow, sc.at(t.table.pinnedRow, &sc.groupHold().pins))
|
render(s, tbl.pinnedRow, sc.at(tbl.pinnedRow, &sc.groupHold().pins))
|
||||||
}
|
}
|
||||||
case t.repeat > 1:
|
case t.repeat > 1:
|
||||||
expand(s, t, sc)
|
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) {
|
func renderRecordRoot(s *session, t *template, columns []Column, trace renderTrace) {
|
||||||
set := holdSet{trace: trace}
|
set := holdSet{trace: trace}
|
||||||
sc := renderScope{set: &set}
|
sc := renderScope{set: &set}
|
||||||
if t.table != nil {
|
if t.site.row == formatRow {
|
||||||
t.table.drawIn(s, &sc.groupHold().pins)
|
t.site.table.drawIn(s, &sc.groupHold().pins)
|
||||||
}
|
}
|
||||||
renderRecord(s, t, columns, sc)
|
renderRecord(s, t, columns, sc)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,8 +263,8 @@ func recordOf(n node) (*template, []Column, error) {
|
|||||||
// over sc's hold; a table's columns read the row pinned there.
|
// over sc's hold; a table's columns read the row pinned there.
|
||||||
func renderRecord(s *session, t *template, columns []Column, sc renderScope) *Record {
|
func renderRecord(s *session, t *template, columns []Column, sc renderScope) *Record {
|
||||||
sc = sc.in(t)
|
sc = sc.in(t)
|
||||||
if t.table != nil {
|
if t.site.row == formatRow {
|
||||||
sc = sc.at(t.table.pinnedRow, &sc.groupHold().pins)
|
sc = sc.at(t.site.table.pinnedRow, &sc.groupHold().pins)
|
||||||
}
|
}
|
||||||
r := &Record{columns: append([]Column(nil), columns...)}
|
r := &Record{columns: append([]Column(nil), columns...)}
|
||||||
for i := range r.columns {
|
for i := range r.columns {
|
||||||
|
|||||||
+2
-2
@@ -81,8 +81,8 @@ func linkRefs(root map[string]node) error {
|
|||||||
// linkTemplate resolves t's references in category, "" for an inline template, and
|
// linkTemplate resolves t's references in category, "" for an inline template, and
|
||||||
// compiles its format against them.
|
// compiles its format against them.
|
||||||
func linkTemplate(folder []string, path, category string, t *template, root map[string]node) error {
|
func linkTemplate(folder []string, path, category string, t *template, root map[string]node) error {
|
||||||
if t.cell.table != nil {
|
if t.site.table != nil && t.site.row != formatRow {
|
||||||
path = fmt.Sprintf("%s, line %d", path, t.cell.row+2)
|
path = fmt.Sprintf("%s, line %d", path, t.site.row+2)
|
||||||
}
|
}
|
||||||
link, err := t.resolveLink(folder, path, category, root)
|
link, err := t.resolveLink(folder, path, category, root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ func (t *table) checkCells() error {
|
|||||||
if strings.IndexByte(cell, '{') < 0 && strings.IndexByte(cell, '}') < 0 {
|
if strings.IndexByte(cell, '{') < 0 && strings.IndexByte(cell, '}') < 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
n, err := compileString(cell, cellSite{t, row})
|
n, err := compileString(cell, tableSite{t, row})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("line %d, %s: %w", row+2, t.header[col], err)
|
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 {
|
if err := checkTokens(toks, t.fields); err != nil {
|
||||||
return err
|
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}
|
t.pinnedRow = &tableRow{t}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ least modify.
|
|||||||
|
|
||||||
This round, in order:
|
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
|
- 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.
|
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
|
- Test that `disagreements` holds every key `disagreement` can build, and panic through
|
||||||
|
|||||||
Reference in New Issue
Block a user