Reject a record's top-level repeat, share one record fence between both entry points, and give the renderer one spelling

This commit is contained in:
2026-09-04 08:40:58 +02:00
parent 54e6865b11
commit 8cb8cf6334
6 changed files with 146 additions and 127 deletions
+12 -17
View File
@@ -27,7 +27,7 @@ func (f *Generator) Fake(path string) (string, error) {
if _, ok := n.(*group); ok {
return "", fmt.Errorf("fejkdata: %s names a folder, not a value", path)
}
return render(f.rand, n), nil
return render(f.rand, n, nil), nil
}
// descend walks named fields to the node a path names. It is the one render-side
@@ -50,23 +50,18 @@ func descend(s *session, root node, segments []string) (node, error) {
}
// render evaluates a compiled node to a string. compile validates every node up
// front, so rendering a compiled tree cannot fail.
func render(s *session, n node) string {
return renderShared(s, n, nil)
}
// renderShared is render with a shared draw context: the draws a record shares
// across its columns; a nil shared is a standalone render.
func renderShared(s *session, n node, shared *draws) string {
// front, so rendering a compiled tree cannot fail. refScope carries the draws a
// reference shares beyond its own expansion; nil keeps every reference local.
func render(s *session, n node, refScope *draws) string {
switch n := n.(type) {
case *choice:
return renderShared(s, pick(s, n), shared)
return render(s, pick(s, n), refScope)
case *template:
if n.repeat == 1 {
if n.fixed {
return n.lit
}
return expand(s, n, shared)
return expand(s, n, refScope)
}
var b strings.Builder
b.Grow(n.repeat * (n.grow + len(n.separator)))
@@ -74,7 +69,7 @@ func renderShared(s *session, n node, shared *draws) string {
if i > 0 {
b.WriteString(n.separator)
}
b.WriteString(expand(s, n, shared))
b.WriteString(expand(s, n, refScope))
}
return b.String()
default:
@@ -96,12 +91,12 @@ func pick(r rng, c *choice) node {
// expand renders a template's compiled ops. compile validated every token, so this
// cannot fail.
func expand(s *session, t *template, shared *draws) string {
func expand(s *session, t *template, refScope *draws) string {
var b strings.Builder
b.Grow(t.grow)
// One draw per held name, for this expansion only: a nested template and each
// repeat iteration get their own, since each is its own expansion. A shared
// draw context, when a record supplies one, overrides that for references.
// repeat iteration get their own, since each is its own expansion. A reference
// reads the caller's scope instead, whenever one was supplied.
var held *draws
if len(t.held) > 0 {
held = &draws{
@@ -115,7 +110,7 @@ func expand(s *session, t *template, shared *draws) string {
case 'l':
b.WriteString(o.lit)
case 'f':
b.WriteString(readField(s, t, held, shared, o.arms[s.IntN(len(o.arms))]))
b.WriteString(readField(s, t, held, refScope, o.arms[s.IntN(len(o.arms))]))
case 'b':
// Read before the call, so the value a calc computes is the value the
// format showed. calcVars fixed the order op.operands holds.
@@ -123,7 +118,7 @@ func expand(s *session, t *template, shared *draws) string {
if len(o.operands) > 0 {
operands = make([]string, len(o.operands))
for j, a := range o.operands {
operands[j] = readField(s, t, held, shared, a)
operands[j] = readField(s, t, held, refScope, a)
}
}
b.WriteString(o.call(s, b.String(), operands)) // b.String() is the output so far