Name the draw option drawGroup, refuse one whose reads all sit behind a nested draw group, and record why the two fences stay apart
Tests / vet + fmt + tests (pull_request) Successful in 1m7s
Tests / vet + fmt + tests (pull_request) Successful in 1m7s
This commit is contained in:
@@ -127,8 +127,8 @@ renders nothing by `Fake`, and is compiled only so the tree's fences still run.
|
|||||||
The columns are the point, and their facts stay together: a record is one render, so
|
The columns are the point, and their facts stay together: a record is one render, so
|
||||||
columns that read a path into one category — `{/currency.code}` and
|
columns that read a path into one category — `{/currency.code}` and
|
||||||
`{/currency.symbol}` — read one draw of it ([References](#references)), and a
|
`{/currency.symbol}` — read one draw of it ([References](#references)), and a
|
||||||
[group](#group) draws a column apart. That one draw is also why the columns of one
|
[draw group](#draw-group) draws a column apart. That one draw is also why the columns of one
|
||||||
record may not overlap — `{/cat.a}`, or a bare `{/cat}` rendering it, beside
|
record may not overlap — `{/cat.a}`, or a bare `{/cat}`, beside
|
||||||
`{/cat.a.b}` is refused, naming the fields to write instead, as
|
`{/cat.a.b}` is refused, naming the fields to write instead, as
|
||||||
[One draw, one spelling](#one-draw-one-spelling) refuses that pair inside a single
|
[One draw, one spelling](#one-draw-one-spelling) refuses that pair inside a single
|
||||||
format. A column may not reference the record it belongs to by any spelling: `{/users.first}`
|
format. A column may not reference the record it belongs to by any spelling: `{/users.first}`
|
||||||
@@ -341,7 +341,7 @@ different datatypes.
|
|||||||
|
|
||||||
### Options and fields
|
### Options and fields
|
||||||
|
|
||||||
`format`, `weight`, `repeat`, `separator`, `datatype` and `group` are the only options;
|
`format`, `weight`, `repeat`, `separator`, `datatype` and `drawGroup` are the only options;
|
||||||
**any other key is a field** (see [Decisions](#decisions)). An object that does nothing a
|
**any other key is a field** (see [Decisions](#decisions)). An object that does nothing a
|
||||||
string can't — only a `format` — is rejected naming the string, as is a one-item
|
string can't — only a `format` — is rejected naming the string, as is a one-item
|
||||||
choice naming its item.
|
choice naming its item.
|
||||||
@@ -443,7 +443,7 @@ Renders e.g. `Hej, Pat Smith!`. A reference path into a category is held like a
|
|||||||
record — rather than one format: `{.person.femalefirst} {.person.last}` name one
|
record — rather than one format: `{.person.femalefirst} {.person.last}` name one
|
||||||
person, as do the same two references in sibling fields or a nested template, and
|
person, as do the same two references in sibling fields or a nested template, and
|
||||||
`{lowercase(.person.femalefirst)}` reads that same draw. Each `repeat` iteration is
|
`{lowercase(.person.femalefirst)}` reads that same draw. Each `repeat` iteration is
|
||||||
a render of its own, in no group, so it draws anew, and a [group](#group) holds a
|
a render of its own, in no group, so it draws anew, and a [draw group](#draw-group) holds a
|
||||||
draw apart. A bare reference names no field and makes its own picks each time —
|
draw apart. A bare reference names no field and makes its own picks each time —
|
||||||
`{/misc.uuid} {/misc.uuid}` is two draws — while the reference paths inside what it
|
`{/misc.uuid} {/misc.uuid}` is two draws — while the reference paths inside what it
|
||||||
renders still read the render's draws. Rejected at `New`: a path that is
|
renders still read the render's draws. Rejected at `New`: a path that is
|
||||||
@@ -451,27 +451,28 @@ unknown, names a folder, has no folder above, or reads a field not every variant
|
|||||||
of a choice carries, and a reference that leads back to its own value, directly,
|
of a choice carries, and a reference that leads back to its own value, directly,
|
||||||
mutually or through a chain.
|
mutually or through a chain.
|
||||||
|
|
||||||
### Group
|
### Draw group
|
||||||
|
|
||||||
A template may carry `group` to hold its reference draws apart: every reference path
|
A template may carry `drawGroup` to hold its reference draws apart: every reference path
|
||||||
it renders, however deep short of a `repeat`, reads the draw of that group, and the
|
it renders, however deep short of a `repeat` or a nested `drawGroup`, reads the draw of
|
||||||
templates of one category naming one group read one draw. A group name is local to
|
that group, and the templates of one category naming one group in one render read one
|
||||||
its category, so a category another one references never joins its groups by name;
|
draw. A name is local to its category, so a category another one references never joins
|
||||||
the unnamed group spans them all.
|
its groups by name; the unnamed group spans them all.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{ "format": "{payer} pays {payee}; signed {signature}",
|
{ "format": "{payer} pays {payee}; signed {signature}",
|
||||||
"payer": { "format": "{/sv_SE.person.femalefirst} {/sv_SE.person.last}", "group": "payer" },
|
"payer": { "format": "{/sv_SE.person.femalefirst} {/sv_SE.person.last}", "drawGroup": "payer" },
|
||||||
"payee": "{/sv_SE.person.femalefirst} {/sv_SE.person.last}",
|
"payee": "{/sv_SE.person.femalefirst} {/sv_SE.person.last}",
|
||||||
"signature": { "format": "{/sv_SE.person.last}", "group": "payer" } }
|
"signature": { "format": "{/sv_SE.person.last}", "drawGroup": "payer" } }
|
||||||
```
|
```
|
||||||
|
|
||||||
Renders e.g. `Sara Eriksson pays Ebba Lind; signed Eriksson`: the signature reads the
|
Renders e.g. `Sara Eriksson pays Ebba Lind; signed Eriksson`: the signature reads the
|
||||||
payer's draw, while the payee is drawn apart. Rejected at load, each naming nothing: a
|
payer's draw, while the payee is drawn apart. Rejected at load, each naming nothing: a
|
||||||
`group` of `""` (the default); one naming the group its template already draws in; one
|
`drawGroup` of `""` (the default); one naming the draw group its template already draws
|
||||||
on a template that renders no reference path short of a `repeat`, on a `repeat` itself —
|
in; one on a template that renders no reference path short of a `repeat` or a nested
|
||||||
each iteration renders in no group — or on an inline template's root, which nothing
|
`drawGroup`, on a `repeat` itself — each iteration renders in no draw group — or on an
|
||||||
references. So is a path reading into a level that carries a `group`.
|
inline template's root, which nothing references. So is a path reading into a level that
|
||||||
|
carries a `drawGroup`.
|
||||||
|
|
||||||
### Correlated fields
|
### Correlated fields
|
||||||
|
|
||||||
@@ -505,9 +506,9 @@ The sub-fields stay addressable — `Fake("address.place.locality")` renders, an
|
|||||||
|
|
||||||
A name any token reads as a path (`{p.first}`) or as an operand (`{calc(net * 2)}`,
|
A name any token reads as a path (`{p.first}`) or as an operand (`{calc(net * 2)}`,
|
||||||
`{uppercase(w)}`) is drawn **once per expansion**, a reference path (`{/cat.p.first}`)
|
`{uppercase(w)}`) is drawn **once per expansion**, a reference path (`{/cat.p.first}`)
|
||||||
**once per render** in its [group](#group), and every other route to either — a bare
|
**once per render** in its [draw group](#draw-group), and every other route to either — a bare
|
||||||
`{p}`, a second bare `{w}`, `{/cat.net}`, a nested template rendering `{/cat.p.last}`
|
`{p}`, a second bare `{w}`, `{/cat.net}`, a nested template rendering `{/cat.p.last}`
|
||||||
beside `{p.first}`, a bare `{/cat}` rendering what `{/cat.p.first}` reads, at any
|
beside `{p.first}`, a bare `{/cat}` beside `{/cat.p.first}`, at any
|
||||||
depth — is a load error naming the spelling to use. A name nothing reads that way is
|
depth — is a load error naming the spelling to use. A name nothing reads that way is
|
||||||
drawn each time: `{word} {word}` differs. An expansion is one render of one format, so
|
drawn each time: `{word} {word}` differs. An expansion is one render of one format, so
|
||||||
each nested template draws its own names again; a render is one `Fake` or one record,
|
each nested template draws its own names again; a render is one `Fake` or one record,
|
||||||
@@ -552,7 +553,7 @@ tokens add cost in proportion to the output.
|
|||||||
## Decisions
|
## Decisions
|
||||||
|
|
||||||
- **Options and fields share one namespace.** `format`, `weight`, `repeat`,
|
- **Options and fields share one namespace.** `format`, `weight`, `repeat`,
|
||||||
`separator`, `datatype` and `group` are reserved; every other key is a field. Nesting fields under a
|
`separator`, `datatype` and `drawGroup` are reserved; every other key is a field. Nesting fields under a
|
||||||
key, or prefixing options, would tax every template to guard against a
|
key, or prefixing options, would tax every template to guard against a
|
||||||
misspelt option.
|
misspelt option.
|
||||||
- **`{a|b}` stays beside nested choices.** `[[…], […]]` picks the same way, but
|
- **`{a|b}` stays beside nested choices.** `[[…], […]]` picks the same way, but
|
||||||
@@ -673,16 +674,22 @@ tokens add cost in proportion to the output.
|
|||||||
value's facts agree across its fields, nested templates and columns alike —
|
value's facts agree across its fields, nested templates and columns alike —
|
||||||
`{/currency.code}` in one field and `{/currency.symbol}` in another name one
|
`{/currency.code}` in one field and `{/currency.symbol}` in another name one
|
||||||
currency, whichever view renders them. A `repeat` iteration is a render of its
|
currency, whichever view renders them. A `repeat` iteration is a render of its
|
||||||
own, since repeating asks for another entity, and a [group](#group) names further
|
own, since repeating asks for another entity, and a [draw group](#draw-group) names further
|
||||||
entities within one render, so a payer and a payee are two groups over one
|
entities within one render, so a payer and a payee are two groups over one
|
||||||
`person` rather than two copies of it. Only references share: a sibling field is
|
`person` rather than two copies of it. Only references share: a sibling field is
|
||||||
local to its own expansion, so a `first` column does not silently bind to a
|
local to its own expansion, so a `first` column does not silently bind to a
|
||||||
`first` in the column next to it.
|
`first` in the column next to it.
|
||||||
- **A group name is local to its category.** A category's groups are its own
|
- **A draw group name is local to its category.** A category's groups are its own
|
||||||
entities, so a caller naming a group the same way never joins them by accident,
|
entities, so a caller naming a group the same way never joins them by accident,
|
||||||
and renaming a group inside one file changes no render elsewhere. The unnamed
|
and renaming a group inside one file changes no render elsewhere. The unnamed
|
||||||
group still spans categories, since facts that belong together across categories
|
group still spans categories, since facts that belong together across categories
|
||||||
must agree.
|
must agree.
|
||||||
|
- **The expansion hold and the render's draws are two fences.** One proves a sibling
|
||||||
|
path or an operand is reached only by its readers within an expansion, the other
|
||||||
|
does the same for reference paths across a render and its draw groups. They pin
|
||||||
|
different things — an operand pins the value its own render produced and stops at a
|
||||||
|
reference, a path pins every level it passes through — so one walk would carry both
|
||||||
|
rules and both scopes anyway, and tell them apart at every step.
|
||||||
- **A record's column set is fixed before the first draw.** Only a category-level
|
- **A record's column set is fixed before the first draw.** Only a category-level
|
||||||
template is a record: a path descending into a field, or naming a folder or a
|
template is a record: a path descending into a field, or naming a folder or a
|
||||||
choice, errors. A tail may pass through a choice whose variants carry different
|
choice, errors. A tail may pass through a choice whose variants carry different
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// drawSet is one render's reference draws: the unnamed group's, and each named group's.
|
// drawSet is one render's reference draws: the unnamed draw group's, and each named one's.
|
||||||
type drawSet struct {
|
type drawSet struct {
|
||||||
unnamed draws
|
unnamed draws
|
||||||
named map[string]*draws
|
named map[string]*draws
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawScope is where a render reads its reference paths: a draw set, in the group of the
|
// drawScope is where a render reads its reference paths: a draw set, in the draw group of the
|
||||||
// template rendering.
|
// template rendering.
|
||||||
type drawScope struct {
|
type drawScope struct {
|
||||||
set *drawSet
|
set *drawSet
|
||||||
@@ -31,15 +31,15 @@ func renderOnce(s *session, n node) string {
|
|||||||
return render(s, n, drawScope{set: &set})
|
return render(s, n, drawScope{set: &set})
|
||||||
}
|
}
|
||||||
|
|
||||||
// in is the scope t renders in: its group where it names one, else its caller's.
|
// in is the scope t renders in: its draw group where it names one, else its caller's.
|
||||||
func (sc drawScope) in(t *template) drawScope {
|
func (sc drawScope) in(t *template) drawScope {
|
||||||
if t.groupKey != "" {
|
if t.drawGroupKey != "" {
|
||||||
sc.group = t.groupKey
|
sc.group = t.drawGroupKey
|
||||||
}
|
}
|
||||||
return sc
|
return sc
|
||||||
}
|
}
|
||||||
|
|
||||||
// draws is the set's draws for sc's group.
|
// draws is the set's draws for sc's draw group.
|
||||||
func (sc drawScope) draws() *draws {
|
func (sc drawScope) draws() *draws {
|
||||||
if sc.group == "" {
|
if sc.group == "" {
|
||||||
return &sc.set.unnamed
|
return &sc.set.unnamed
|
||||||
@@ -55,36 +55,36 @@ func (sc drawScope) draws() *draws {
|
|||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
// groupOf reads a template's "group" (default ""), which a repeat cannot carry: each iteration
|
// drawGroupOf reads a template's "drawGroup" (default ""), which a repeat cannot carry: each
|
||||||
// renders in no group.
|
// iteration renders in no draw group.
|
||||||
func groupOf(m map[string]any, repeat int) (string, error) {
|
func drawGroupOf(m map[string]any, repeat int) (string, error) {
|
||||||
v, ok := m["group"]
|
v, ok := m["drawGroup"]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
name, ok := v.(string)
|
name, ok := v.(string)
|
||||||
switch {
|
switch {
|
||||||
case !ok:
|
case !ok:
|
||||||
return "", fmt.Errorf("group must be a string, got %T", v)
|
return "", fmt.Errorf("drawGroup must be a string, got %T", v)
|
||||||
case name == "":
|
case name == "":
|
||||||
return "", fmt.Errorf(`group "" is the default, so it has no effect; drop it`)
|
return "", fmt.Errorf(`drawGroup "" is the default, so it has no effect; drop it`)
|
||||||
case repeat > 1:
|
case repeat > 1:
|
||||||
return "", fmt.Errorf("group %q on a repeat names nothing, since each iteration is a render of its own; drop it", name)
|
return "", fmt.Errorf("drawGroup %q on a repeat names nothing, since each iteration is a render of its own; drop it", name)
|
||||||
}
|
}
|
||||||
return name, nil
|
return name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyGroup keys t's group by the category t sits in, "" for an inline template, so a group name is
|
// keyDrawGroup keys t's draw group by the category t sits in, "" for an inline template, so a name
|
||||||
// local to its category.
|
// is local to its category.
|
||||||
func (t *template) keyGroup(category string) {
|
func (t *template) keyDrawGroup(category string) {
|
||||||
if t.group != "" {
|
if t.drawGroup != "" {
|
||||||
t.groupKey = category + "/" + t.group
|
t.drawGroupKey = category + "/" + t.drawGroup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkNestedGroup refuses a template beneath one drawing in group that names group again, short of
|
// checkNestedDrawGroup refuses a template beneath one drawing in group that names group again,
|
||||||
// a repeat or another group.
|
// short of a repeat or another draw group.
|
||||||
func checkNestedGroup(fields map[string]node, group string) error {
|
func checkNestedDrawGroup(fields map[string]node, group string) error {
|
||||||
if group == "" {
|
if group == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -92,9 +92,9 @@ func checkNestedGroup(fields map[string]node, group string) error {
|
|||||||
walk = func(path string, n node) error {
|
walk = func(path string, n node) error {
|
||||||
t, isTemplate := n.(*template)
|
t, isTemplate := n.(*template)
|
||||||
switch {
|
switch {
|
||||||
case isTemplate && t.group == group:
|
case isTemplate && t.drawGroup == group:
|
||||||
return fmt.Errorf("%q names group %q, the group this template draws in already; drop it", path, group)
|
return fmt.Errorf("%q names drawGroup %q, the draw group this template draws in already; drop it", path, group)
|
||||||
case isTemplate && (t.group != "" || t.repeat > 1):
|
case isTemplate && (t.drawGroup != "" || t.repeat > 1):
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, c := range contained(n) {
|
for _, c := range contained(n) {
|
||||||
@@ -115,14 +115,15 @@ func checkNestedGroup(fields map[string]node, group string) error {
|
|||||||
// drawCheck fences each template of a scope as a render of its own, remembering which nodes read a
|
// drawCheck fences each template of a scope as a render of its own, remembering which nodes read a
|
||||||
// reference path.
|
// reference path.
|
||||||
type drawCheck struct {
|
type drawCheck struct {
|
||||||
reads map[node]bool
|
reads map[node]bool
|
||||||
|
splits map[node]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkGroup refuses a group that splits nothing: one whose render reads no reference path short of
|
// checkDrawGroup refuses a draw group that splits nothing: one whose render reads every reference
|
||||||
// a repeat.
|
// path inside a repeat or a nested draw group, which draw apart from it whatever it names.
|
||||||
func (c *drawCheck) checkGroup(path string, n node) error {
|
func (c *drawCheck) checkDrawGroup(path string, n node) error {
|
||||||
if t, ok := n.(*template); ok && t.group != "" && !c.readsPath(t) {
|
if t, ok := n.(*template); ok && t.drawGroup != "" && !c.splitsDraws(t) {
|
||||||
return fmt.Errorf("%s: group %q splits nothing, since nothing it renders reads a reference path; drop it", path, t.group)
|
return fmt.Errorf("%s: drawGroup %q splits nothing, since nothing it renders reads a reference path outside a repeat or a nested drawGroup; drop it", path, t.drawGroup)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -134,7 +135,7 @@ func (c *drawCheck) checkDraws(path string, n node) error {
|
|||||||
}
|
}
|
||||||
w := newDrawWalk(nil)
|
w := newDrawWalk(nil)
|
||||||
for _, e := range renderEdges(t) {
|
for _, e := range renderEdges(t) {
|
||||||
w.edge(t, e, drawAt{group: t.groupKey, route: drawRoute{e.reached(), e.label}})
|
w.edge(t, e, drawAt{group: t.drawGroupKey, route: drawRoute{e.reached(), e.label}})
|
||||||
}
|
}
|
||||||
if err := w.check(); err != nil {
|
if err := w.check(); err != nil {
|
||||||
return fmt.Errorf("%s: %w", path, err)
|
return fmt.Errorf("%s: %w", path, err)
|
||||||
@@ -142,8 +143,8 @@ func (c *drawCheck) checkDraws(path string, n node) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readsPath reports whether rendering n reads a reference path, short of a repeat, which renders over
|
// readsPath reports whether rendering n reads a reference path, short of a repeat, which renders
|
||||||
// draws of its own.
|
// over draws of its own.
|
||||||
func (c *drawCheck) readsPath(n node) bool {
|
func (c *drawCheck) readsPath(n node) bool {
|
||||||
if r, done := c.reads[n]; done {
|
if r, done := c.reads[n]; done {
|
||||||
return r
|
return r
|
||||||
@@ -162,11 +163,36 @@ func (c *drawCheck) readsPath(n node) bool {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// splitsDraws reports whether rendering n reads a reference path that n's own draw group answers
|
||||||
|
// for: one outside a repeat and outside a nested draw group, which hold their own draws.
|
||||||
|
func (c *drawCheck) splitsDraws(n node) bool {
|
||||||
|
if r, done := c.splits[n]; done {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
r := false
|
||||||
|
for _, e := range renderEdges(n) {
|
||||||
|
if a, _, isRef := refRead(n, e.label); (isRef && len(a.tail) > 0) || (!repeats(e.to) && !grouped(e.to) && c.splitsDraws(e.to)) {
|
||||||
|
r = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.splits == nil {
|
||||||
|
c.splits = map[node]bool{}
|
||||||
|
}
|
||||||
|
c.splits[n] = r
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
func repeats(n node) bool {
|
func repeats(n node) bool {
|
||||||
t, isTemplate := n.(*template)
|
t, isTemplate := n.(*template)
|
||||||
return isTemplate && t.repeat > 1
|
return isTemplate && t.repeat > 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func grouped(n node) bool {
|
||||||
|
t, isTemplate := n.(*template)
|
||||||
|
return isTemplate && t.drawGroup != ""
|
||||||
|
}
|
||||||
|
|
||||||
// refRead is the reference an edge of n reads, and the node it is bound to; false when the edge
|
// refRead is the reference an edge of n reads, and the node it is bound to; false when the edge
|
||||||
// reads none.
|
// reads none.
|
||||||
func refRead(n node, label string) (arm, node, bool) {
|
func refRead(n node, label string) (arm, node, bool) {
|
||||||
@@ -182,13 +208,13 @@ func refRead(n node, label string) (arm, node, bool) {
|
|||||||
func checkColumnDraws(t *template, columns []string) error {
|
func checkColumnDraws(t *template, columns []string) error {
|
||||||
w := newDrawWalk(t)
|
w := newDrawWalk(t)
|
||||||
for _, name := range columns {
|
for _, name := range columns {
|
||||||
w.walk(t.fields[name], drawAt{group: t.groupKey, route: drawRoute{spelling: fmt.Sprintf("column %q", name)}})
|
w.walk(t.fields[name], drawAt{group: t.drawGroupKey, route: drawRoute{spelling: fmt.Sprintf("column %q", name)}})
|
||||||
}
|
}
|
||||||
return w.check()
|
return w.check()
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawWalk gathers what one render reads by reference and what it draws afresh, each by group, for
|
// drawWalk gathers what one render reads by reference and what it draws afresh, each by draw group,
|
||||||
// check to compare. record is set for a record's columns, which may not read the record back.
|
// for check to compare. record is set for a record's columns, which may not read the record back.
|
||||||
type drawWalk struct {
|
type drawWalk struct {
|
||||||
record *template
|
record *template
|
||||||
reads []pathRead
|
reads []pathRead
|
||||||
@@ -198,7 +224,7 @@ type drawWalk struct {
|
|||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawAt is where a walk stands: the group it draws in, how the render's root reached it, the
|
// drawAt is where a walk stands: the draw group it draws in, how the render's root reached it, the
|
||||||
// reference it last crossed, and whether it renders inside a reference path's draw.
|
// reference it last crossed, and whether it renders inside a reference path's draw.
|
||||||
type drawAt struct {
|
type drawAt struct {
|
||||||
group string
|
group string
|
||||||
@@ -251,8 +277,8 @@ func (w *drawWalk) walk(n node, at drawAt) {
|
|||||||
if repeats(n) {
|
if repeats(n) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if t, isTemplate := n.(*template); isTemplate && t.groupKey != "" {
|
if t, isTemplate := n.(*template); isTemplate && t.drawGroupKey != "" {
|
||||||
at.group = t.groupKey
|
at.group = t.drawGroupKey
|
||||||
}
|
}
|
||||||
for _, e := range renderEdges(n) {
|
for _, e := range renderEdges(n) {
|
||||||
w.edge(n, e, at)
|
w.edge(n, e, at)
|
||||||
@@ -331,7 +357,7 @@ func (w *drawWalk) checkFreshDraws() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func overlapError(route drawRoute, ref string, into pathRead) error {
|
func overlapError(route drawRoute, ref string, into pathRead) error {
|
||||||
return fmt.Errorf("%s renders a level that %s reads a path into; name the fields you want instead, or draw them apart with a group", route.spelled(ref), into.at.route.spelled(into.a.name))
|
return fmt.Errorf("%s renders a level that %s reads a path into; name the fields you want instead, or draw them apart with a drawGroup", route.spelled(ref), into.at.route.spelled(into.a.name))
|
||||||
}
|
}
|
||||||
|
|
||||||
// spelled names the route, and the reference it reaches a draw by where its root edge is not that
|
// spelled names the route, and the reference it reaches a draw by where its root edge is not that
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ func checkRenders(s nodeScope) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fence := &drawCheck{}
|
fence := &drawCheck{}
|
||||||
if err := s(fence.checkGroup); err != nil {
|
if err := s(fence.checkDrawGroup); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s(fence.checkDraws); err != nil {
|
if err := s(fence.checkDraws); err != nil {
|
||||||
|
|||||||
@@ -136,8 +136,8 @@ func inputValue(input string) (any, error) {
|
|||||||
// bindInline links an inline node's references against root and runs check over it, naming its
|
// bindInline links an inline node's references against root and runs check over it, naming its
|
||||||
// nodes from label.
|
// nodes from label.
|
||||||
func bindInline(n node, label string, root map[string]node, check func(nodeScope) error) error {
|
func bindInline(n node, label string, root map[string]node, check func(nodeScope) error) error {
|
||||||
if t, isTemplate := n.(*template); isTemplate && t.group != "" {
|
if t, isTemplate := n.(*template); isTemplate && t.drawGroup != "" {
|
||||||
return fmt.Errorf("%s: group %q names nothing, since nothing can reference an inline template; drop it", label, t.group)
|
return fmt.Errorf("%s: drawGroup %q names nothing, since nothing can reference an inline template; drop it", label, t.drawGroup)
|
||||||
}
|
}
|
||||||
scope := inlineScope(n, label)
|
scope := inlineScope(n, label)
|
||||||
if err := linkNodeRefs(scope, root); err != nil {
|
if err := linkNodeRefs(scope, root); err != nil {
|
||||||
@@ -154,7 +154,7 @@ func linkNodeRefs(scope nodeScope, root map[string]node) error {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
t.keyGroup("")
|
t.keyDrawGroup("")
|
||||||
for _, name := range refTokens(t.format) {
|
for _, name := range refTokens(t.format) {
|
||||||
sigil, rest, err := refShape(name)
|
sigil, rest, err := refShape(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -58,12 +58,12 @@ type template struct {
|
|||||||
bound map[string]string
|
bound map[string]string
|
||||||
// held is every name drawn once per expansion: the bound levels above, plus the
|
// held is every name drawn once per expansion: the bound levels above, plus the
|
||||||
// siblings a {calc()} reads. nil when the format holds nothing (see expand).
|
// siblings a {calc()} reads. nil when the format holds nothing (see expand).
|
||||||
held map[string]bool
|
held map[string]bool
|
||||||
fromString bool // written as a JSON string rather than an object
|
fromString bool // written as a JSON string rather than an object
|
||||||
readsColumn *columnRead // set when the format is one reference alone reading a record's column
|
readsColumn *columnRead // set when the format is one reference alone reading a record's column
|
||||||
record bool // compiled at the top without a repeat, so its fields are record columns
|
record bool // compiled at the top without a repeat, so its fields are record columns
|
||||||
group string // the group it draws in, as written; "" keeps its caller's
|
drawGroup string // the draw group it draws in, as written; "" keeps its caller's
|
||||||
groupKey string // group keyed by its category once linked, what a render reads its reference paths under
|
drawGroupKey string // its draw group keyed by its category once linked: what a render reads its reference paths under
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*template) isNode() {}
|
func (*template) isNode() {}
|
||||||
@@ -249,10 +249,10 @@ func compileTemplate(m map[string]any, pos position) (node, error) {
|
|||||||
if err := checkTokens(o.format, fields); err != nil {
|
if err := checkTokens(o.format, fields); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := checkNestedGroup(fields, o.group); err != nil {
|
if err := checkNestedDrawGroup(fields, o.group); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
t := &template{format: o.format, fields: fields, repeat: o.repeat, separator: o.separator, datatype: o.datatype, group: o.group, record: fieldPos == inColumn}
|
t := &template{format: o.format, fields: fields, repeat: o.repeat, separator: o.separator, datatype: o.datatype, drawGroup: o.group, record: fieldPos == inColumn}
|
||||||
if err := t.compileFormat(); err != nil {
|
if err := t.compileFormat(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -284,7 +284,7 @@ func readOptions(m map[string]any, pos position) (templateOptions, error) {
|
|||||||
if o.datatype, err = datatypeOf(m, pos); err != nil {
|
if o.datatype, err = datatypeOf(m, pos); err != nil {
|
||||||
return o, err
|
return o, err
|
||||||
}
|
}
|
||||||
if o.group, err = groupOf(m, repeat); err != nil {
|
if o.group, err = drawGroupOf(m, repeat); err != nil {
|
||||||
return o, err
|
return o, err
|
||||||
}
|
}
|
||||||
if sv, ok := m["separator"]; ok {
|
if sv, ok := m["separator"]; ok {
|
||||||
@@ -419,7 +419,7 @@ func checkPathNames(path string) error {
|
|||||||
// field. These names can never be fields.
|
// field. These names can never be fields.
|
||||||
func isOption(name string) bool {
|
func isOption(name string) bool {
|
||||||
switch name {
|
switch name {
|
||||||
case "datatype", "format", "group", "repeat", "separator", "weight":
|
case "datatype", "drawGroup", "format", "repeat", "separator", "weight":
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ func checkPath(n node, tail []string, level string) error {
|
|||||||
switch {
|
switch {
|
||||||
case t.repeat > 1:
|
case t.repeat > 1:
|
||||||
return fmt.Errorf("the level %q carries a repeat, which a path reading one draw of it cannot apply", name)
|
return fmt.Errorf("the level %q carries a repeat, which a path reading one draw of it cannot apply", name)
|
||||||
case t.group != "":
|
case t.drawGroup != "":
|
||||||
return fmt.Errorf("the level %q carries a group, which a path reading into it cannot apply", name)
|
return fmt.Errorf("the level %q carries a drawGroup, which a path reading into it cannot apply", name)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -72,7 +72,7 @@ func refSegments(name string, folder []string) ([]string, error) {
|
|||||||
// error, never a random render-time one.
|
// error, never a random render-time one.
|
||||||
func linkRefs(root map[string]node) error {
|
func linkRefs(root map[string]node) error {
|
||||||
return eachTemplate(root, func(folder []string, path string, t *template) error {
|
return eachTemplate(root, func(folder []string, path string, t *template) error {
|
||||||
t.keyGroup(strings.Join(strings.Split(path, ".")[:len(folder)+1], "."))
|
t.keyDrawGroup(strings.Join(strings.Split(path, ".")[:len(folder)+1], "."))
|
||||||
return linkTemplateRefs(folder, path, t, root)
|
return linkTemplateRefs(folder, path, t, root)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user