Draw every {…} afresh, keep a pick only by name, and delete drawGroup with the hold and draw fences
This commit is contained in:
@@ -25,8 +25,6 @@ func (f *Generator) Fake(path string) (string, error) {
|
||||
return "", fmt.Errorf("fejkdata: %w", err)
|
||||
}
|
||||
f.loadShippedAt(segments)
|
||||
var draws renderDraws
|
||||
sc := renderScope{draws: &draws}
|
||||
n, pins, err := descend(f.rand, &f.root, segments)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("fejkdata: %s: %w", path, err)
|
||||
@@ -34,9 +32,8 @@ func (f *Generator) Fake(path string) (string, error) {
|
||||
if _, ok := n.(*folder); ok {
|
||||
return "", fmt.Errorf("fejkdata: %s names a folder, not a value", path)
|
||||
}
|
||||
group := sc.groupDraws()
|
||||
group.pins = pins
|
||||
sc, _ = sc.at(n, &group.pins).enter(n, &group.memo)
|
||||
var frames frameStack
|
||||
sc, _ := renderScope{frames: &frames}.at(n, &pins).enter(n, nil)
|
||||
return render(f.rand, n, sc), nil
|
||||
}
|
||||
|
||||
@@ -53,15 +50,14 @@ func descend(s *generatorState, root node, segments []string) (node, pinSet, err
|
||||
return n, *leafPins, nil
|
||||
}
|
||||
|
||||
// renderOnce renders n as one render, over draws of its own.
|
||||
// renderOnce renders n as one render, over frames of its own.
|
||||
func renderOnce(s *generatorState, n node) string {
|
||||
var draws renderDraws
|
||||
return render(s, n, renderScope{draws: &draws})
|
||||
var frames frameStack
|
||||
return render(s, n, renderScope{frames: &frames})
|
||||
}
|
||||
|
||||
// render evaluates a compiled node to a string. compile validates every node up
|
||||
// front, so rendering a compiled tree cannot fail. sc holds the reference draws the
|
||||
// render shares; each repeat iteration renders over draws of its own.
|
||||
// front, so rendering a compiled tree cannot fail.
|
||||
// A child this switch renders is one renderEdges must list too, or the fences miss it.
|
||||
func render(s *generatorState, n node, sc renderScope) string {
|
||||
switch n := n.(type) {
|
||||
@@ -81,10 +77,9 @@ func render(s *generatorState, n node, sc renderScope) string {
|
||||
}
|
||||
return n.t.cell(row, n.i)
|
||||
case *template:
|
||||
sc = sc.in(n)
|
||||
if n.repeat == 1 {
|
||||
if mark := sc.renderFrame(n); mark >= 0 {
|
||||
defer sc.draws.popFrames(mark)
|
||||
defer sc.frames.pop(mark)
|
||||
}
|
||||
if lit, fixed := n.fixedText(); fixed {
|
||||
return lit
|
||||
@@ -97,8 +92,8 @@ func render(s *generatorState, n node, sc renderScope) string {
|
||||
}
|
||||
}
|
||||
|
||||
// renderRepeat renders each iteration of t as a render of its own, inside the name scopes
|
||||
// rendering t, where a name bound outside t keeps its pick.
|
||||
// renderRepeat renders each iteration of t inside the name scopes rendering t, where a name bound
|
||||
// outside t keeps its pick, and a name t binds picks again.
|
||||
func renderRepeat(s *generatorState, t *template, sc renderScope) string {
|
||||
var b strings.Builder
|
||||
b.Grow(t.repeat * (t.compiled.grow + len(t.separator)))
|
||||
@@ -106,23 +101,17 @@ func renderRepeat(s *generatorState, t *template, sc renderScope) string {
|
||||
if i > 0 {
|
||||
b.WriteString(t.separator)
|
||||
}
|
||||
b.WriteString(expandAnew(s, t, sc.draws.frameStack, sc.base))
|
||||
if t.ownNameScope == nil {
|
||||
b.WriteString(expand(s, t, sc))
|
||||
continue
|
||||
}
|
||||
mark := sc.frames.push(newPickFrame(t.ownNameScope))
|
||||
b.WriteString(expand(s, t, sc))
|
||||
sc.frames.pop(mark)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// expandAnew expands one repeat iteration of t as a render of its own, in no group, inside the
|
||||
// name scopes of stack from base. Inlined into renderRepeat's loop, its draws would move to the heap.
|
||||
//
|
||||
//go:noinline
|
||||
func expandAnew(s *generatorState, t *template, stack *frameStack, base int) string {
|
||||
draws := renderDraws{frameStack: stack}
|
||||
if t.ownNameScope != nil {
|
||||
defer draws.popFrames(draws.pushFrame(newPickFrame(t.ownNameScope)))
|
||||
}
|
||||
return expand(s, t, renderScope{draws: &draws, base: base})
|
||||
}
|
||||
|
||||
// pick selects one item. Uniform choices are O(1); weighted choices are an
|
||||
// O(log n) search over precomputed cumulative weights.
|
||||
func pick(s *generatorState, c *choice) node {
|
||||
@@ -135,31 +124,19 @@ func pick(s *generatorState, c *choice) node {
|
||||
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
|
||||
// repeat iteration get their own, since each is its own expansion. A reference
|
||||
// path reads its draw group's memo in sc instead.
|
||||
var hold *drawMemo
|
||||
if len(t.compiled.held) > 0 {
|
||||
hold = &drawMemo{
|
||||
variant: make(map[string]node, len(t.compiled.held)),
|
||||
value: make(map[string]readValue, len(t.compiled.held)),
|
||||
}
|
||||
}
|
||||
for i := range t.compiled.ops {
|
||||
o := &t.compiled.ops[i]
|
||||
switch o.kind {
|
||||
case literalRun:
|
||||
b.WriteString(o.lit)
|
||||
case nameRead:
|
||||
b.WriteString(readField(s, t, hold, sc, o.arms[s.IntN(len(o.arms))]).text)
|
||||
b.WriteString(readField(s, t, sc, o.arms[s.IntN(len(o.arms))]).text)
|
||||
case builtinCall:
|
||||
// Read before the call, so the value a calc computes is the value the
|
||||
// format showed. calcVars fixed the order op.operands holds.
|
||||
var operands []string
|
||||
if len(o.operands) > 0 {
|
||||
operands = make([]string, len(o.operands))
|
||||
for j, a := range o.operands {
|
||||
operands[j] = readField(s, t, hold, sc, a).text
|
||||
operands[j] = readField(s, t, sc, a).text
|
||||
}
|
||||
}
|
||||
b.WriteString(o.call(s, b.String(), operands)) // b.String() is the output so far
|
||||
@@ -168,19 +145,10 @@ func expand(s *generatorState, t *template, sc renderScope) string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// readField renders one arm of a token. A field the expansion holds — a level some
|
||||
// token addresses by a dotted path that is not a reference, or a field an operand
|
||||
// reads — is drawn once and kept in hold, so {place.postal-code} and {place.locality}
|
||||
// read one row, either read twice gives one value, and a shown operand is the operand
|
||||
// computed. A field a read of a name addresses is kept in that name's pick.
|
||||
// Every other field is drawn afresh, so {word} {word} still draws twice.
|
||||
func readField(s *generatorState, t *template, hold *drawMemo, sc renderScope, a arm) readValue {
|
||||
if a.kind == refPathRead {
|
||||
return readReference(s, t, sc, a)
|
||||
}
|
||||
if sc.draws.trace != nil {
|
||||
traceRead(sc.draws.trace, t, sc, a)
|
||||
}
|
||||
// readField renders one arm of a token. A read of a name, or of a level a read of the name
|
||||
// rendering addresses, is kept in that name's pick; every other read draws afresh, so {word}
|
||||
// {word} draws twice and {p.a} {p.b} reads two draws of p.
|
||||
func readField(s *generatorState, t *template, sc renderScope, a arm) readValue {
|
||||
switch {
|
||||
case a.kind == namedRead:
|
||||
return readName(s, sc, a)
|
||||
@@ -188,56 +156,18 @@ func readField(s *generatorState, t *template, hold *drawMemo, sc renderScope, a
|
||||
return readUnder(s, t, sc, a)
|
||||
}
|
||||
sc.pick = nil
|
||||
switch {
|
||||
case a.kind == freshRead && isRef(a.head):
|
||||
return readValue{text: render(s, t.head(a.head), sc.entering())}
|
||||
case a.kind == freshRead:
|
||||
return readValue{text: render(s, t.head(a.head), sc)}
|
||||
}
|
||||
return readMemo(s, t, hold, nil, sc, a)
|
||||
}
|
||||
|
||||
// readReference reads a reference path, kept in its draw group's memo, so its draw
|
||||
// spans the render.
|
||||
func readReference(s *generatorState, t *template, sc renderScope, a arm) readValue {
|
||||
if sc.draws.trace != nil {
|
||||
traceRead(sc.draws.trace, t, sc, a)
|
||||
}
|
||||
group := sc.groupDraws()
|
||||
return readMemo(s, t, &group.memo, &group.pins, sc, a)
|
||||
}
|
||||
|
||||
func readMemo(s *generatorState, t *template, memo *drawMemo, pins *pinSet, sc renderScope, a arm) readValue {
|
||||
if r, done := memo.value[a.path]; done {
|
||||
return r
|
||||
}
|
||||
leaf, leafPins := drawSteps(s, t.head(a.head), a.steps, pins, memo, a.levels)
|
||||
mark := -1
|
||||
if pins != nil {
|
||||
sc, mark = sc.at(leaf, leafPins).enter(leaf, memo)
|
||||
if !isRef(a.head) {
|
||||
leaf, _ := drawSteps(s, t.head(a.head), a.steps, nil, nil, a.levels)
|
||||
return renderLeaf(s, leaf, sc)
|
||||
}
|
||||
var pins pinSet
|
||||
leaf, leafPins := drawSteps(s, t.head(a.head), a.steps, &pins, nil, a.levels)
|
||||
sc, mark := sc.at(leaf, leafPins).enter(leaf, nil)
|
||||
r := renderLeaf(s, leaf, sc)
|
||||
if mark >= 0 {
|
||||
sc.draws.popFrames(mark)
|
||||
}
|
||||
if memo.value == nil {
|
||||
memo.value = map[string]readValue{}
|
||||
}
|
||||
memo.value[a.path] = r
|
||||
sc.frames.pop(mark)
|
||||
return r
|
||||
}
|
||||
|
||||
func traceRead(trace renderTrace, t *template, sc renderScope, a arm) {
|
||||
var row renderedRow
|
||||
switch {
|
||||
case t.site.isCell():
|
||||
row = renderedRow{t.site.table, t.site.row}
|
||||
case t.site.isFormat():
|
||||
row = renderedRow{t.site.table, sc.rowOf(t.site.table)}
|
||||
}
|
||||
trace(strings.Clone(sc.group), row, a)
|
||||
}
|
||||
|
||||
// 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 *generatorState, n node, sc renderScope) readValue {
|
||||
@@ -247,15 +177,14 @@ func renderLeaf(s *generatorState, n node, sc renderScope) readValue {
|
||||
return readValue{null: true}
|
||||
case *template:
|
||||
if leaf.link.readsColumn != nil {
|
||||
return readReference(s, leaf, sc.in(leaf), leaf.link.readsColumn.a)
|
||||
return readField(s, leaf, sc, leaf.link.readsColumn.a)
|
||||
}
|
||||
}
|
||||
return readValue{text: render(s, n, sc)}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// resolveChoice resolves a choice to one variant. Nested choices unwrap too: a draw is one value,
|
||||
// not another set to pick from.
|
||||
func resolveChoice(s *generatorState, n node) node {
|
||||
for c, ok := n.(*choice); ok; c, ok = n.(*choice) {
|
||||
n = pick(s, c)
|
||||
|
||||
Reference in New Issue
Block a user