From 14194a775d7d8b1fe6efeba99c36bf7956620522 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Tue, 29 Sep 2026 12:31:52 +0200 Subject: [PATCH] Rename ftoken to scanUnit and session to generatorState --- builtins.go | 18 +++++++++--------- builtins_test.go | 10 +++++----- calc.go | 2 +- fejkdata.go | 12 ++++++------ format.go | 16 ++++++++-------- layout.go | 4 ++-- path.go | 2 +- readsurvey_test.go | 4 ++-- record.go | 4 ++-- render.go | 24 ++++++++++++------------ struct.go | 6 +++--- table.go | 8 ++++---- todo.md | 3 --- transform.go | 2 +- 14 files changed, 56 insertions(+), 59 deletions(-) diff --git a/builtins.go b/builtins.go index f4be6ea..58b7e1d 100644 --- a/builtins.go +++ b/builtins.go @@ -40,19 +40,19 @@ var builtins = withTransforms(map[string]builtin{ "lower": {arity: 1, checkArgs: posIntArg, prep: chars("abcdefghijklmnopqrstuvwxyz")}, "base64": {arity: 1, checkArgs: posIntArg, prep: func(a []string) callFn { n := atoi(a[0]) - return func(s *session, _ string, _ []string) string { + return func(s *generatorState, _ string, _ []string) string { return base64.StdEncoding.EncodeToString(randBytes(s, n)) } }}, "int": {arity: 2, checkArgs: intRangeArgs, prep: func(a []string) callFn { lo, span := atoi(a[0]), atoi(a[1])-atoi(a[0])+1 - return func(s *session, _ string, _ []string) string { return strconv.Itoa(lo + s.IntN(span)) } + return func(s *generatorState, _ string, _ []string) string { return strconv.Itoa(lo + s.IntN(span)) } }, prints: DataTypeInteger, proveNumber: func(token string, prints DataType, a []string) proven { return printing(token, prints, bounded(float64(atoi(a[0])), float64(atoi(a[1])), true)) }}, "float": {arity: 3, checkArgs: floatArgs, prep: func(a []string) callFn { lo, hi, dp := atof(a[0]), atof(a[1]), atoi(a[2]) - return func(s *session, _ string, _ []string) string { + return func(s *generatorState, _ string, _ []string) string { return formatFloat(lo+s.Float64()*(hi-lo), dp) } }, prints: DataTypeNumber, proveNumber: func(token string, _ DataType, a []string) proven { @@ -60,12 +60,12 @@ var builtins = withTransforms(map[string]builtin{ }}, "iban": {arity: 1, checkArgs: ibanArg, prep: func(a []string) callFn { cc := a[0] - return func(s *session, _ string, _ []string) string { return iban(s, cc) } + return func(s *generatorState, _ string, _ []string) string { return iban(s, cc) } }}, "date": {arity: -1, checkArgs: dateArgs, prep: datePrep}, "time": {arity: -1, checkArgs: timeArg, prep: timePrep}, "calc": {arity: -1, checkArgs: checkCalc, prep: calcPrep, operands: calcOperands}, - // seq is the one stateful builtin: a per-session counter from 1, advancing on + // seq is the one stateful builtin: a per-generator counter from 1, advancing on // each call. An optional name selects an independent counter; no name uses the // default one. Deterministic by construction, so seeded output stays stable. "seq": {arity: -1, checkArgs: seqArg, prep: func(a []string) callFn { @@ -73,7 +73,7 @@ var builtins = withTransforms(map[string]builtin{ if len(a) == 1 { key = a[0] } - return func(s *session, _ string, _ []string) string { + return func(s *generatorState, _ string, _ []string) string { return strconv.FormatUint(s.next(key), 10) } }, prints: DataTypeInteger, proveNumber: func(token string, prints DataType, _ []string) proven { @@ -86,18 +86,18 @@ var builtins = withTransforms(map[string]builtin{ // sample of n characters drawn from an alphabet. func derive(f func(emitted string) string) func([]string) callFn { return func([]string) callFn { - return func(_ *session, emitted string, _ []string) string { return f(emitted) } + return func(_ *generatorState, emitted string, _ []string) string { return f(emitted) } } } func sample(f func(rng) string) func([]string) callFn { return func([]string) callFn { - return func(s *session, _ string, _ []string) string { return f(s) } + return func(s *generatorState, _ string, _ []string) string { return f(s) } } } func chars(alphabet string) func([]string) callFn { return func(a []string) callFn { n := atoi(a[0]) - return func(s *session, _ string, _ []string) string { return randChars(s, n, alphabet) } + return func(s *generatorState, _ string, _ []string) string { return randChars(s, n, alphabet) } } } diff --git a/builtins_test.go b/builtins_test.go index d093483..5c30158 100644 --- a/builtins_test.go +++ b/builtins_test.go @@ -102,10 +102,10 @@ func TestBuiltinChecksums(t *testing.T) { } } -// TestBuiltinSeqPerSession pins seq's contract: a counter from 1, advancing on +// TestBuiltinSeqPerGenerator pins seq's contract: a counter from 1, advancing on // each call, named counters independent, and the whole thing scoped to one -// session so a fresh Generator restarts at 1. -func TestBuiltinSeqPerSession(t *testing.T) { +// generatorState so a fresh Generator restarts at 1. +func TestBuiltinSeqPerGenerator(t *testing.T) { f := engine(1) for i := 1; i <= 5; i++ { if got := mustRender(t, f, `"{seq()}"`); got != strconv.Itoa(i) { @@ -122,9 +122,9 @@ func TestBuiltinSeqPerSession(t *testing.T) { if got := mustRender(t, engine(2), `{"format":"{seq()}","repeat":3,"separator":","}`); got != "1,2,3" { t.Fatalf("repeat seq = %q, want 1,2,3", got) } - // ...and a fresh session restarts from 1. + // ...and a fresh generator restarts from 1. if got := mustRender(t, engine(2), `"{seq()}"`); got != "1" { - t.Fatalf("new session seq = %q, want 1", got) + t.Fatalf("new generator seq = %q, want 1", got) } } diff --git a/calc.go b/calc.go index 8179cfb..04879ef 100644 --- a/calc.go +++ b/calc.go @@ -199,7 +199,7 @@ func calcPrep(args []string) callFn { } placed := indexVars(expr, at) dp := calcDecimals(args) - return func(_ *session, _ string, operands []string) string { + return func(_ *generatorState, _ string, operands []string) string { return formatFloat(placed.eval(operands), dp) } } diff --git a/fejkdata.go b/fejkdata.go index d13a7be..5c1c17d 100644 --- a/fejkdata.go +++ b/fejkdata.go @@ -34,20 +34,20 @@ type Generator struct { // table's row lookup, built under its sync.Once, so List, NewTemplate and // NewRecordTemplate read it without mu. mu sync.Mutex - rand *session + rand *generatorState root folder // the categories as the node a path walks from, owned here so a walk allocates none records map[node]recordShape structs map[reflect.Type]structResult } -// session is one generator's mutable render state: the seeded rng plus the {seq()} +// generatorState is one generator's state across its renders: the seeded rng plus the {seq()} // counters. -type session struct { +type generatorState struct { *rand.Rand counters map[string]uint64 } -func (s *session) next(key string) uint64 { +func (s *generatorState) next(key string) uint64 { s.counters[key]++ return s.counters[key] } @@ -228,7 +228,7 @@ func join(prefix, name string) string { // randomBytes seeds an unseeded generator. var randomBytes = crand.Read -func newRand(seed uint64, seeded bool) (*session, error) { +func newRand(seed uint64, seeded bool) (*generatorState, error) { var r *rand.Rand if seeded { r = rand.New(rand.NewPCG(seed, seed^0x9e3779b97f4a7c15)) @@ -239,7 +239,7 @@ func newRand(seed uint64, seeded bool) (*session, error) { } r = rand.New(rand.NewPCG(binary.LittleEndian.Uint64(b[:8]), binary.LittleEndian.Uint64(b[8:]))) } - return &session{Rand: r, counters: map[string]uint64{}}, nil + return &generatorState{Rand: r, counters: map[string]uint64{}}, nil } func internalError(format string, a ...any) string { diff --git a/format.go b/format.go index ae043e3..9498152 100644 --- a/format.go +++ b/format.go @@ -5,9 +5,9 @@ import ( "strings" ) -// ftoken is one unit of a scanned format string: a literal rune or the body of a +// scanUnit is one unit of a scanned format string: a literal rune or the body of a // {…} token. -type ftoken struct { +type scanUnit struct { isToken bool // a {…} body, else a literal rune char rune body string @@ -16,10 +16,10 @@ type ftoken struct { // eachToken scans a format string once and calls fn for each unit, the single // source of truth for how braces are read: "{{" and "}}" are literal braces, a "{" // opens a token that must reach its "}", and a lone "}" is an error. -func eachToken(format string, fn func(ftoken) error) error { +func eachToken(format string, fn func(scanUnit) error) error { rs := []rune(format) for i := 0; i < len(rs); i++ { - var t ftoken + var t scanUnit switch c := rs[i]; c { case '{': if i+1 < len(rs) && rs[i+1] == '{' { @@ -85,7 +85,7 @@ func parseFormat(format string) ([]formatToken, error) { lit.Reset() } } - err := eachToken(format, func(t ftoken) error { + err := eachToken(format, func(t scanUnit) error { if !t.isToken { lit.WriteRune(t.char) return nil @@ -110,11 +110,11 @@ func parseFormat(format string) ([]formatToken, error) { } // builtin is a format-string function invoked as {name(args)}. It receives the -// session (its rng, and the {seq()} counters), the output emitted so far in the +// generatorState (its rng, and the {seq()} counters), the output emitted so far in the // current expansion (for derivations such as a checksum over preceding digits), and // the values of the operands it named (calc and the transforms name them). All must // stay pure over (rng, emitted, args) so seeded output is reproducible; seq advances -// per-session counter state, which is itself deterministic. arity is the exact arg +// per-generator counter state, which is itself deterministic. arity is the exact arg // count, or -1 for variadic (then checkArgs does all the validation). type builtin struct { arity int @@ -366,7 +366,7 @@ func checkSegments(a arm) error { // callFn is a builtin prepared for one call site: its args already parsed. It reads the // output emitted so far in the current expansion (a derivation's payload) and the // values of the operands it named, which expand read for it. -type callFn func(s *session, emitted string, operands []string) string +type callFn func(s *generatorState, emitted string, operands []string) string // op is one compiled unit of a format string: a literal run, a name read, // or a builtin already prepared with its args. compile builds these so render never diff --git a/layout.go b/layout.go index af98d7e..cb1a61c 100644 --- a/layout.go +++ b/layout.go @@ -135,13 +135,13 @@ func datePrep(a []string) callFn { panic(internalError("builtin arg %q reached prep unvalidated: %v", a[1], err)) } layout, start, span := layoutOf(a[2]), from.Unix(), int(to.Unix()-from.Unix())+86400 - return func(s *session, _ string, _ []string) string { + return func(s *generatorState, _ string, _ []string) string { return time.Unix(start+int64(s.IntN(span)), 0).UTC().Format(layout) } } func timePrep(a []string) callFn { layout := layoutOf(a[0]) - return func(s *session, _ string, _ []string) string { + return func(s *generatorState, _ string, _ []string) string { return time.Unix(int64(s.IntN(86400)), 0).UTC().Format(layout) } } diff --git a/path.go b/path.go index b8b253f..02037ab 100644 --- a/path.go +++ b/path.go @@ -353,7 +353,7 @@ func (w *pathProbe) readRow(t *table, tail []string, r tableRoute) error { // Where pathCheck walks every variant of a choice, it walks the one drawn; at a table it selects // as pathCheck does and draws the row the route asks for. type pathDraw struct { - s *session + s *generatorState pins *pinSet held *hold a *arm diff --git a/readsurvey_test.go b/readsurvey_test.go index 992fd11..0c5f780 100644 --- a/readsurvey_test.go +++ b/readsurvey_test.go @@ -76,7 +76,7 @@ func spellPins(p *pinSet) string { return "row " + strings.Join(rows, ", ") } -func renderRoot(s *session, t *template, trace renderTrace) { +func renderRoot(s *generatorState, t *template, trace renderTrace) { set := holdSet{trace: trace} sc := renderScope{set: &set} switch { @@ -96,7 +96,7 @@ func renderRoot(s *session, t *template, trace renderTrace) { } } -func renderRecordRoot(s *session, t *template, columns []Column, trace renderTrace) { +func renderRecordRoot(s *generatorState, t *template, columns []Column, trace renderTrace) { set := holdSet{trace: trace} sc := renderScope{set: &set} if t.site.row == formatRow { diff --git a/record.go b/record.go index 0298571..e8dee4f 100644 --- a/record.go +++ b/record.go @@ -152,7 +152,7 @@ func (f *Generator) FakeRecord(path string) (*Record, error) { // tableRecord walks a path's tail from a table to the table whose row is the record, // pinning the rows it selects or draws. -func tableRecord(s *session, t *table, tail []string, sc renderScope) (node, error) { +func tableRecord(s *generatorState, t *table, tail []string, sc renderScope) (node, error) { n, err := descend(s, t, tail, sc) if err != nil { return nil, err @@ -261,7 +261,7 @@ func recordOf(n node) (*template, []Column, error) { // renderRecord draws each column once, in the name order recordOf fixed, as one render // 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 *generatorState, t *template, columns []Column, sc renderScope) *Record { sc = sc.in(t) if t.site.row == formatRow { sc = sc.at(t.site.table.rowNode, &sc.groupHold().pins) diff --git a/render.go b/render.go index a03cd73..e05ca5c 100644 --- a/render.go +++ b/render.go @@ -6,7 +6,7 @@ import ( ) // rng is the randomness a builtin sample draws from; *rand.Rand satisfies it. The -// render path takes the concrete *session instead, which keeps the hold set of the walk +// render path takes the concrete *generatorState instead, which keeps the hold set of the walk // drawing through it off the heap. type rng interface { IntN(n int) int @@ -38,7 +38,7 @@ func (f *Generator) Fake(path string) (string, error) { // descend walks a caller's path to the node it names, pinning in sc the rows it // selects or draws. -func descend(s *session, root node, segments []string, sc renderScope) (node, error) { +func descend(s *generatorState, root node, segments []string, sc renderScope) (node, error) { // docs/decisions.md#a-path-is-walked-once-without-drawing-before-it-is-walked-for-real if _, err := (&pathProbe{}).walk(root, segments); err != nil { return nil, err @@ -47,7 +47,7 @@ func descend(s *session, root node, segments []string, sc renderScope) (node, er } // renderOnce renders n as one render, over a hold set of its own. -func renderOnce(s *session, n node) string { +func renderOnce(s *generatorState, n node) string { var set holdSet return render(s, n, renderScope{set: &set}) } @@ -56,7 +56,7 @@ func renderOnce(s *session, n node) string { // front, so rendering a compiled tree cannot fail. sc holds the reference draws the // render shares; each repeat iteration renders over a hold set of its own. // A child this switch renders is one renderEdges must list too, or the fences miss it. -func render(s *session, n node, sc renderScope) string { +func render(s *generatorState, n node, sc renderScope) string { switch n := n.(type) { case *choice: return render(s, pick(s, n), sc) @@ -99,7 +99,7 @@ func render(s *session, n node, sc renderScope) string { // render's loop, its hold set would move to the heap. // //go:noinline -func expandAnew(s *session, t *template) string { +func expandAnew(s *generatorState, t *template) string { var set holdSet return expand(s, t, renderScope{set: &set}) } @@ -107,7 +107,7 @@ func expandAnew(s *session, t *template) string { // pick selects one item. Uniform choices are O(1); weighted choices are an // O(log n) search over precomputed cumulative weights. compile guarantees a // non-empty choice and a finite positive total, so the index is always in range. -func pick(s *session, c *choice) node { +func pick(s *generatorState, c *choice) node { if c.cum == nil { return c.items[s.IntN(len(c.items))] } @@ -116,7 +116,7 @@ func pick(s *session, c *choice) node { // expand renders a template's compiled ops. compile validated every token, so this // cannot fail. -func expand(s *session, t *template, sc renderScope) string { +func expand(s *generatorState, t *template, sc renderScope) string { var b strings.Builder b.Grow(t.compiled.grow) // One draw per held name, for this expansion only: a nested template and each @@ -157,7 +157,7 @@ func expand(s *session, t *template, sc renderScope) string { // reads — is drawn once and kept in held, so {place.postal-code} and {place.locality} // read one row, either read twice gives one value, and a shown operand is the operand // computed. Every other name is drawn afresh, so {word} {word} still draws twice. -func readField(s *session, t *template, held *hold, sc renderScope, a arm) readValue { +func readField(s *generatorState, t *template, held *hold, sc renderScope, a arm) readValue { if isRef(a.head) && len(a.tail) > 0 { return readReference(s, t, sc, a) } @@ -178,7 +178,7 @@ func readField(s *session, t *template, held *hold, sc renderScope, a arm) readV // readReference reads a reference path, kept in the render's hold for its group, so its draw // spans the render. -func readReference(s *session, t *template, sc renderScope, a arm) readValue { +func readReference(s *generatorState, t *template, sc renderScope, a arm) readValue { if sc.set.trace != nil { traceRead(sc.set.trace, t, sc, a) } @@ -186,7 +186,7 @@ func readReference(s *session, t *template, sc renderScope, a arm) readValue { return readHeld(s, t, &group.hold, &group.pins, sc, a) } -func readHeld(s *session, t *template, held *hold, pins *pinSet, sc renderScope, a arm) readValue { +func readHeld(s *generatorState, t *template, held *hold, pins *pinSet, sc renderScope, a arm) readValue { if r, done := held.value[a.path]; done { return r } @@ -215,7 +215,7 @@ func traceRead(trace renderTrace, t *template, sc renderScope, a arm) { // renderLeaf draws and renders what a read lands on: null on a null item, or on a column of one // reference alone whose read drew null. -func renderLeaf(s *session, n node, sc renderScope) readValue { +func renderLeaf(s *generatorState, n node, sc renderScope) readValue { n = resolveChoice(s, n) switch leaf := n.(type) { case *nullItem: @@ -231,7 +231,7 @@ func renderLeaf(s *session, n node, sc renderScope) readValue { // resolveChoice resolves a choice to one variant, so a held head is a concrete node the // rest of the expansion shares. Nested choices unwrap too: a draw is one value, not // another set to pick from. -func resolveChoice(s *session, n node) node { +func resolveChoice(s *generatorState, n node) node { for c, ok := n.(*choice); ok; c, ok = n.(*choice) { n = pick(s, c) } diff --git a/struct.go b/struct.go index 8f6b82a..f51f218 100644 --- a/struct.go +++ b/struct.go @@ -349,10 +349,10 @@ func (p *valueProof) checkField(label string, ft reflect.Type, column node) erro } // fill draws the record into v's tagged fields, then each nested struct as a record of its own. -func (s *structShape) fill(sess *session, v reflect.Value) { +func (s *structShape) fill(state *generatorState, v reflect.Value) { if s.record != nil { var set holdSet - for i, c := range renderRecord(sess, s.record, s.columns, renderScope{set: &set}).columns { + for i, c := range renderRecord(state, s.record, s.columns, renderScope{set: &set}).columns { setColumn(fieldAt(v, s.fieldIndexes[i]), c) } } @@ -364,7 +364,7 @@ func (s *structShape) fill(sess *session, v reflect.Value) { } field = field.Elem() } - n.shape.fill(sess, field) + n.shape.fill(state, field) } } diff --git a/table.go b/table.go index 1e81db0..71c4fc8 100644 --- a/table.go +++ b/table.go @@ -472,20 +472,20 @@ func (t *table) builtLookup() *rowLookup { } // drawRow picks a row over the whole table. -func (t *table) drawRow(s *session) int { +func (t *table) drawRow(s *generatorState) int { if t.cum == nil { return s.IntN(t.rowCount()) } return pickCum(s, t.cum) } -func pickCum(s *session, cum []float64) int { +func pickCum(s *generatorState, cum []float64) int { x := s.Float64() * cum[len(cum)-1] return min(sort.Search(len(cum), func(i int) bool { return cum[i] > x }), len(cum)-1) // x can round up to the total } // drawUnder picks a row among those linked to parent row pr. -func (t *table) drawUnder(s *session, pr int) int { +func (t *table) drawUnder(s *generatorState, pr int) int { lookup := t.builtLookup() k := t.parentT.cell(pr, t.parentT.keyIndex) rows := lookup.rowsByParent[k] @@ -498,7 +498,7 @@ func (t *table) drawUnder(s *session, pr int) int { // drawIn is the render's row of t: the one pinned in p, else one drawn inside the nearest // pinned ancestor — its parent drawn inside that first where the ancestor is further up — or // over the whole table, and pinned with its ancestors. -func (t *table) drawIn(s *session, p *pinSet) int { +func (t *table) drawIn(s *generatorState, p *pinSet) int { if r, ok := p.pinned(t); ok { return r } diff --git a/todo.md b/todo.md index 5215fb9..970d455 100644 --- a/todo.md +++ b/todo.md @@ -31,9 +31,6 @@ on 2026-09-29 a third time (Navigation 7.0, Locality 5.6, Shape 6.3, Self-suffic This round, in order: -- Rename `ftoken` to `scanUnit`, beside `formatToken`, and `session` to - `generatorState`: three seats confused the token types, and both architects read - `session` as per-render state. - State at `pinSet.pin` that its caller proved r sits inside the nearest pinned ancestor, which `pinRow` and `drawIn` do. - Delete the prose two or more seats found empty: `loadData`'s comment, which also names diff --git a/transform.go b/transform.go index e5f965e..64d4d11 100644 --- a/transform.go +++ b/transform.go @@ -70,7 +70,7 @@ func transformPrep(outer func(string) string) func([]string) callFn { if err != nil { panic(internalError("transform arg %q reached prep unvalidated: %v", a[0], err)) } - return func(_ *session, _ string, operands []string) string { + return func(_ *generatorState, _ string, operands []string) string { v := operands[0] for i := len(chain) - 1; i >= 0; i-- { v = chain[i](v)