Give head, index, bound and draw one meaning each, name familyRoot, and type the token kinds

This commit is contained in:
2026-09-28 11:36:04 +02:00
committed by Lillem4n
parent cb064362d1
commit affbb9ac6f
14 changed files with 149 additions and 145 deletions
+3 -3
View File
@@ -16,8 +16,8 @@ package fejkdata
// Vocabulary
//
// - draw — one pick from a choice, or one row taken from a table, and the value a
// read produced: `pick`, `resolveChoice`, `table.drawRow`, `draw`.
// - draw — one pick from a choice, or one row taken from a table: `pick`,
// `resolveChoice`, `table.drawRow`.
// - expansion — one render of one format: `expand`.
// - render — what owns one `holdSet`, and so what one reference draw spans:
// `Generator.Fake`, `FakeRecord`, `Template.Fake` and `RecordTemplate.Fake`
@@ -31,7 +31,7 @@ package fejkdata
// - pin — fixing which row of a table the render uses, which the draw fences replay:
// `pinSet`, `pinSet.pin`, `table.drawIn`.
// - family — a table and every table reaching it through a chain of
// `table.parentT`, named by the root that chain ends at: `table.family`.
// `table.parentT`, named by the root that chain ends at: `table.familyRoot`.
// - whole — a read of a table with no selector and no descent, {/city}, landing on
// the `table` itself: it draws a row apart from every pin, so the fences keep the rows
// its cells stand in as `drawAt.wholePins`, beside `drawAt.wholeTable`.
+2 -2
View File
@@ -97,7 +97,7 @@ func checkOwnFamily(t *template) error {
seen[n] = true
for _, e := range renderEdges(n) {
if a, isRef := refRead(n, e.label); isRef {
if head, isTable := n.(*template).head(a.key).(*table); isTable && head.family() == own.family() {
if head, isTable := n.(*template).head(a.key).(*table); isTable && head.familyRoot() == own.familyRoot() {
return e, head, true
}
}
@@ -357,7 +357,7 @@ func checkFamilies(reads []pathRead) error {
continue
}
for _, o := range reads[:i] {
if o.tr == nil || o.at.group != r.at.group || alternatives(o.at, r.at) || o.tr.headTable.family() != r.tr.headTable.family() {
if o.tr == nil || o.at.group != r.at.group || alternatives(o.at, r.at) || o.tr.headTable.familyRoot() != r.tr.headTable.familyRoot() {
continue
}
if err := checkFamilyPair(o, r); err != nil {
+45 -38
View File
@@ -8,7 +8,7 @@ import (
// ftoken is one unit of a scanned format string: a literal rune or the body of a
// {…} token.
type ftoken struct {
kind byte // 'l' literal rune, 't' token body
isToken bool // a {…} body, else a literal rune
char rune
body string
}
@@ -24,7 +24,7 @@ func eachToken(format string, fn func(ftoken) error) error {
switch c := rs[i]; c {
case '{':
if i+1 < len(rs) && rs[i+1] == '{' {
t.kind, t.char = 'l', '{'
t.char = '{'
i++
break
}
@@ -38,17 +38,17 @@ func eachToken(format string, fn func(ftoken) error) error {
if end >= len(rs) {
return fmt.Errorf("unterminated '{' in %q", format)
}
t.kind, t.body = 't', string(rs[i+1:end])
t.isToken, t.body = true, string(rs[i+1:end])
i = end
case '}':
if i+1 < len(rs) && rs[i+1] == '}' {
t.kind, t.char = 'l', '}'
t.char = '}'
i++
break
}
return fmt.Errorf("lone '}' in %q; a literal brace is written }}", format)
default:
t.kind, t.char = 'l', c
t.char = c
}
if err := fn(t); err != nil {
return err
@@ -57,17 +57,24 @@ func eachToken(format string, fn func(ftoken) error) error {
return nil
}
// formatToken is one parsed unit of a format: a literal run, a field alternation or a
// builtin call.
// formatToken is one parsed unit of a format.
type formatToken struct {
kind byte // 'l' literal run, 'f' field alternation, 'b' builtin call
lit string // kind 'l'
kind tokenKind
lit string // literalRun
body string // the braces' content, as written
fn string // kind 'b'
args []string // kind 'b'
names []string // kind 'f': the '|' arms; kind 'b': the operands its builtin reads
fn string // builtinCall
args []string // builtinCall
names []string // fieldAlternation: the '|' arms; builtinCall: the operands its builtin reads
}
type tokenKind uint8
const (
builtinCall tokenKind = iota
fieldAlternation
literalRun
)
// parseFormat is the one reading of a format's tokens: a '(' outside a selector makes
// a token a call.
func parseFormat(format string) ([]formatToken, error) {
@@ -75,25 +82,25 @@ func parseFormat(format string) ([]formatToken, error) {
var lit strings.Builder
flush := func() {
if lit.Len() > 0 {
toks = append(toks, formatToken{kind: 'l', lit: lit.String()})
toks = append(toks, formatToken{kind: literalRun, lit: lit.String()})
lit.Reset()
}
}
err := eachToken(format, func(t ftoken) error {
if t.kind == 'l' {
if !t.isToken {
lit.WriteRune(t.char)
return nil
}
flush()
if indexOutside(t.body, '(') < 0 {
toks = append(toks, formatToken{kind: 'f', body: t.body, names: splitOutside(t.body, '|')})
toks = append(toks, formatToken{kind: fieldAlternation, body: t.body, names: splitOutside(t.body, '|')})
return nil
}
name, args, ok := funcCall(t.body)
if !ok {
return fmt.Errorf("malformed function token {%s}", t.body)
}
toks = append(toks, formatToken{kind: 'b', body: t.body, fn: name, args: args, names: builtinOperands(name, args)})
toks = append(toks, formatToken{kind: builtinCall, body: t.body, fn: name, args: args, names: builtinOperands(name, args)})
return nil
})
if err != nil {
@@ -207,9 +214,9 @@ func checkTokens(toks []formatToken, fields map[string]node) error {
func checkToken(t formatToken, fields map[string]node) error {
switch t.kind {
case 'b':
case builtinCall:
return checkFunc(t, fields)
case 'f':
case fieldAlternation:
for _, name := range t.names {
if isRef(name) {
if _, _, err := refShape(name); err != nil {
@@ -236,7 +243,7 @@ func checkArm(name string, fields map[string]node, wholeToken bool) error {
if err := checkSegments(a); err != nil {
return err
}
head, ok := fields[a.key]
field, ok := fields[a.key]
if !ok {
if a.key == "" {
return fmt.Errorf("a name is never empty, so this token can name no field")
@@ -253,7 +260,7 @@ func checkArm(name string, fields map[string]node, wholeToken bool) error {
}
return fmt.Errorf("no field %q", a.key)
}
if err := checkPath(head, a.tail, a.key); err != nil {
if err := checkPath(field, a.tail, a.key); err != nil {
return fmt.Errorf("field %q: %w", a.key, err)
}
return nil
@@ -294,24 +301,24 @@ func checkNoRepeatedArm(body string, names []string) error {
// arm is one alternative of a {a|b} token or one operand, split into the key
// `template.head` resolves and the tail of a dotted path into it. A non-empty tail
// is what makes the arm a bound draw: its head is drawn once per expansion (see
// is what makes the arm a held draw: its head is drawn once per expansion (see
// compileOps).
type arm struct {
spelling string // as written, for messages
key string
tail []string
levels []string // the key at each level the tail passes through, the head's first
levels []string // the key at each level the tail passes through, the arm's key first
path string // key and tail, the one path every way of writing this read shares
}
// splitArm splits one name into key and tail. refs maps a reference to what
// linkRefs bound it to; before linking, a reference is whole.
// linkRefs resolved it to; before linking, a reference is whole.
func splitArm(name string, refs map[string]refBinding) arm {
if isRef(name) {
b, bound := refs[name]
if !bound || len(b.tail) == 0 {
b, linked := refs[name]
if !linked || len(b.tail) == 0 {
key := name
if bound {
if linked {
key = b.key
}
return arm{spelling: name, key: key, path: key}
@@ -351,17 +358,17 @@ func checkSegments(a arm) error {
return nil
}
// callFn is a builtin bound to 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
// values of the operands it named, which expand read for it.
type callFn func(s *session, emitted string, operands []string) string
// op is one compiled unit of a format string: a literal run, a field alternation,
// or a builtin already bound to its args. compile builds these so render never
// or a builtin already prepared with its args. compile builds these so render never
// re-scans the format.
type op struct {
formatToken
arms []arm // kind 'f': the '|' alternatives, split into key and path once
arms []arm // fieldAlternation: the '|' alternatives, split into key and path once
call callFn
// operands are the fields the builtin reads, in the order its operands func
// fixed; expand reads them before the call. nil for a builtin that reads none.
@@ -369,7 +376,7 @@ type op struct {
}
// formatOps is a compiled format: its ops, the size of its literal text (to size
// the render buffer), and the names drawn once per expansion. bound maps each level
// the render buffer), and the names drawn once per expansion. pathLevels maps each level
// a path reads into to the first such path; held is every such level plus the
// fields an operand reads; holder maps each held name to the first reader holding
// it, for error messages. The maps are nil when the format holds nothing, so data
@@ -377,7 +384,7 @@ type op struct {
type formatOps struct {
ops []op
grow int
bound map[string]string
pathLevels map[string]string
held map[string]bool
holder map[string]string
heldLocal bool // some held name is kept by the expansion itself rather than the render's hold
@@ -396,11 +403,11 @@ func (c *formatOps) holdName(a arm, label string) {
c.holder[a.key] = label
}
if len(a.tail) > 0 {
if c.bound == nil {
c.bound = map[string]string{}
if c.pathLevels == nil {
c.pathLevels = map[string]string{}
}
if _, named := c.bound[a.key]; !named {
c.bound[a.key] = a.spelling
if _, named := c.pathLevels[a.key]; !named {
c.pathLevels[a.key] = a.spelling
}
}
}
@@ -432,12 +439,12 @@ func compileOps(toks []formatToken, refs map[string]refBinding) formatOps {
var c formatOps
for _, tok := range toks {
switch tok.kind {
case 'l':
case literalRun:
c.grow += len(tok.lit)
c.ops = append(c.ops, op{formatToken: tok})
case 'b':
case builtinCall:
c.function(tok, refs)
case 'f':
case fieldAlternation:
c.field(tok, refs)
}
}
+2 -2
View File
@@ -18,7 +18,7 @@ func walkNodes(root map[string]node, fn func(path string, n node) error) error {
}
// eachNode visits n and every node contained within it once, passing the dot path
// that reaches each. It never crosses a reference edge — a bound {/path} field is
// that reaches each. It never crosses a reference edge — a {/path} reference is
// skipped — so a single inline node is walked on its own.
func eachNode(n node, path string, fn func(path string, n node) error) error {
seen := map[node]bool{}
@@ -105,7 +105,7 @@ type renderEdge struct {
operand string
}
// reached names an edge as the author spelled it, the vocabulary boundReaders uses
// reached names an edge as the author spelled it, the vocabulary levelReaders uses
// for the sibling fence.
func (e renderEdge) reached() string {
if e.operand != "" {
+10 -10
View File
@@ -11,12 +11,12 @@ import (
// its sibling names, and a render one per group for its reference paths.
type hold struct {
variant map[string]node
value map[string]draw
value map[string]readValue
pins pinSet
}
// draw is what one read drew: its text, and whether it landed on a null.
type draw struct {
// readValue is what one read drew: its text, and whether it landed on a null.
type readValue struct {
text string
null bool
}
@@ -26,7 +26,7 @@ type draw struct {
// kept, 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) draw {
func readField(s *session, t *template, held *hold, sc renderScope, a arm) readValue {
if s.trace != nil {
traceRead(s.trace, t, sc, a)
}
@@ -34,7 +34,7 @@ func readField(s *session, t *template, held *hold, sc renderScope, a arm) draw
if len(a.tail) > 0 {
panic(fmt.Sprintf("fejkdata: %q reads a path into %q, which the expansion does not hold", a.spelling, a.key))
}
return draw{text: render(s, t.head(a.key), sc)}
return readValue{text: render(s, t.head(a.key), sc)}
}
d := readHold(held, sc, a)
if r, done := d.value[a.path]; done {
@@ -45,7 +45,7 @@ func readField(s *session, t *template, held *hold, sc renderScope, a arm) draw
}
r := renderLeaf(s, drawPath(t.head(a.key), a.tail, a.key, &pathDraw{s: s, pins: &d.pins, held: d, a: &a}), sc)
if d.value == nil {
d.value = map[string]draw{}
d.value = map[string]readValue{}
}
d.value[a.path] = r
return r
@@ -81,20 +81,20 @@ func readHold(held *hold, sc renderScope, a arm) *hold {
// 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) draw {
func renderLeaf(s *session, n node, sc renderScope) readValue {
n = resolveChoice(s, n)
switch leaf := n.(type) {
case *nullItem:
return draw{null: true}
return readValue{null: true}
case *template:
if leaf.readsColumn != nil {
return readField(s, leaf, nil, sc.in(leaf), leaf.readsColumn.a)
}
}
return draw{text: render(s, n, sc)}
return readValue{text: render(s, n, sc)}
}
// resolveChoice resolves a choice to one variant, so a bound 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
// another set to pick from.
func resolveChoice(s *session, n node) node {
+37 -37
View File
@@ -15,81 +15,81 @@ func heldCheck(path string, n node) error {
if !ok || len(t.held) == 0 {
return nil
}
readers := boundReaders(t.ops, t.bound)
for _, head := range heldHeads(t) {
if _, isPath := t.bound[head]; isPath && isRef(head) {
readers := levelReaders(t.ops, t.pathLevels)
for _, name := range heldNames(t) {
if _, isPath := t.pathLevels[name]; isPath && isRef(name) {
continue // held for the render: drawCheck compares every read of it, by path, across the render and its groups
}
if err := checkHeadHeld(t, head, readers); err != nil {
if err := checkNameHeld(t, name, readers); err != nil {
return fmt.Errorf("%s: %w", path, err)
}
}
return nil
}
// heldHeads lists a template's held names, operand heads first, then paths, each
// heldNames lists a template's held names, operands' first, then paths', each
// in name order: a level read both ways is reported by the operand's fence, and
// which overlap is reported does not vary.
func heldHeads(t *template) []string {
heads := make([]string, 0, len(t.held))
for head := range t.held {
heads = append(heads, head)
func heldNames(t *template) []string {
names := make([]string, 0, len(t.held))
for name := range t.held {
names = append(names, name)
}
sort.Slice(heads, func(i, j int) bool {
_, pi := t.bound[heads[i]]
_, pj := t.bound[heads[j]]
sort.Slice(names, func(i, j int) bool {
_, pi := t.pathLevels[names[i]]
_, pj := t.pathLevels[names[j]]
if pi != pj {
return !pi
}
return heads[i] < heads[j]
return names[i] < names[j]
})
return heads
return names
}
// heldNodes is what the hold of head answers for: a path pins the levels it
// heldNodes is what the hold of name answers for: a path pins the levels it
// passes through and the leaf it lands on, an operand exactly the value its render
// produces.
func heldNodes(t *template, head string, readers []reader) map[node]bool {
func heldNodes(t *template, name string, readers []reader) map[node]bool {
held := map[node]bool{}
if _, isPath := t.bound[head]; !isPath {
operandDraw(t.head(head), held)
if _, isPath := t.pathLevels[name]; !isPath {
operandDraw(t.head(name), held)
return held
}
for _, r := range readers {
if a := splitArm(r.spelling, t.refs); a.key == head {
coverPath(t.head(head), a.tail, held)
if a := splitArm(r.spelling, t.refs); a.key == name {
coverPath(t.head(name), a.tail, held)
}
}
return held
}
// checkHeadHeld rejects every route to what head's hold pins except the readers
// checkNameHeld rejects every route to what name's hold pins except the readers
// holding it. One seen set across the edges: a node that cannot reach the level
// cannot reach it by another route either, so it is walked once.
func checkHeadHeld(t *template, head string, readers []reader) error {
held := heldNodes(t, head, readers)
func checkNameHeld(t *template, name string, readers []reader) error {
held := heldNodes(t, name, readers)
if len(held) == 0 {
return nil // a fixed head holds nothing to reach
}
reader, isPath := t.bound[head]
reader, isPath := t.pathLevels[name]
seen := map[node]bool{}
for _, e := range renderEdges(t) {
if splitArm(e.label, t.refs).key == head || !renders(e.to, held, seen) {
if splitArm(e.label, t.refs).key == name || !renders(e.to, held, seen) {
continue
}
if isPath {
return fmt.Errorf("%s renders %q, which {%s} reads a path into; name the fields you want instead", e.reached(), head, reader)
return fmt.Errorf("%s renders %q, which {%s} reads a path into; name the fields you want instead", e.reached(), name, reader)
}
return fmt.Errorf("%s renders %q, which a {%s()} also reads; reach it one way so it is drawn once", e.reached(), head, operandReader(t, head))
return fmt.Errorf("%s renders %q, which a {%s()} also reads; reach it one way so it is drawn once", e.reached(), name, operandReader(t, name))
}
return nil
}
// operandReader names the builtin whose operand holds head.
func operandReader(t *template, head string) string {
// operandReader names the builtin whose operand holds name.
func operandReader(t *template, name string) string {
for _, o := range t.ops {
for _, a := range o.operands {
if a.key == head {
if a.key == name {
return o.fn
}
}
@@ -177,8 +177,8 @@ func renders(n node, want, seen map[node]bool) bool {
// disagree. Reads are compared in sorted order, so which pair is reported does not
// depend on where the tokens sit.
// docs/decisions.md#a-bare-reference-draws-each-time-a-reference-path-is-held
func checkNoOverlap(ops []op, bound map[string]string) error {
names := boundReaders(ops, bound)
func checkNoOverlap(ops []op, pathLevels map[string]string) error {
names := levelReaders(ops, pathLevels)
// Stable over one format-order scan, so two readers of one name (a token and a
// calc operand both naming "p") are reported as the format writes them.
sort.SliceStable(names, func(i, j int) bool { return names[i].path < names[j].path })
@@ -192,23 +192,23 @@ func checkNoOverlap(ops []op, bound map[string]string) error {
return nil
}
// reader is one way a format reaches a bound field: as written, by its path,
// reader is one way a format reaches a path level: as written, by its path,
// and how to name it.
type reader struct{ spelling, path, label string }
// boundReaders lists every way a format reaches a bound sibling field, in the order the
// levelReaders lists every way a format reaches a sibling path level, in the order the
// format writes them. An operand renders its field, so it names a level exactly
// as a token does; one scan finds both, which is what puts them in one order.
func boundReaders(ops []op, bound map[string]string) []reader {
func levelReaders(ops []op, pathLevels map[string]string) []reader {
var names []reader
for _, o := range ops {
for _, a := range o.operands {
if _, isBound := bound[a.key]; isBound && !isRef(a.key) {
if _, isLevel := pathLevels[a.key]; isLevel && !isRef(a.key) {
names = append(names, reader{a.spelling, a.path, fmt.Sprintf("%s operand %q", o.fn, a.spelling)})
}
}
for _, a := range o.arms {
if _, isBound := bound[a.key]; isBound && !isRef(a.key) {
if _, isLevel := pathLevels[a.key]; isLevel && !isRef(a.key) {
names = append(names, reader{a.spelling, a.path, "token {" + a.spelling + "}"})
}
}
+2 -2
View File
@@ -21,7 +21,7 @@ type renderScope struct {
}
func eagerHoldSet() holdSet {
return holdSet{unnamed: hold{variant: map[string]node{}, value: map[string]draw{}}}
return holdSet{unnamed: hold{variant: map[string]node{}, value: map[string]readValue{}}}
}
// renderOnce renders n as one render, over a hold set of its own.
@@ -67,7 +67,7 @@ func (sc renderScope) hold() *hold {
if sc.set.named == nil {
sc.set.named = map[string]*hold{}
}
d = &hold{variant: map[string]node{}, value: map[string]draw{}}
d = &hold{variant: map[string]node{}, value: map[string]readValue{}}
sc.set.named[strings.Clone(sc.group)] = d
}
return d
+7 -7
View File
@@ -62,17 +62,17 @@ type template struct {
// Filled by `template.compileFormat` at link, from `template.tokens` and `template.refs`:
ops []op // what expand walks
grow int // minimum output size, to size the render buffer
// bound maps each field the format addresses by dotted path to one path token
// pathLevels maps each field the format addresses by dotted path to one path token
// reading it, which is the half of an overlap the fences name. nil when the
// format takes no path.
bound map[string]string
// held is every name drawn once per expansion: the bound levels above, plus the
pathLevels map[string]string
// held is every name drawn once per expansion: the path levels above, plus the
// siblings a {calc()} reads. nil when the format holds nothing (see expand).
held map[string]bool
heldLocal bool // some held name is kept by the expansion itself, so expand makes its hold
// Filled by `linkTemplateRefs` and `keyDrawGroup`, from the assembled tree:
refs map[string]refBinding // each reference the format reads -> what it is bound to
refs map[string]refBinding // each reference the format reads -> what it resolves to
refHeads map[string]node // each refBinding.key -> the category it names
readsColumn *columnRead // set when the format is one reference alone reading a record's column
drawGroupKey string // its draw group keyed by its category: what a render reads its reference paths under
@@ -214,7 +214,7 @@ func (t *template) fixedText() (string, bool) {
switch {
case len(t.tokens) == 0:
return "", true
case len(t.tokens) == 1 && t.tokens[0].kind == 'l':
case len(t.tokens) == 1 && t.tokens[0].kind == literalRun:
return t.tokens[0].lit, true
}
return "", false
@@ -224,8 +224,8 @@ func (t *template) fixedText() (string, bool) {
// compiled reads.
func (t *template) compileFormat() error {
c := compileOps(t.tokens, t.refs)
t.ops, t.grow, t.bound, t.held, t.heldLocal = c.ops, c.grow, c.bound, c.held, c.heldLocal
if err := checkNoOverlap(t.ops, t.bound); err != nil {
t.ops, t.grow, t.pathLevels, t.held, t.heldLocal = c.ops, c.grow, c.pathLevels, c.held, c.heldLocal
if err := checkNoOverlap(t.ops, t.pathLevels); err != nil {
return err
}
return checkNoRepeatedRead(c)
+3 -3
View File
@@ -343,11 +343,11 @@ type pathDraw struct {
}
// drawPath walks w over a path proved first, by a probe or at load, so the walk
// cannot fail. head names n in the panic.
func drawPath(n node, tail []string, head string, w *pathDraw) node {
// cannot fail. level names n in the panic.
func drawPath(n node, tail []string, level string, w *pathDraw) node {
leaf, err := w.walk(n, tail)
if err != nil {
panic(fmt.Sprintf("fejkdata: %s: %v; the path should have been proved before it was drawn", join(head, joinSegments(tail)), err))
panic(fmt.Sprintf("fejkdata: %s: %v; the path should have been proved before it was drawn", join(level, joinSegments(tail)), err))
}
return leaf
}
+6 -6
View File
@@ -9,7 +9,7 @@ import (
// data root, {.a} from the folder this file sits in, {..a} from the folder above.
func isRef(name string) bool { return strings.HasPrefix(name, ".") || strings.HasPrefix(name, "/") }
// refBinding is what a reference was bound to: the head key its category is held
// refBinding is what a reference resolves to: the key its category is held
// under, and the tail read into it.
type refBinding struct {
key string
@@ -105,11 +105,11 @@ func (t *template) bindRefs(folder []string, path, category string, names []stri
if err != nil {
return fmt.Errorf("%s: reference {%s}: %w", path, name, err)
}
head, target, tail, err := resolveCategory(root, segments)
categorySegs, target, tail, err := resolveCategory(root, segments)
if err != nil {
return fmt.Errorf("%s: reference {%s}: %w", path, name, err)
}
key := "/" + strings.Join(head, ".")
key := "/" + strings.Join(categorySegs, ".")
if category != "" && key == "/"+category {
return fmt.Errorf("%s: reference {%s}: names the category it sits in; read a sibling field as a path, or move the shared value into its own category and reference that", path, name)
}
@@ -144,7 +144,7 @@ func columnReadOf(t *template) *columnRead {
// loneRef is the reference a format of one reference token and nothing else reads.
func loneRef(toks []formatToken) (string, bool) {
if len(toks) != 1 || toks[0].kind != 'f' || len(toks[0].names) != 1 || !isRef(toks[0].names[0]) {
if len(toks) != 1 || toks[0].kind != fieldAlternation || len(toks[0].names) != 1 || !isRef(toks[0].names[0]) {
return "", false
}
return toks[0].names[0], true
@@ -187,8 +187,8 @@ func eachTemplate(root map[string]node, fn func(folder []string, path string, t
}
// resolveCategory walks a dotted path through the folders to the category it
// names, returning that head, the node, and the tail left to read into it.
func resolveCategory(root map[string]node, segments []string) (head []string, target node, tail []string, err error) {
// names, returning that category's segments, the node, and the tail left to read into it.
func resolveCategory(root map[string]node, segments []string) (category []string, target node, tail []string, err error) {
var n node = &folder{children: root}
i := 0
for ; i < len(segments); i++ {
+4 -4
View File
@@ -114,17 +114,17 @@ func expand(s *session, t *template, sc renderScope) string {
if t.heldLocal {
held = &hold{
variant: make(map[string]node, len(t.held)),
value: make(map[string]draw, len(t.held)),
value: make(map[string]readValue, len(t.held)),
}
}
for i := range t.ops {
o := &t.ops[i]
switch o.kind {
case 'l':
case literalRun:
b.WriteString(o.lit)
case 'f':
case fieldAlternation:
b.WriteString(readField(s, t, held, sc, o.arms[s.IntN(len(o.arms))]).text)
case 'b':
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
+20 -20
View File
@@ -32,12 +32,12 @@ type table struct {
parentT *table
children map[string]*table
once sync.Once
index tableIndex
lookup rowLookup
}
// tableIndex is what selection and linked draws look up, built on the first draw
// rowLookup is what selection and linked draws look up, built on the first draw
// that needs it.
type tableIndex struct {
type rowLookup struct {
byName map[string][]int
rowsByParent map[string][]int
childCum map[string][]float64 // cumulative weights per parent key, nil when uniform
@@ -226,7 +226,7 @@ func (t *table) bindOptions(o tableOptionValues) error {
if t.nameIndex >= 0 && t.keyIndex < 0 && t.parentIndex < 0 {
return fmt.Errorf("name selects a row as a key does, and lists the rows it matches by their keys, so it needs a key column, or a parent inside which each name is one row; add key")
}
if err := t.indexKeys(); err != nil {
if err := t.mapKeys(); err != nil {
return err
}
if err := t.proveNamesInsideParent(); err != nil {
@@ -251,8 +251,8 @@ func (t *table) proveNamesInsideParent() error {
return nil
}
// indexKeys proves every key names one row, and keeps the index a link is proved by.
func (t *table) indexKeys() error {
// mapKeys proves every key names one row, and keeps the map a link is proved by.
func (t *table) mapKeys() error {
if t.keyIndex < 0 {
return nil
}
@@ -329,7 +329,7 @@ func (t *table) compileRowFormat(format string) error {
return err
}
for _, tok := range toks {
if tok.kind != 'f' {
if tok.kind != fieldAlternation {
continue
}
for _, name := range tok.names {
@@ -440,36 +440,36 @@ func (t *table) linkParent(path string, siblings map[string]node) error {
return nil
}
func (t *table) indexed() *tableIndex {
func (t *table) builtLookup() *rowLookup {
t.once.Do(func() {
if t.nameIndex >= 0 {
t.index.byName = make(map[string][]int, t.rowCount())
t.lookup.byName = make(map[string][]int, t.rowCount())
for r := 0; r < t.rowCount(); r++ {
n := t.cell(r, t.nameIndex)
t.index.byName[n] = append(t.index.byName[n], r)
t.lookup.byName[n] = append(t.lookup.byName[n], r)
}
}
if t.parentIndex >= 0 {
t.index.rowsByParent = map[string][]int{}
t.lookup.rowsByParent = map[string][]int{}
for r := 0; r < t.rowCount(); r++ {
k := t.cell(r, t.parentIndex)
t.index.rowsByParent[k] = append(t.index.rowsByParent[k], r)
t.lookup.rowsByParent[k] = append(t.lookup.rowsByParent[k], r)
}
if t.cum != nil {
t.index.childCum = make(map[string][]float64, len(t.index.rowsByParent))
for k, rows := range t.index.rowsByParent {
t.lookup.childCum = make(map[string][]float64, len(t.lookup.rowsByParent))
for k, rows := range t.lookup.rowsByParent {
cum, total := make([]float64, len(rows)), 0.0
for i, r := range rows {
w, _ := strconv.ParseFloat(t.cell(r, t.weightIndex), 64) // sumWeights proved it
total += w
cum[i] = total
}
t.index.childCum[k] = cum
t.lookup.childCum[k] = cum
}
}
}
})
return &t.index
return &t.lookup
}
// drawRow picks a row over the whole table. The session is concrete rather than the rng
@@ -488,7 +488,7 @@ func pickCum(s *session, cum []float64) int {
// drawUnder picks a row among those linked to parent row pr.
func (t *table) drawUnder(s *session, pr int) int {
ix := t.indexed()
ix := t.builtLookup()
k := t.parentT.cell(pr, t.parentT.keyIndex)
rows := ix.rowsByParent[k]
if ix.childCum == nil {
@@ -567,7 +567,7 @@ func (t *table) find(sel string, pins *pinSet) (int, error) {
if r, ok := t.byKey[sel]; ok {
return r, nil
}
rows := t.indexed().byName[sel]
rows := t.builtLookup().byName[sel]
if len(rows) > 1 {
rows = pins.inside(t, rows)
}
@@ -614,8 +614,8 @@ func (t *table) selectorSpelling(r int) string {
return fmt.Sprintf("%s line %d", t.file, r+2)
}
// family is the table a chain of parents ends at.
func (t *table) family() *table {
// familyRoot is the table a chain of parents ends at.
func (t *table) familyRoot() *table {
for t.parentT != nil {
t = t.parentT
}
-3
View File
@@ -21,9 +21,6 @@ Self-sufficiency 5.6), every seat capped by Locality, and all nine naming
This round, in order:
- Give `head`, `index`, `bound` and `draw` one meaning each, name `table.family`
for the root it returns, and replace the token kind bytes `'l'`, `'t'`, `'f'` and
`'b'`, whose `'l'` means different things in `ftoken` and `formatToken`.
- Start every render from one spelling of an empty hold set: `Fake` uses
`holdSet{}`, the record and struct paths `eagerHoldSet()`.
- Correct the false comments: `funcCall` names `checkFunc` where `parseFormat`
+1 -1
View File
@@ -157,7 +157,7 @@ func (p *valueProof) proveTemplate(t *template) proven {
o := t.ops[0]
body, name, args := o.body, o.fn, o.args
switch _, isTransform := transforms[name]; {
case o.kind == 'f':
case o.kind == fieldAlternation:
var leaves []node
for _, a := range o.arms {
leaves = append(leaves, pathLeaves(t.head(a.key), a.tail)...)