Answer stability round 1 and product-owner round 1: refuse a field binding read once from above its binder, word the self-read refusal plainly, say how a bound field's column keeps the pick, and ask item 96
This commit is contained in:
+1
-3
@@ -21,9 +21,7 @@ replacement, and each removed path, column or flag.
|
|||||||
`{w} {uppercase(w)}` and `{/person.first} {/person.last}` are two draws each, which
|
`{w} {uppercase(w)}` and `{/person.first} {/person.last}` are two draws each, which
|
||||||
`{p as q}{q.a} {q.b}` and `{/person as n}{n.first} {n.last}` make one. `{x as n}`
|
`{p as q}{q.a} {q.b}` and `{/person as n}{n.first} {n.last}` make one. `{x as n}`
|
||||||
binds a sibling field as well as a reference, and a struct's tags share its names,
|
binds a sibling field as well as a reference, and a struct's tags share its names,
|
||||||
`{/sv_SE.person as p}{p.first}` beside `{p.sex}`. `drawGroup` is no longer an option, and `New` no longer
|
`{/sv_SE.person as p}{p.first}` beside `{p.sex}`.
|
||||||
refuses a read beside a path into what it reads, or reads of one table family that
|
|
||||||
could disagree.
|
|
||||||
- First release: the CLI, the library and the shipped data set.
|
- First release: the CLI, the library and the shipped data set.
|
||||||
- Table categories: a category JSON naming a `rows` TSV beside it, with the options
|
- Table categories: a category JSON naming a `rows` TSV beside it, with the options
|
||||||
`key`, `name`, `weight` and `parent`; a path selects a row by key or name,
|
`key`, `name`, `weight` and `parent`; a path selects a row by key or name,
|
||||||
|
|||||||
@@ -136,7 +136,9 @@ it as nothing.
|
|||||||
|
|
||||||
A record is one render, so its columns read one pick of each [name](#names) its category
|
A record is one render, so its columns read one pick of each [name](#names) its category
|
||||||
binds: `"code": "{/currency as c}{c.code}"` beside `"symbol": "{c.symbol}"` is one
|
binds: `"code": "{/currency as c}{c.code}"` beside `"symbol": "{c.symbol}"` is one
|
||||||
currency, while `{/currency.code}` and `{/currency.symbol}` in two columns may name two.
|
currency, while `{/currency.code}` and `{/currency.symbol}` in two columns may name two. A
|
||||||
|
field a name binds draws afresh as a column of its own, so write the column through the name,
|
||||||
|
`"where": "{p}"` beside `{place as p}`, for it to agree with the columns reading `p`.
|
||||||
|
|
||||||
A category never references itself: `{/users.first}` or a bare `{/users}` inside `users`
|
A category never references itself: `{/users.first}` or a bare `{/users}` inside `users`
|
||||||
describes a draw other than the fields beside it. Read a sibling as a field, and put a
|
describes a draw other than the fields beside it. Read a sibling as a field, and put a
|
||||||
@@ -754,8 +756,7 @@ Refused at `New`, each error naming what to write instead:
|
|||||||
and outside it;
|
and outside it;
|
||||||
- a name that is a field or an option;
|
- a name that is a field or an option;
|
||||||
- a binding of anything but a reference or a path into a field of the template binding it;
|
- a binding of anything but a reference or a path into a field of the template binding it;
|
||||||
- a read of a name inside the field it binds, which would render itself: read it outside the
|
- a read of a name inside the field it binds: read it outside the field;
|
||||||
field;
|
|
||||||
- a binding in a choice's item, which every other item would leave unbound;
|
- a binding in a choice's item, which every other item would leave unbound;
|
||||||
- a binding nothing reads, or one read once outside a `repeat` nested where it is bound,
|
- a binding nothing reads, or one read once outside a `repeat` nested where it is bound,
|
||||||
which the spelling it binds draws the same way: `{/word as w}{w}` is `{/word}`, and
|
which the spelling it binds draws the same way: `{/word as w}{w}` is `{/word}`, and
|
||||||
|
|||||||
+3
-1
@@ -299,7 +299,9 @@ not agree on one among themselves; each must only hold that type.
|
|||||||
The fields an embedded struct promotes are the struct's own — `e.First`, as
|
The fields an embedded struct promotes are the struct's own — `e.First`, as
|
||||||
`encoding/json` and SQL mappers read them — so they are columns of its record and share
|
`encoding/json` and SQL mappers read them — so they are columns of its record and share
|
||||||
its names: one tag may bind `{/person as p}{p.first}` and another read `{p.sex}` (goal 4.1),
|
its names: one tag may bind `{/person as p}{p.first}` and another read `{p.sex}` (goal 4.1),
|
||||||
while a path tag draws afresh as `{/path}` does (goal 5.2); a tagged field that another field hides is refused, not dropped. A named
|
while a path tag draws afresh as `{/path}` does (goal 5.2). A tag binding a name only an
|
||||||
|
embedding type reads is refused on the embedded type alone, as any name read once is, so the
|
||||||
|
embedder binds it in a tag of its own; a tagged field that another field hides is refused, not dropped. A named
|
||||||
struct field is another entity and a record of its own. `fake:"-"` leaves a struct
|
struct field is another entity and a record of its own. `fake:"-"` leaves a struct
|
||||||
field, embedded or named, unfilled, so no name may be `-`; a pointer back to a struct
|
field, embedded or named, unfilled, so no name may be `-`; a pointer back to a struct
|
||||||
already being filled is left alone, since filling it would never end. `New` cannot see a
|
already being filled is left alone, since filling it would never end. `New` cannot see a
|
||||||
|
|||||||
+27
-5
@@ -19,7 +19,7 @@ func checkNameReads(path string, t *template) error {
|
|||||||
}
|
}
|
||||||
return namedReads(t, func(o *op, a *arm) error {
|
return namedReads(t, func(o *op, a *arm) error {
|
||||||
if a.named.bindsField() && rendersInside(compilePath(a.named.head, a.named.tail).leaves, t) {
|
if a.named.bindsField() && rendersInside(compilePath(a.named.head, a.named.tail).leaves, t) {
|
||||||
return fmt.Errorf("%s: token {%s}: name %q is read inside %q, the field it binds, which would render itself; read the name outside that field", t.site.label(path), o.body, a.named.name, a.named.ref)
|
return fmt.Errorf("%s: token {%s}: name %q is read inside %q, the field it binds; read the name outside that field", t.site.label(path), o.body, a.named.name, a.named.ref)
|
||||||
}
|
}
|
||||||
for _, leaf := range a.leaves {
|
for _, leaf := range a.leaves {
|
||||||
if err := a.named.checkOnce(a.spelling, leaf, a.path); err != nil {
|
if err := a.named.checkOnce(a.spelling, leaf, a.path); err != nil {
|
||||||
@@ -58,15 +58,22 @@ func rendersInside(nodes []node, t *template) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checkUses refuses a binding read once at a spot the bound spelling can stand: that spelling
|
// checkUses refuses a binding read once at a spot the bound spelling can stand: that spelling
|
||||||
// draws the same way without the name. A field stands only beside its binding. Where the spelling
|
// draws the same way without the name. A field stands where the reading template reaches the
|
||||||
// would be a CLI argument or tag of one reference alone, that entry point's own refusal then
|
// binder through fields. Where the spelling would be a CLI argument or tag of one reference alone,
|
||||||
// names the bare path.
|
// that entry point's own refusal then names the bare path.
|
||||||
func (b *nameBinding) checkUses() error {
|
func (b *nameBinding) checkUses() error {
|
||||||
r := b.uses[0]
|
r := b.uses[0]
|
||||||
if len(b.uses) > 1 || r.nested || b.bindsField() && r.in != b.binder {
|
if len(b.uses) > 1 || r.nested {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
spelling := b.ref
|
spelling := b.ref
|
||||||
|
if b.bindsField() {
|
||||||
|
down, reaches := fieldPathTo(r.in, b.binder)
|
||||||
|
if !reaches {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
spelling = joinSegments(append(down, b.ref))
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(r.tail, ".."):
|
case strings.HasPrefix(r.tail, ".."):
|
||||||
spelling += r.tail
|
spelling += r.tail
|
||||||
@@ -82,6 +89,21 @@ func (b *nameBinding) checkUses() error {
|
|||||||
return fmt.Errorf("name %q is read once, so it keeps no pick for another read; write %s where it is read, and drop the token", b.name, spelling)
|
return fmt.Errorf("name %q is read once, so it keeps no pick for another read; write %s where it is read, and drop the token", b.name, spelling)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fieldPathTo is the fields leading from t down to the template target, if t reaches it so.
|
||||||
|
func fieldPathTo(t, target *template) ([]string, bool) {
|
||||||
|
if t == target {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
for _, name := range sortedNames(t.fields) {
|
||||||
|
if sub, isTemplate := t.fields[name].(*template); isTemplate {
|
||||||
|
if rest, reaches := fieldPathTo(sub, target); reaches {
|
||||||
|
return append([]string{name}, rest...), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
// calcReads reports whether a calc reads spelling as one operand.
|
// calcReads reports whether a calc reads spelling as one operand.
|
||||||
func calcReads(spelling string) bool {
|
func calcReads(spelling string) bool {
|
||||||
n, err := parseCalc(spelling)
|
n, err := parseCalc(spelling)
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ func TestAFieldBindingIsRefusedWhereNoFieldIs(t *testing.T) {
|
|||||||
`{"format":"{nope as p}{p}{p}","place":"x"}`: `no field "nope"`,
|
`{"format":"{nope as p}{p}{p}","place":"x"}`: `no field "nope"`,
|
||||||
`{"format":"{place as p}{p.x}","place":{"format":"{x}","x":["a","b"]}}`: `write {place.x} where it is read`,
|
`{"format":"{place as p}{p.x}","place":{"format":"{x}","x":["a","b"]}}`: `write {place.x} where it is read`,
|
||||||
`{"format":"{place as p}{p}","place":["x","y"]}`: `write {place} where it is read`,
|
`{"format":"{place as p}{p}","place":["x","y"]}`: `write {place} where it is read`,
|
||||||
|
`{"format":"{n}|{x}","x":{"format":"{a as n}{a}","a":["1","2"]}}`: `write {x.a} where it is read`,
|
||||||
`{"format":"{place as p}{calc(p * 2)}","place":["1","2"]}`: `write place where it is read`,
|
`{"format":"{place as p}{calc(p * 2)}","place":["1","2"]}`: `write place where it is read`,
|
||||||
`{"format":"{place as place}{place}{place}","place":["x","y"]}`: `name "place" is a field of the root template too`,
|
`{"format":"{place as place}{place}{place}","place":["x","y"]}`: `name "place" is a field of the root template too`,
|
||||||
} {
|
} {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
`Bar = 9`
|
`Bar = 9`
|
||||||
|
|
||||||
`Next ID = 96`
|
`Next ID = 97`
|
||||||
|
|
||||||
| Goal | W |
|
| Goal | W |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -72,6 +72,7 @@
|
|||||||
| 15 | 0.1.0 | | **Add the remaining `misc` tables and templates, one row of its detail's table per chunk.** | 3 | 9 | 8 | 8 | 2, 15, 14 | 25.9 |
|
| 15 | 0.1.0 | | **Add the remaining `misc` tables and templates, one row of its detail's table per chunk.** | 3 | 9 | 8 | 8 | 2, 15, 14 | 25.9 |
|
||||||
| 57 | 0.1.0 | | **Rewrite `CHANGELOG.md`'s `[Unreleased]` as what v0.1.0 holds.** | 1 | 2 | 6 | 6 | 1.1 | 22.5 |
|
| 57 | 0.1.0 | | **Rewrite `CHANGELOG.md`'s `[Unreleased]` as what v0.1.0 holds.** | 1 | 2 | 6 | 6 | 1.1 | 22.5 |
|
||||||
| 34 | 0.1.0 | | **Add `{isin()}`, `{cusip()}`, `{aba()}` and `{vin()}`.** | 2 | 4 | 6 | 7 | 4 | 20.9 |
|
| 34 | 0.1.0 | | **Add `{isin()}`, `{cusip()}`, `{aba()}` and `{vin()}`.** | 2 | 4 | 6 | 7 | 4 | 20.9 |
|
||||||
|
| 96 | 0.1.0 | question | **Refuse two reads of one sibling, one category or one struct category by different spellings, `{place.postal-code} {place.locality}`, showing the spelling for one pick and the one for two.** | 4 | 4 | 7 | 7 | 5.6, 4 | 20.9 |
|
||||||
| 5 | 0.1.0 | decision | **Run the nine-seat comprehension panel after the draw restructure, and file what it names.** | 1 | 3 | 3 | 9 | 3 | 20.4 |
|
| 5 | 0.1.0 | decision | **Run the nine-seat comprehension panel after the draw restructure, and file what it names.** | 1 | 3 | 3 | 9 | 3 | 20.4 |
|
||||||
| 91 | 0.1.0 | | **Accept a path through a name as a `calc` operand, so `{/misc.coordinate as c}{c}: {calc(c.lat * 60)}` computes from the coordinate `{c}` prints.** | 3 | 4 | 6 | 7 | 4.1, 5.3 | 19.9 |
|
| 91 | 0.1.0 | | **Accept a path through a name as a `calc` operand, so `{/misc.coordinate as c}{c}: {calc(c.lat * 60)}` computes from the coordinate `{c}` prints.** | 3 | 4 | 6 | 7 | 4.1, 5.3 | 19.9 |
|
||||||
| 89 | 0.1.0 | | **Fail `New` without `WithSeed`, naming the option, and seed the CLI from the system.** | 3 | 3 | 6 | 7 | 10.1 | 16.0 |
|
| 89 | 0.1.0 | | **Fail `New` without `WithSeed`, naming the option, and seed the CLI from the system.** | 3 | 3 | 6 | 7 | 10.1 | 16.0 |
|
||||||
@@ -233,6 +234,10 @@ Nothing has shipped, so "no longer paths", "where it used to fail" and "where it
|
|||||||
|
|
||||||
`isin`: Luhn over letters expanded to digits. `aba`: 3-7-1 weights. `vin`: position 9 over the whole; a sample taking the WMI, since the check sits mid-string.
|
`isin`: Luhn over letters expanded to digits. `aba`: 3-7-1 weights. `vin`: position 9 over the whole; a sample taking the WMI, since the check sits mid-string.
|
||||||
|
|
||||||
|
### 96. Refuse two reads of one sibling, one category or one struct category by different spellings, `{place.postal-code} {place.locality}`, showing the spelling for one pick and the one for two.
|
||||||
|
|
||||||
|
Asked of the maintainer by the product-owner pass of item 4's chunk. Since every `{…}` draws afresh, `{net} x {qty} = {calc(net * qty, 2)}`, `{place.postal-code} {place.locality}` and struct tags `sv_SE.address.street` beside `sv_SE.address.postal-code` load and may render a pair that disagrees, and nothing tells the author to bind a name (goal 4.1). Goal 5.6 asks such a template to fail, showing `{place as p}{p.postal-code} {p.locality}` for one place and two names for two. Item 4 planned the draw checks as names, paths through names, cycles and the repeat cap, so the fence would revise the decision "Every `{…}` draws afresh, and only a name keeps a pick". `{w} {w}`, one spelling twice, stays two draws. Open: whether reads across categories, `{/city.name}, {/country.name}`, count.
|
||||||
|
|
||||||
### 5. Run the nine-seat comprehension panel after the draw restructure, and file what it names.
|
### 5. Run the nine-seat comprehension panel after the draw restructure, and file what it names.
|
||||||
|
|
||||||
Needs item 90. The decision "comprehension floor: every dimension and the overall at 7.0 or above; the draw restructure and item 90 ship with no scoring run, and item 5 scores them" suspends every scoring run and panel until then.
|
Needs item 90. The decision "comprehension floor: every dimension and the overall at 7.0 or above; the draw restructure and item 90 ship with no scoring run, and item 5 scores them" suspends every scoring run and panel until then.
|
||||||
|
|||||||
Reference in New Issue
Block a user