Answer prose round 1: name the parent after .., mark the step down as the one exception to the shared draw, and plainer errors

This commit is contained in:
2026-10-05 00:18:46 +02:00
committed by Lillem4n
parent 406232e8f9
commit ec1dcbf9d3
8 changed files with 26 additions and 22 deletions
+1 -1
View File
@@ -80,7 +80,7 @@ In [docs/decisions.md](docs/decisions.md):
- `parent` names the link column and the table alike - `parent` names the link column and the table alike
- After a row, a path names a column or a linked table - After a row, a path names a column or a linked table
- A selected row is fixed, not drawn, and an unnamed read beside it is refused - A selected row is fixed, not drawn, and an unnamed read beside it is refused
- `..` steps up to the parent row, and a step down after it draws once per draw group or pick - `..` steps up to the parent row, and a step down after it draws once per render and draw group, or per pick
- A table selects by one key column, a code, never a free-form name - A table selects by one key column, a code, never a free-form name
- A parent row with no child row is a load error - A parent row with no child row is a load error
- The choice-of-rows fence guards a data file's root, and requires string fields - The choice-of-rows fence guards a data file's root, and requires string fields
+7 -6
View File
@@ -552,7 +552,8 @@ A path descends from a row to a linked table by name, at any depth, and `--list`
advertises each direct step. Within one render and [draw group](#draw-group), linked advertises each direct step. Within one render and [draw group](#draw-group), linked
tables agree: the first table a reference path reads pins its ancestors, and a tables agree: the first table a reference path reads pins its ancestors, and a
descendant read after it is drawn inside them, so `{/city.name}` and descendant read after it is drawn inside them, so `{/city.name}` and
`{/country.name}` are a city and its country whichever is read first. A selected row `{/country.name}` are a city and its country whichever is read first, except a step down
after a `..` ([Step up](#step-up)). A selected row
pins the render the same way, so every reference path into one family of linked pins the render the same way, so every reference path into one family of linked
tables in one render and group selects the same rows: one that selects none beside tables in one render and group selects the same rows: one that selects none beside
one that does is refused naming the spelling that does, `{/country[SE].city.name}` one that does is refused naming the spelling that does, `{/country[SE].city.name}`
@@ -574,8 +575,8 @@ or the reference draws it.
### Step up ### Step up
`..` after a row steps up to the row its link column names, and the parent table's name `..` after a row steps up to the row its link column names. Name the parent table right
follows it, as a folder's does in a filesystem path: after the `..`:
```sh ```sh
fejkdata -d ./mydata 'city[Oslo]..country.name' # Norway fejkdata -d ./mydata 'city[Oslo]..country.name' # Norway
@@ -585,11 +586,11 @@ fejkdata -d ./mydata '{/city as c}{c}, {c..country.name}' # Oslo, Norway
``` ```
It steps up from a selected row, a drawn row or a [name](#names) bound to a row, one level It steps up from a selected row, a drawn row or a [name](#names) bound to a row, one level
per `..`: `{l..municipality..region.name}`. `.country` after a row still reads the link per `..`: `geo.SE.locality..municipality..region.name`. `.country` after a row still reads the link
column's cell, `NO`. column's cell, `NO`.
After a `..`, `.` steps down again, drawing afresh inside the row stepped up to, After a `..`, `.` steps down again and draws afresh inside the row stepped up to, so
independent of the row the path stepped up from, which it may draw again. Paths stepping down from one `..` read one draw, as one `city[Göteborg]..country.city` may draw Göteborg again. Paths stepping down from one `..` read one draw, as one
reference path does: `{/city..country.city.name}` and `{/city..country.city.population}` reference path does: `{/city..country.city.name}` and `{/city..country.city.population}`
describe one city, within a render and [draw group](#draw-group), or within a name's pick. describe one city, within a render and [draw group](#draw-group), or within a name's pick.
+1 -1
View File
@@ -25,7 +25,7 @@ const usage = `Usage: fejkdata [flags] <path|template>
<path> a category, or a dotted path into one (person, person.last); <path> a category, or a dotted path into one (person, person.last);
a table's row by key or name: 'misc.territory[SE]', 'misc.territory[Sweden].tld'; a table's row by key or name: 'misc.territory[SE]', 'misc.territory[Sweden].tld';
.. steps up to the row a row links to: 'geo.SE.locality..municipality.name' .. steps up from a row to the row it links to: 'geo.SE.locality..municipality.name'
<template> a format string or JSON value to render inline, e.g. <template> a format string or JSON value to render inline, e.g.
'name: {/sv_SE.person.last}' or '{"format":"{x}","x":["bosse","lina"]}' 'name: {/sv_SE.person.last}' or '{"format":"{x}","x":["bosse","lina"]}'
+8 -5
View File
@@ -511,15 +511,18 @@ case but the last, where two expected a render and all three accepted the refusa
Goal 5.2 overrides its shared draw beneath a selection and its refusal of an unnamed read; `todo.md` item 4 replaces them. Goal 5.2 overrides its shared draw beneath a selection and its refusal of an unnamed read; `todo.md` item 4 replaces them.
## `..` steps up to the parent row, and a step down after it draws once per draw group or pick ## `..` steps up to the parent row, and a step down after it draws once per render and draw group, or per pick
2026-10-05, larv-review on `todo.md` item 2. Goal 5.4 has `..` go up one level, and goal 5.2 2026-10-05, larv-review in PR #158. Serves goal 5.4, which has `..` go up one level. A step
has each `{…}` make a new pick, which `todo.md` item 4 brings to every path. down draws afresh because goal 5.2 has each `{…}` make a new pick, which `todo.md` item 4
brings to every path.
- `..` names the parent table next, `city..country`, as a folder path names the folder it - `..` names the parent table next, `city..country`, as a folder path names the folder it
enters. `.country` after a row stays the link column's cell. enters. `.country` after a row stays the link column's cell.
- A step down after a `..` draws afresh inside the row stepped up to (`drawSteps`), and the - A step down after a `..` draws afresh inside the row stepped up to (`drawSteps`), and the
draw fences leave it out (`tableReadOf`), since it pins none of the render's rows. Paths draw fences leave it out (`tableReadOf`), since it pins none of the render's rows: the one
exception to [A render shares one reference draw per category, per
group](#a-render-shares-one-reference-draw-per-category-per-group). Paths
stepping down from one `..` share that draw through their memo (`drawMemo.stepDownPins`), stepping down from one `..` share that draw through their memo (`drawMemo.stepDownPins`),
as reference paths share a draw today, so two reads of one step down describe one row. as reference paths share a draw today, so two reads of one step down describe one row.
- A route through another row is another path to the same data, not a second spelling: - A route through another row is another path to the same data, not a second spelling:
@@ -926,4 +929,4 @@ Valid while names are read only inside the category binding them.
## comprehension floor: every dimension and the overall at 7.0 or above; items 71, 3, 4 and 90 ship with no scoring run, and item 5 scores them ## comprehension floor: every dimension and the overall at 7.0 or above; items 71, 3, 4 and 90 ship with no scoring run, and item 5 scores them
2026-10-04, Lilleman auf Larv. Serves goal 3. The nine-seat panels in `todo.md`'s git history, 2026-09-22 to 2026-09-30, held the score between 5.2 and 6.1, with Locality lowest at every seat, and the hardest units are the draw fences item 4 deletes. Items 4 and 90 delete or move what the earlier items touch, so a scoring run before item 90 merges would score code about to go. Items 2, 71, 3, 4 and 90 are the round `AGENTS.md`'s comprehension rule lets merge below the floor, a restructure the maintainer approved. No scoring run or panel runs until item 5, which runs the nine-seat panel and larv-review's scoring run. The scoring run writes this entry's first scores. Valid until item 5 runs. 2026-10-04, Lilleman auf Larv. Serves goal 3. The nine-seat panels in `todo.md`'s git history, 2026-09-22 to 2026-09-30, held the score between 5.2 and 6.1, with Locality lowest at every seat, and the hardest units are the draw fences item 4 deletes. Items 4 and 90 delete or move what the earlier items touch, so a scoring run before item 90 merges would score code about to go. Items 71, 3, 4 and 90 are the round `AGENTS.md`'s comprehension rule lets merge below the floor, a restructure the maintainer approved. No scoring run or panel runs until item 5, which runs the nine-seat panel and larv-review's scoring run. The scoring run writes this entry's first scores. Valid until item 5 runs.
+3 -3
View File
@@ -5,7 +5,7 @@ import "strings"
// drawMemo is what a hold, a draw group or a named pick has drawn: the variant each level was // drawMemo is what a hold, a draw group or a named pick has drawn: the variant each level was
// drawn as, so every path under it reads one variant; the value each read produced, by its path, // drawn as, so every path under it reads one variant; the value each read produced, by its path,
// so the same read written twice reads one value; the frames a read opens for the name // so the same read written twice reads one value; the frames a read opens for the name
// scopes around where it lands (enter); and the rows drawn below each step up stepped down from. // scopes around where it lands (enter); and the rows drawn by each step down after a "..", by level.
type drawMemo struct { type drawMemo struct {
variant map[string]node variant map[string]node
value map[string]readValue value map[string]readValue
@@ -13,8 +13,8 @@ type drawMemo struct {
steppedDown map[string]*pinSet steppedDown map[string]*pinSet
} }
// stepDownPins is the pins a path steps down into after stepping up to t, at the level at: kept // stepDownPins is the pins a path steps down into after stepping up to t: kept in m where there
// in m where there is one, so every path stepping down there reads one draw. // is one, so every path stepping down there reads one draw.
func (m *drawMemo) stepDownPins(pins *pinSet, t *table, levels []string, at int) *pinSet { func (m *drawMemo) stepDownPins(pins *pinSet, t *table, levels []string, at int) *pinSet {
if m == nil { if m == nil {
return pins.above(t) return pins.above(t)
+2 -2
View File
@@ -157,7 +157,7 @@ func stepInto(n node, seg string) (node, error) {
if c, ok := n.(*tableColumn); ok { if c, ok := n.(*tableColumn); ok {
return nil, fmt.Errorf(`".." steps up from a table's row, and %q is a column; put the ".." right after the row`, c.t.header[c.i]) return nil, fmt.Errorf(`".." steps up from a table's row, and %q is a column; put the ".." right after the row`, c.t.header[c.i])
} }
return nil, fmt.Errorf(`".." steps up from a table's row to the row it links to, and what it follows is not a table; a path names every other level from the data root down`) return nil, fmt.Errorf(`".." steps up only from a table's row; name any other level by its path from the data root`)
} }
switch n := n.(type) { switch n := n.(type) {
case *folder: case *folder:
@@ -238,7 +238,7 @@ func (t *table) stepUp(rest []string) error {
case len(rest) == 0 || rest[0] != t.parentT.segment: case len(rest) == 0 || rest[0] != t.parentT.segment:
return fmt.Errorf(`".." steps up from %s to its parent table, so name that next: %s`, t.segment, t.climbTo(rest)) return fmt.Errorf(`".." steps up from %s to its parent table, so name that next: %s`, t.segment, t.climbTo(rest))
case hasSelector(rest): case hasSelector(rest):
return fmt.Errorf(`a path selects its rows before a "..", since a row selected after it could lie outside the row it steps up to; select from the table instead, its path from the data root: %s`, joinSegments(append([]string{t.parentT.path}, rest[1:]...))) return fmt.Errorf(`no selector after "..": a row selected there could lie outside the row stepped up to; select from the table's path from the data root: %s`, joinSegments(append([]string{t.parentT.path}, rest[1:]...)))
} }
return nil return nil
} }
+1 -1
View File
@@ -230,7 +230,7 @@ func (r readRoute) spelled(ref string) string {
type tableRead struct { type tableRead struct {
headTable *table headTable *table
pins pinSet pins pinSet
leafPins *pinSet // the rows the leaf renders in: pins, short of the rows a step down after a step up draws afresh leafPins *pinSet // the rows the leaf renders in: pins, without the rows below the row a ".." stepped up to
drawn map[*table]bool drawn map[*table]bool
sels []tableSel sels []tableSel
landsRow bool landsRow bool
+3 -3
View File
@@ -338,7 +338,7 @@ func TestTableStepUpRefusals(t *testing.T) {
"locality[L4]...municipality": "empty segment", "locality[L4]...municipality": "empty segment",
"locality[L4].municipality.name": "..municipality.name", "locality[L4].municipality.name": "..municipality.name",
"locality[L4].name..municipality": "column", "locality[L4].name..municipality": "column",
"x..y": "not a table", "x..y": "only from a table's row",
} { } {
f := newGenerator(t, writeFiles(t, with(geo(), map[string]string{"x.json": `{"format":"{y}","y":"a"}`}))) f := newGenerator(t, writeFiles(t, with(geo(), map[string]string{"x.json": `{"format":"{y}","y":"a"}`})))
if _, err := f.Fake(path); err == nil || !strings.Contains(err.Error(), want) { if _, err := f.Fake(path); err == nil || !strings.Contains(err.Error(), want) {
@@ -352,8 +352,8 @@ func TestTableStepUpRefusals(t *testing.T) {
t.Errorf("{/%s}: New = %v, want an error mentioning %s", path, err, want) t.Errorf("{/%s}: New = %v, want an error mentioning %s", path, err, want)
} }
} }
if _, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, map[string]string{"s.json": `{"format":"{a..b}","a":{"format":"{b}","b":"x"}}`}))); err == nil || !strings.Contains(err.Error(), "not a table") { if _, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, map[string]string{"s.json": `{"format":"{a..b}","a":{"format":"{b}","b":"x"}}`}))); err == nil || !strings.Contains(err.Error(), "only from a table's row") {
t.Errorf("{a..b} on a field: New = %v, want an error mentioning not a table", err) t.Errorf("{a..b} on a field: New = %v, want an error mentioning only from a table's row", err)
} }
} }