From 097fae4cd2eb1a9f781df459dbfb4715c016f58a Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Wed, 7 Oct 2026 02:08:38 +0200 Subject: [PATCH] Move frameStack into env.go and the name steps of resolveTemplates into name.go, and hold a binding's target in a named field --- README.md | 6 ++++-- env.go | 15 ++++++++++++++ name.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++- pick.go | 17 +-------------- record.go | 4 ++-- resolve.go | 61 +----------------------------------------------------- 6 files changed, 83 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index 7b86cd5..9e7b08d 100644 --- a/README.md +++ b/README.md @@ -960,14 +960,15 @@ Where the project is heading; the sections before Audience document what ships t ``` doc.go the package doc, and the vocabulary the package is written in fejkdata.go Generator, New, options, List +list.go the paths List advertises, every one a node reaches a direct descent at a time shipped.go the embedded data set, and loading a shipped category on the first call reaching it shippedindex.go the index of shipped categories, generated from data/ node.go the node model and JSON -> node compilation table.go tables: their options, format and cells, the link to the parent beside them, and how a path passes and draws a table -name.go names: {x as n} bindings, the scopes they live in, and the reads they answer +name.go names: {x as n} bindings, the scopes they live in, the reads they answer, and what each resolves to at load: its target, its reads and the keys they address namefence.go the load fences over names pick.go a render's named picks: the frames of the name scopes rendering, the memo a pick keeps, and the reads kept under it -env.go renderEnv: what a render reads names and rows through, and the frames a read opens and finds +env.go renderEnv: what a render reads names and rows through, the stack of frames it renders in, and the frames a read opens and finds path.go the dotted-path walks, and proving a path reaches a node render.go Fake, the recursive renderer (choices, format strings, expansions) and the reads its tokens make record.go records: Record, the JSON/CSV/SQL serializers, and their entry points @@ -978,6 +979,7 @@ arm.go the compiled read of one {a|b} alternative or operand: its head, reference.go one reference's resolution: its sigil, its category, its tail resolve.go resolving templates against the assembled tree in steps, each over every template before the next graph.go the node graph: tree walks, containment and render edges, and the cycle and repeat fences +sorted.go sortedNames: a map's keys, sorted calc.go the {calc()} operand checks: never a number, and a constant zero divisor datatype.go column datatypes: DataType, where datatype and null may sit, a column's datatype valueproof.go the value proof: what a typed column, calc operand or struct field holds, checked at load diff --git a/env.go b/env.go index 9327b2f..59a35bf 100644 --- a/env.go +++ b/env.go @@ -11,6 +11,21 @@ type readValue struct { null bool } +// frameStack is the frames of the name scopes rendering, innermost last. A render's env points +// at it, and a repeat iteration's shares it: holding the frames in a renderEnv would move every +// render's env to the heap. +type frameStack struct { + frames []*pickFrame +} + +func (st *frameStack) push(f *pickFrame) int { + mark := len(st.frames) + st.frames = append(st.frames, f) + return mark +} + +func (st *frameStack) pop(mark int) { st.frames = st.frames[:mark] } + // renderEnv is where a render reads: the frames of the name scopes rendering; and row, the row // of the table rendering, which its columns read. It passes by value, and what is read from it // reaches a map key, an interface or a func value only as a copy; else env, and with it every diff --git a/name.go b/name.go index a901790..d7c2b25 100644 --- a/name.go +++ b/name.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/larvit/fejkdata/internal/builtinfunc" "github.com/larvit/fejkdata/internal/grammar" ) @@ -30,7 +31,7 @@ type nameBinding struct { binder *template // Filled by `resolveTemplates`, once every check it runs passed: - nameTarget + target nameTarget // addressed is every key a read of the name lands on or passes, the spelling of the // first read reaching it beside it; a pick keeps the draws at these keys, and only these. addressed map[pickKey]string @@ -45,6 +46,13 @@ type nameUse struct { operand, noRef, nested bool } +// nameTarget is what a binding resolves to in the assembled tree: start, the node its head names, and +// the path it reads into that node. +type nameTarget struct { + start node + tail []string +} + // bindsField reports whether b binds a path into a field, so a read of it stays in the category. func (b *nameBinding) bindsField() bool { return !grammar.IsRef(b.ref) } @@ -257,3 +265,54 @@ func namedReads(t *template) []namedReadAt { } return out } + +// nameTargets resolves what each binding of ts binds, from its binder's references. +func nameTargets(ts []templateSite) map[*nameBinding]nameTarget { + targets := map[*nameBinding]nameTarget{} + for _, s := range ts { + for _, tok := range s.t.tokens { + if tok.Kind == grammar.NameBind { + a := splitArm(tok.BoundRef, s.t.refs.byName) + targets[s.t.nameScope.bindings[tok.Bound]] = nameTarget{start: s.t.startOf(a.head), tail: a.tail} + } + } + } + return targets +} + +// addressedKeys is every key the reads of each name in ts land on or pass, from the name's own +// level, with the spelling of the first read reaching it. +func addressedKeys(ts []templateSite, targets map[*nameBinding]nameTarget) map[*nameBinding]map[pickKey]string { + keys := map[*nameBinding]map[pickKey]string{} + for _, s := range ts { + for _, r := range namedReads(s.t) { + b := r.a.named + if keys[b] == nil { + keys[b] = map[pickKey]string{} + } + for _, key := range r.a.levels[len(targets[b].tail):] { + if _, seen := keys[b][key]; !seen { + keys[b][key] = r.a.spelling + } + } + } + } + return keys +} + +func nameUses(ts []templateSite) map[*nameBinding][]nameUse { + uses := map[*nameBinding][]nameUse{} + for _, s := range ts { + for _, r := range namedReads(s.t) { + b := r.a.named + uses[b] = append(uses[b], nameUse{ + tail: grammar.JoinSegments(r.a.tail), + in: s.t, + operand: r.operand, + noRef: r.operand && builtinfunc.NoRefOperands(r.o.Fn), + nested: s.t.nameScope != b.scope, + }) + } + } + return uses +} diff --git a/pick.go b/pick.go index 07c1a77..9df372b 100644 --- a/pick.go +++ b/pick.go @@ -69,21 +69,6 @@ func newPickFrame(scope *nameScope) *pickFrame { return f } -// frameStack is the frames of the name scopes rendering, innermost last. A render's env points -// at it, and a repeat iteration's shares it: holding the frames in a renderEnv would move every -// render's env to the heap. -type frameStack struct { - frames []*pickFrame -} - -func (st *frameStack) push(f *pickFrame) int { - mark := len(st.frames) - st.frames = append(st.frames, f) - return mark -} - -func (st *frameStack) pop(mark int) { st.frames = st.frames[:mark] } - // scopeAround is the innermost name scope a render of n reads names in, short of the frames n // renders itself, one per iteration of a repeat. func scopeAround(n node) *nameScope { @@ -129,7 +114,7 @@ func readName(s *drawstate.State, env renderEnv, a arm) readValue { if r, done := p.memo.value[a.key()]; done { return r } - leaf, pins := p.draw(s, a.named.start, a.steps, a.levels) + leaf, pins := p.draw(s, a.named.target.start, a.steps, a.levels) if a.named.bindsField() { return p.renderAt(s, leaf, pins, a.key(), env) } diff --git a/record.go b/record.go index 0524b59..7639d80 100644 --- a/record.go +++ b/record.go @@ -266,8 +266,8 @@ func recordColumns(t *template) []recordColumn { if tok.Kind != grammar.NameBind { continue } - if b := t.nameScope.bindings[tok.Bound]; b.bindsField() && len(b.tail) == 0 && whole[b.start] == nil { - whole[b.start] = b + if b := t.nameScope.bindings[tok.Bound]; b.bindsField() && len(b.target.tail) == 0 && whole[b.target.start] == nil { + whole[b.target.start] = b } } names := sortedNames(t.fields) diff --git a/resolve.go b/resolve.go index 3332bbd..f4c467d 100644 --- a/resolve.go +++ b/resolve.go @@ -3,7 +3,6 @@ package fejkdata import ( "fmt" - "github.com/larvit/fejkdata/internal/builtinfunc" "github.com/larvit/fejkdata/internal/grammar" ) @@ -45,13 +44,6 @@ func resolveInlineTemplates(nodes nodeSet, root map[string]node) error { return resolveTemplates(ts, root) } -// nameTarget is what a binding resolves to in the assembled tree: start, the node its head names, and -// the path it reads into that node. -type nameTarget struct { - start node - tail []string -} - // resolvedNames is what resolveTemplates resolved about the names of a set of templates: each binding's // target, the keys its reads address, and its reads. type resolvedNames struct { @@ -94,62 +86,11 @@ func resolveTemplates(ts []templateSite, root map[string]node) error { } } for b, target := range targets { - b.nameTarget, b.addressed = target, names.addressed[b] + b.target, b.addressed = target, names.addressed[b] } return nil } -// nameTargets resolves what each binding of ts binds, from its binder's references. -func nameTargets(ts []templateSite) map[*nameBinding]nameTarget { - targets := map[*nameBinding]nameTarget{} - for _, s := range ts { - for _, tok := range s.t.tokens { - if tok.Kind == grammar.NameBind { - a := splitArm(tok.BoundRef, s.t.refs.byName) - targets[s.t.nameScope.bindings[tok.Bound]] = nameTarget{start: s.t.startOf(a.head), tail: a.tail} - } - } - } - return targets -} - -// addressedKeys is every key the reads of each name in ts land on or pass, from the name's own -// level, with the spelling of the first read reaching it. -func addressedKeys(ts []templateSite, targets map[*nameBinding]nameTarget) map[*nameBinding]map[pickKey]string { - keys := map[*nameBinding]map[pickKey]string{} - for _, s := range ts { - for _, r := range namedReads(s.t) { - b := r.a.named - if keys[b] == nil { - keys[b] = map[pickKey]string{} - } - for _, key := range r.a.levels[len(targets[b].tail):] { - if _, seen := keys[b][key]; !seen { - keys[b][key] = r.a.spelling - } - } - } - } - return keys -} - -func nameUses(ts []templateSite) map[*nameBinding][]nameUse { - uses := map[*nameBinding][]nameUse{} - for _, s := range ts { - for _, r := range namedReads(s.t) { - b := r.a.named - uses[b] = append(uses[b], nameUse{ - tail: grammar.JoinSegments(r.a.tail), - in: s.t, - operand: r.operand, - noRef: r.operand && builtinfunc.NoRefOperands(r.o.Fn), - nested: s.t.nameScope != b.scope, - }) - } - } - return uses -} - // columnRead is a record's column read by a format that only reads one reference or name, // which is the column: it takes the column's datatype and null. category and field name it; // category is "" for a column of the reading template's own record, which checkColumns reaches