Set a table's path when it compiles, link a table family in one function, and label a cell from the walk

This commit is contained in:
2026-10-05 21:06:38 +02:00
committed by Lillem4n
parent 21a4ed1795
commit 8ffaa31e39
11 changed files with 88 additions and 105 deletions
+12 -41
View File
@@ -8,6 +8,7 @@ import (
"sort"
"strings"
"github.com/larvit/fejkdata/internal/datafiles"
"github.com/larvit/fejkdata/internal/grammar"
)
@@ -55,9 +56,8 @@ type template struct {
repeat int
separator string
datatype DataType
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
site tableSite
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
unbound []unboundRead // the heads its tokens read that no field holds
// Filled by `bindNames`, from the compiled category:
@@ -69,27 +69,6 @@ type template struct {
compiled formatOps
}
// 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 // the cell's row, or formatRow for the format
}
const formatRow = -1
func (s tableSite) isCell() bool {
return s.table != nil && s.row != formatRow
}
// label names the template at path in an error, a cell by its line in the rows file.
func (s tableSite) label(path string) string {
if s.isCell() {
return fmt.Sprintf("%s, line %d", path, s.row+2)
}
return path
}
// 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
@@ -119,18 +98,18 @@ func compile(v any) (node, error) {
// compileCategory compiles a data file's value, which may be a table over a rows
// file beside it, and refuses a choice that is a table written as templates.
func compileCategory(v any, name string, readRows func(file string) (string, error)) (node, error) {
if m, ok := v.(map[string]any); ok {
func compileCategory(c datafiles.Category) (node, error) {
if m, ok := c.JSON.(map[string]any); ok {
if _, isTable := m["rows"]; isTable {
return compileTable(m, name, readRows)
return compileTable(m, c.Name, join(strings.Join(c.Folders, "."), c.Name), c.ReadRows)
}
}
if items, ok := v.([]any); ok {
if err := checkNotRows(items, name); err != nil {
if items, ok := c.JSON.([]any); ok {
if err := checkNotRows(items, c.Name); err != nil {
return nil, err
}
}
return compile(v)
return compile(c.JSON)
}
// checkNotRows refuses a choice of templates sharing one format and one set of
@@ -197,7 +176,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, tableSite{})
t, err := compileString(v)
if err != nil {
return nil, err
}
@@ -228,20 +207,12 @@ func jsonKind(v any) string {
return fmt.Sprintf("%T", v)
}
func compileString(s string, site tableSite) (*template, error) {
func compileString(s string) (*template, error) {
toks, unbound, err := parseChecked(s, nil)
if err != nil {
return nil, err
}
if site.table != nil {
if err := refuseTableBinding(toks); err != nil {
return nil, err
}
if len(unbound) > 0 {
return nil, unbound[0].err
}
}
return &template{format: s, tokens: toks, repeat: 1, fromString: true, site: site, unbound: unbound}, nil
return &template{format: s, tokens: toks, repeat: 1, fromString: true, unbound: unbound}, nil
}
// fixedText is one render's output when the format holds no token; repeat is the caller's.