Rename ftoken to scanUnit and session to generatorState
This commit is contained in:
+9
-9
@@ -40,19 +40,19 @@ var builtins = withTransforms(map[string]builtin{
|
|||||||
"lower": {arity: 1, checkArgs: posIntArg, prep: chars("abcdefghijklmnopqrstuvwxyz")},
|
"lower": {arity: 1, checkArgs: posIntArg, prep: chars("abcdefghijklmnopqrstuvwxyz")},
|
||||||
"base64": {arity: 1, checkArgs: posIntArg, prep: func(a []string) callFn {
|
"base64": {arity: 1, checkArgs: posIntArg, prep: func(a []string) callFn {
|
||||||
n := atoi(a[0])
|
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))
|
return base64.StdEncoding.EncodeToString(randBytes(s, n))
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
"int": {arity: 2, checkArgs: intRangeArgs, prep: func(a []string) callFn {
|
"int": {arity: 2, checkArgs: intRangeArgs, prep: func(a []string) callFn {
|
||||||
lo, span := atoi(a[0]), atoi(a[1])-atoi(a[0])+1
|
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 {
|
}, 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))
|
return printing(token, prints, bounded(float64(atoi(a[0])), float64(atoi(a[1])), true))
|
||||||
}},
|
}},
|
||||||
"float": {arity: 3, checkArgs: floatArgs, prep: func(a []string) callFn {
|
"float": {arity: 3, checkArgs: floatArgs, prep: func(a []string) callFn {
|
||||||
lo, hi, dp := atof(a[0]), atof(a[1]), atoi(a[2])
|
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)
|
return formatFloat(lo+s.Float64()*(hi-lo), dp)
|
||||||
}
|
}
|
||||||
}, prints: DataTypeNumber, proveNumber: func(token string, _ DataType, a []string) proven {
|
}, 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 {
|
"iban": {arity: 1, checkArgs: ibanArg, prep: func(a []string) callFn {
|
||||||
cc := a[0]
|
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},
|
"date": {arity: -1, checkArgs: dateArgs, prep: datePrep},
|
||||||
"time": {arity: -1, checkArgs: timeArg, prep: timePrep},
|
"time": {arity: -1, checkArgs: timeArg, prep: timePrep},
|
||||||
"calc": {arity: -1, checkArgs: checkCalc, prep: calcPrep, operands: calcOperands},
|
"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
|
// each call. An optional name selects an independent counter; no name uses the
|
||||||
// default one. Deterministic by construction, so seeded output stays stable.
|
// default one. Deterministic by construction, so seeded output stays stable.
|
||||||
"seq": {arity: -1, checkArgs: seqArg, prep: func(a []string) callFn {
|
"seq": {arity: -1, checkArgs: seqArg, prep: func(a []string) callFn {
|
||||||
@@ -73,7 +73,7 @@ var builtins = withTransforms(map[string]builtin{
|
|||||||
if len(a) == 1 {
|
if len(a) == 1 {
|
||||||
key = a[0]
|
key = a[0]
|
||||||
}
|
}
|
||||||
return func(s *session, _ string, _ []string) string {
|
return func(s *generatorState, _ string, _ []string) string {
|
||||||
return strconv.FormatUint(s.next(key), 10)
|
return strconv.FormatUint(s.next(key), 10)
|
||||||
}
|
}
|
||||||
}, prints: DataTypeInteger, proveNumber: func(token string, prints DataType, _ []string) proven {
|
}, 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.
|
// sample of n characters drawn from an alphabet.
|
||||||
func derive(f func(emitted string) string) func([]string) callFn {
|
func derive(f func(emitted string) string) func([]string) callFn {
|
||||||
return 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 {
|
func sample(f func(rng) string) func([]string) callFn {
|
||||||
return 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 {
|
func chars(alphabet string) func([]string) callFn {
|
||||||
return func(a []string) callFn {
|
return func(a []string) callFn {
|
||||||
n := atoi(a[0])
|
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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -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
|
// each call, named counters independent, and the whole thing scoped to one
|
||||||
// session so a fresh Generator restarts at 1.
|
// generatorState so a fresh Generator restarts at 1.
|
||||||
func TestBuiltinSeqPerSession(t *testing.T) {
|
func TestBuiltinSeqPerGenerator(t *testing.T) {
|
||||||
f := engine(1)
|
f := engine(1)
|
||||||
for i := 1; i <= 5; i++ {
|
for i := 1; i <= 5; i++ {
|
||||||
if got := mustRender(t, f, `"{seq()}"`); got != strconv.Itoa(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" {
|
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)
|
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" {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ func calcPrep(args []string) callFn {
|
|||||||
}
|
}
|
||||||
placed := indexVars(expr, at)
|
placed := indexVars(expr, at)
|
||||||
dp := calcDecimals(args)
|
dp := calcDecimals(args)
|
||||||
return func(_ *session, _ string, operands []string) string {
|
return func(_ *generatorState, _ string, operands []string) string {
|
||||||
return formatFloat(placed.eval(operands), dp)
|
return formatFloat(placed.eval(operands), dp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -34,20 +34,20 @@ type Generator struct {
|
|||||||
// table's row lookup, built under its sync.Once, so List, NewTemplate and
|
// table's row lookup, built under its sync.Once, so List, NewTemplate and
|
||||||
// NewRecordTemplate read it without mu.
|
// NewRecordTemplate read it without mu.
|
||||||
mu sync.Mutex
|
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
|
root folder // the categories as the node a path walks from, owned here so a walk allocates none
|
||||||
records map[node]recordShape
|
records map[node]recordShape
|
||||||
structs map[reflect.Type]structResult
|
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.
|
// counters.
|
||||||
type session struct {
|
type generatorState struct {
|
||||||
*rand.Rand
|
*rand.Rand
|
||||||
counters map[string]uint64
|
counters map[string]uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) next(key string) uint64 {
|
func (s *generatorState) next(key string) uint64 {
|
||||||
s.counters[key]++
|
s.counters[key]++
|
||||||
return s.counters[key]
|
return s.counters[key]
|
||||||
}
|
}
|
||||||
@@ -228,7 +228,7 @@ func join(prefix, name string) string {
|
|||||||
// randomBytes seeds an unseeded generator.
|
// randomBytes seeds an unseeded generator.
|
||||||
var randomBytes = crand.Read
|
var randomBytes = crand.Read
|
||||||
|
|
||||||
func newRand(seed uint64, seeded bool) (*session, error) {
|
func newRand(seed uint64, seeded bool) (*generatorState, error) {
|
||||||
var r *rand.Rand
|
var r *rand.Rand
|
||||||
if seeded {
|
if seeded {
|
||||||
r = rand.New(rand.NewPCG(seed, seed^0x9e3779b97f4a7c15))
|
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:])))
|
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 {
|
func internalError(format string, a ...any) string {
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import (
|
|||||||
"strings"
|
"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.
|
// {…} token.
|
||||||
type ftoken struct {
|
type scanUnit struct {
|
||||||
isToken bool // a {…} body, else a literal rune
|
isToken bool // a {…} body, else a literal rune
|
||||||
char rune
|
char rune
|
||||||
body string
|
body string
|
||||||
@@ -16,10 +16,10 @@ type ftoken struct {
|
|||||||
// eachToken scans a format string once and calls fn for each unit, the single
|
// 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 "{"
|
// 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.
|
// 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)
|
rs := []rune(format)
|
||||||
for i := 0; i < len(rs); i++ {
|
for i := 0; i < len(rs); i++ {
|
||||||
var t ftoken
|
var t scanUnit
|
||||||
switch c := rs[i]; c {
|
switch c := rs[i]; c {
|
||||||
case '{':
|
case '{':
|
||||||
if i+1 < len(rs) && rs[i+1] == '{' {
|
if i+1 < len(rs) && rs[i+1] == '{' {
|
||||||
@@ -85,7 +85,7 @@ func parseFormat(format string) ([]formatToken, error) {
|
|||||||
lit.Reset()
|
lit.Reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err := eachToken(format, func(t ftoken) error {
|
err := eachToken(format, func(t scanUnit) error {
|
||||||
if !t.isToken {
|
if !t.isToken {
|
||||||
lit.WriteRune(t.char)
|
lit.WriteRune(t.char)
|
||||||
return nil
|
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
|
// 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
|
// 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
|
// 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
|
// 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).
|
// count, or -1 for variadic (then checkArgs does all the validation).
|
||||||
type builtin struct {
|
type builtin struct {
|
||||||
arity int
|
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
|
// 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
|
// 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.
|
// 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,
|
// 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
|
// or a builtin already prepared with its args. compile builds these so render never
|
||||||
|
|||||||
@@ -135,13 +135,13 @@ func datePrep(a []string) callFn {
|
|||||||
panic(internalError("builtin arg %q reached prep unvalidated: %v", a[1], err))
|
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
|
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)
|
return time.Unix(start+int64(s.IntN(span)), 0).UTC().Format(layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func timePrep(a []string) callFn {
|
func timePrep(a []string) callFn {
|
||||||
layout := layoutOf(a[0])
|
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)
|
return time.Unix(int64(s.IntN(86400)), 0).UTC().Format(layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
// 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.
|
// as pathCheck does and draws the row the route asks for.
|
||||||
type pathDraw struct {
|
type pathDraw struct {
|
||||||
s *session
|
s *generatorState
|
||||||
pins *pinSet
|
pins *pinSet
|
||||||
held *hold
|
held *hold
|
||||||
a *arm
|
a *arm
|
||||||
|
|||||||
+2
-2
@@ -76,7 +76,7 @@ func spellPins(p *pinSet) string {
|
|||||||
return "row " + strings.Join(rows, ", ")
|
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}
|
set := holdSet{trace: trace}
|
||||||
sc := renderScope{set: &set}
|
sc := renderScope{set: &set}
|
||||||
switch {
|
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}
|
set := holdSet{trace: trace}
|
||||||
sc := renderScope{set: &set}
|
sc := renderScope{set: &set}
|
||||||
if t.site.row == formatRow {
|
if t.site.row == formatRow {
|
||||||
|
|||||||
@@ -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,
|
// tableRecord walks a path's tail from a table to the table whose row is the record,
|
||||||
// pinning the rows it selects or draws.
|
// 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)
|
n, err := descend(s, t, tail, sc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// 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.
|
// 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)
|
sc = sc.in(t)
|
||||||
if t.site.row == formatRow {
|
if t.site.row == formatRow {
|
||||||
sc = sc.at(t.site.table.rowNode, &sc.groupHold().pins)
|
sc = sc.at(t.site.table.rowNode, &sc.groupHold().pins)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// rng is the randomness a builtin sample draws from; *rand.Rand satisfies it. The
|
// 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.
|
// drawing through it off the heap.
|
||||||
type rng interface {
|
type rng interface {
|
||||||
IntN(n int) int
|
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
|
// descend walks a caller's path to the node it names, pinning in sc the rows it
|
||||||
// selects or draws.
|
// 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
|
// docs/decisions.md#a-path-is-walked-once-without-drawing-before-it-is-walked-for-real
|
||||||
if _, err := (&pathProbe{}).walk(root, segments); err != nil {
|
if _, err := (&pathProbe{}).walk(root, segments); err != nil {
|
||||||
return nil, err
|
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.
|
// 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
|
var set holdSet
|
||||||
return render(s, n, renderScope{set: &set})
|
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
|
// 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.
|
// 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.
|
// 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) {
|
switch n := n.(type) {
|
||||||
case *choice:
|
case *choice:
|
||||||
return render(s, pick(s, n), sc)
|
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.
|
// render's loop, its hold set would move to the heap.
|
||||||
//
|
//
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func expandAnew(s *session, t *template) string {
|
func expandAnew(s *generatorState, t *template) string {
|
||||||
var set holdSet
|
var set holdSet
|
||||||
return expand(s, t, renderScope{set: &set})
|
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
|
// pick selects one item. Uniform choices are O(1); weighted choices are an
|
||||||
// O(log n) search over precomputed cumulative weights. compile guarantees a
|
// 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.
|
// 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 {
|
if c.cum == nil {
|
||||||
return c.items[s.IntN(len(c.items))]
|
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
|
// expand renders a template's compiled ops. compile validated every token, so this
|
||||||
// cannot fail.
|
// cannot fail.
|
||||||
func expand(s *session, t *template, sc renderScope) string {
|
func expand(s *generatorState, t *template, sc renderScope) string {
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
b.Grow(t.compiled.grow)
|
b.Grow(t.compiled.grow)
|
||||||
// One draw per held name, for this expansion only: a nested template and each
|
// 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}
|
// 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
|
// 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.
|
// 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 {
|
if isRef(a.head) && len(a.tail) > 0 {
|
||||||
return readReference(s, t, sc, a)
|
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
|
// readReference reads a reference path, kept in the render's hold for its group, so its draw
|
||||||
// spans the render.
|
// 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 {
|
if sc.set.trace != nil {
|
||||||
traceRead(sc.set.trace, t, sc, a)
|
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)
|
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 {
|
if r, done := held.value[a.path]; done {
|
||||||
return r
|
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
|
// 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.
|
// 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)
|
n = resolveChoice(s, n)
|
||||||
switch leaf := n.(type) {
|
switch leaf := n.(type) {
|
||||||
case *nullItem:
|
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
|
// 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
|
// rest of the expansion shares. Nested choices unwrap too: a draw is one value, not
|
||||||
// another set to pick from.
|
// 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) {
|
for c, ok := n.(*choice); ok; c, ok = n.(*choice) {
|
||||||
n = pick(s, c)
|
n = pick(s, c)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
// 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 {
|
if s.record != nil {
|
||||||
var set holdSet
|
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)
|
setColumn(fieldAt(v, s.fieldIndexes[i]), c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,7 +364,7 @@ func (s *structShape) fill(sess *session, v reflect.Value) {
|
|||||||
}
|
}
|
||||||
field = field.Elem()
|
field = field.Elem()
|
||||||
}
|
}
|
||||||
n.shape.fill(sess, field)
|
n.shape.fill(state, field)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -472,20 +472,20 @@ func (t *table) builtLookup() *rowLookup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// drawRow picks a row over the whole table.
|
// 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 {
|
if t.cum == nil {
|
||||||
return s.IntN(t.rowCount())
|
return s.IntN(t.rowCount())
|
||||||
}
|
}
|
||||||
return pickCum(s, t.cum)
|
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]
|
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
|
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.
|
// 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()
|
lookup := t.builtLookup()
|
||||||
k := t.parentT.cell(pr, t.parentT.keyIndex)
|
k := t.parentT.cell(pr, t.parentT.keyIndex)
|
||||||
rows := lookup.rowsByParent[k]
|
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
|
// 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
|
// pinned ancestor — its parent drawn inside that first where the ancestor is further up — or
|
||||||
// over the whole table, and pinned with its ancestors.
|
// 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 {
|
if r, ok := p.pinned(t); ok {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
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
|
- State at `pinSet.pin` that its caller proved r sits inside the nearest pinned
|
||||||
ancestor, which `pinRow` and `drawIn` do.
|
ancestor, which `pinRow` and `drawIn` do.
|
||||||
- Delete the prose two or more seats found empty: `loadData`'s comment, which also names
|
- Delete the prose two or more seats found empty: `loadData`'s comment, which also names
|
||||||
|
|||||||
+1
-1
@@ -70,7 +70,7 @@ func transformPrep(outer func(string) string) func([]string) callFn {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(internalError("transform arg %q reached prep unvalidated: %v", a[0], err))
|
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]
|
v := operands[0]
|
||||||
for i := len(chain) - 1; i >= 0; i-- {
|
for i := len(chain) - 1; i >= 0; i-- {
|
||||||
v = chain[i](v)
|
v = chain[i](v)
|
||||||
|
|||||||
Reference in New Issue
Block a user