Answer prose round 1: delete two false comments, plainer SplitPath, evalCalc and decision wording, a shorter comprehension floor title, and item 110 owes a changelog entry

This commit is contained in:
2026-10-05 12:31:58 +02:00
committed by Lillem4n
parent 199cb577c5
commit 177dd4b50b
8 changed files with 21 additions and 24 deletions
+1 -1
View File
@@ -111,5 +111,5 @@ In [docs/decisions.md](docs/decisions.md):
- `List` advertises direct descents only - `List` advertises direct descents only
- A path draws through its compiled steps - A path draws through its compiled steps
- A name lives in the category binding it, or in the repeat binding it, and is drawn on its first read - A name lives in the category binding it, or in the repeat binding it, and is drawn on its first read
- comprehension floor: every dimension and the overall at 7.0 or above; the change making every `{…}` draw afresh, the moves of the draw state and the grammar, and items 99 to 106 ship with no scoring run, and item 5 scores them - comprehension floor: every dimension and the overall at 7.0 or above; item 5 scores the restructure, which ships unscored: every `{…}` drawing afresh, the moves of the draw state and the grammar, and items 99 to 106
- The template engine stays the root package until item 107; the draw state and the grammar sit in `internal/`, and items 99 to 103 move what else stands alone there - The template engine stays the root package until item 107; the draw state and the grammar sit in `internal/`, and items 99 to 103 move what else stands alone there
+3 -4
View File
@@ -11,8 +11,8 @@ import (
"github.com/larvit/fejkdata/internal/grammar" "github.com/larvit/fejkdata/internal/grammar"
) )
// evalCalc evaluates an expression over the operand values expand read, so the // evalCalc evaluates an expression over the operand values expand already read, so it
// evaluator touches neither the rng nor the node tree. // draws nothing and reads no template node.
func evalCalc(n grammar.CalcNode, operands []string) float64 { func evalCalc(n grammar.CalcNode, operands []string) float64 {
switch n := n.(type) { switch n := n.(type) {
case grammar.CalcNum: case grammar.CalcNum:
@@ -209,8 +209,7 @@ func calcPrep(args []string) callFn {
} }
} }
// parsedCalc parses an expression checkCalc accepted. A nil AST would dereference later, with // parsedCalc parses an expression checkCalc accepted.
// no message.
func parsedCalc(expr string) grammar.Calc { func parsedCalc(expr string) grammar.Calc {
c, err := grammar.ParseCalc(expr) c, err := grammar.ParseCalc(expr)
if err != nil { if err != nil {
+2 -2
View File
@@ -105,11 +105,11 @@ func TestCalcReproducible(t *testing.T) {
func TestParsedCallReadsOnlyAnOperandBuiltin(t *testing.T) { func TestParsedCallReadsOnlyAnOperandBuiltin(t *testing.T) {
for _, format := range []string{"{luhn()}", "{calc()}", "{calc(1 +)}", "{calc(()}"} { for _, format := range []string{"{luhn()}", "{calc()}", "{calc(1 +)}", "{calc(()}"} {
if toks, err := grammar.ParseFormat(format); err != nil || len(toks) != 1 || tokenReads(toks[0]) != nil { if toks, err := grammar.ParseFormat(format); err != nil || len(toks) != 1 || tokenReads(toks[0]) != nil {
t.Errorf("tokenReads(%q) = %+v, %v, want one call reading no operand", format, toks, err) t.Errorf("ParseFormat(%q) = %+v, %v, want one call reading no operand", format, toks, err)
} }
} }
if toks, err := grammar.ParseFormat("{calc(net * qty)}"); err != nil || len(toks) != 1 || len(tokenReads(toks[0])) != 2 { if toks, err := grammar.ParseFormat("{calc(net * qty)}"); err != nil || len(toks) != 1 || len(tokenReads(toks[0])) != 2 {
t.Errorf("tokenReads({calc(net * qty)}) = %+v, %v, want both operands", toks, err) t.Errorf("ParseFormat({calc(net * qty)}) = %+v, %v, want both operands", toks, err)
} }
} }
+6 -6
View File
@@ -825,9 +825,9 @@ category's frames, since the field sits inside it.
Valid while names are read only inside the category binding them. Valid while names are read only inside the category binding them.
## comprehension floor: every dimension and the overall at 7.0 or above; the change making every `{…}` draw afresh, the moves of the draw state and the grammar, and items 99 to 106 ship with no scoring run, and item 5 scores them ## comprehension floor: every dimension and the overall at 7.0 or above; item 5 scores the restructure, which ships unscored: every `{…}` drawing afresh, the moves of the draw state and the grammar, and items 99 to 106
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. The hardest units were the draw fences, which the change making every `{…}` draw afresh deleted. The change making every `{…}` draw afresh, the moves of the draw state into `internal/drawstate` and the grammar into `internal/grammar`, and `todo.md` items 99 to 106 delete or move what the earlier chunks of the below-floor round touched, so a scoring run before item 106 merges would score code about to go; the calc change accepting a name as an operand shipped unscored on 2026-10-05 for that reason. The change making every `{…}` draw afresh, the moves of the draw state and the grammar, and items 99 to 106 are the restructure the maintainer approved, which `AGENTS.md`'s comprehension rule lets merge below the floor. 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 `todo.md` 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. The hardest units were the draw fences, which the change making every `{…}` draw afresh deleted. The change making every `{…}` draw afresh, the moves of the draw state into `internal/drawstate` and the grammar into `internal/grammar`, and `todo.md` items 99 to 106 delete or move what the earlier chunks of the below-floor round touched, so a scoring run before item 106 merges would score code about to go; the calc change accepting a name as an operand shipped unscored on 2026-10-05 for that reason. These are the restructure the maintainer approved, which `AGENTS.md`'s comprehension rule lets merge below the floor. 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 `todo.md` item 5 runs.
## The template engine stays the root package until item 107; the draw state and the grammar sit in `internal/`, and items 99 to 103 move what else stands alone there ## The template engine stays the root package until item 107; the draw state and the grammar sit in `internal/`, and items 99 to 103 move what else stands alone there
@@ -836,9 +836,9 @@ engine's types, `template`, `table`, `arm`, `op` and `nameBinding`, bound in one
because several passes each write part of the same structs; the template language's because several passes each write part of the same structs; the template language's
recursion reads few of those types. Moving the engine before items 101 and 104 to 106 give recursion reads few of those types. Moving the engine before items 101 and 104 to 106 give
each value one writer would spread that knot across packages, behind forwarding calls from each value one writer would spread that knot across packages, behind forwarding calls from
every public method. The draw state reads no engine type, so it sits in `internal/drawstate`, every public method. The draw state and the grammar read no engine type, so they sit in
behind an import list a test checks, and so does the grammar, in `internal/grammar`. `internal/drawstate` and `internal/grammar`, behind an import list a test checks.
`DataType` reads none either. `proven`, what a proof knows of a value, is keyed by `DataType` reads none either. `proven`, what a proof knows of a value, is keyed by
`DataType`, so the two move together. The reading of data files, `proven`, the builtins and `DataType`, so the two move together. The reading of data files, `proven`, the builtins and
a table's rows stop reading any engine type once items 99 to 103 remove those reads, and a table's rows stop reading any engine type once items 99 to 103 remove those reads; items
each of those items moves its part and adds it to that list. Valid until `todo.md` item 107 moves the engine. 99, 100, 102 and 103 each move their part and add it to that list. Valid until `todo.md` item 107 moves the engine.
-2
View File
@@ -304,8 +304,6 @@ func pathArm(name, head, writtenHead string, segs []string) arm {
return arm{spelling: name, head: head, writtenHead: writtenHead, tail: segs, levels: levels, path: head + "." + strings.Join(segs, ".")} return arm{spelling: name, head: head, writtenHead: writtenHead, tail: segs, levels: levels, path: head + "." + strings.Join(segs, ".")}
} }
// checkSegments refuses an empty segment in a path, which a field really named "" would
// otherwise resolve, so a typo would read as a path that worked.
func checkSegments(a arm) error { func checkSegments(a arm) error {
if len(a.tail) == 0 { if len(a.tail) == 0 {
return nil return nil
+4 -5
View File
@@ -6,9 +6,9 @@ import (
"strings" "strings"
) )
// SplitPath splits a dotted path into segments, a selector — [key or name] after a // SplitPath splits a dotted path into segments. A selector, [key or name] after a table's
// table's name — becoming a segment of its own, brackets kept, and so does each "..". A // name, is a segment of its own with its brackets kept, and so is each "..". A dot inside a
// dot inside a selector is part of the key or name. // selector belongs to the key or name.
func SplitPath(path string) ([]string, error) { func SplitPath(path string) ([]string, error) {
segs := make([]string, 0, strings.Count(path, ".")+2*strings.Count(path, "[")+1) segs := make([]string, 0, strings.Count(path, ".")+2*strings.Count(path, "[")+1)
start, open := 0, -1 start, open := 0, -1
@@ -162,8 +162,7 @@ func JoinSegments(segs []string) string {
return b.String() return b.String()
} }
// CheckSegments refuses an unfinished path: "a." and "a...b" each have a segment naming // CheckSegments refuses a path with an empty segment, such as "a." or "a...b".
// nothing.
func CheckSegments(segs []string) error { func CheckSegments(segs []string) error {
if slices.Contains(segs, "") { if slices.Contains(segs, "") {
return fmt.Errorf("path has an empty segment") return fmt.Errorf("path has an empty segment")
+3 -2
View File
@@ -87,8 +87,9 @@ func (t *table) route(tail []string, descended bool) (tableRoute, error) {
return r, nil return r, nil
} }
// stepUp proves a ".." after a row of t can step up: t has a parent, rest names it, and no // stepUp proves a ".." after a row of t can step up: t has a parent, rest has no empty
// selector follows, since a row selected below the parent row could lie outside it. // segment and names it, and no selector follows, since a row selected below the parent row
// could lie outside it.
func (t *table) stepUp(rest []string) error { func (t *table) stepUp(rest []string) error {
if t.parentT == nil { if t.parentT == nil {
return fmt.Errorf(`%s has no parent table for ".." to step up to`, t.segment) return fmt.Errorf(`%s has no parent table for ".." to step up to`, t.segment)
+2 -2
View File
@@ -246,7 +246,7 @@ Asked of the maintainer by the product-owner pass of the chunk that made every `
### 5. Run the nine-seat comprehension panel and larv-review's scoring run once items 99 to 106 have restructured the code, and file what the panel names. ### 5. Run the nine-seat comprehension panel and larv-review's scoring run once items 99 to 106 have restructured the code, and file what the panel names.
Until a panel scores 7.0 or above, only items 99 to 106 and the exceptions in `AGENTS.md`'s comprehension rule merge. The decision "comprehension floor: every dimension and the overall at 7.0 or above; the change making every `{…}` draw afresh, the moves of the draw state and the grammar, and items 99 to 106 ship with no scoring run, and item 5 scores them" suspends every scoring run and panel until this item runs. Until a panel scores 7.0 or above, only items 99 to 106 and the exceptions in `AGENTS.md`'s comprehension rule merge. The decision "comprehension floor: every dimension and the overall at 7.0 or above; item 5 scores the restructure, which ships unscored: every `{…}` drawing afresh, the moves of the draw state and the grammar, and items 99 to 106" suspends every scoring run and panel until this item runs.
### 91. 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. ### 91. 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.
@@ -395,7 +395,7 @@ Require the path step to reach a sibling category.
### 110. Refuse an empty segment in a path `Fake`, `FakeRecord` and the CLI take, as a reference does: `sv_SE.person.` reports `path has an empty segment`. ### 110. Refuse an empty segment in a path `Fake`, `FakeRecord` and the CLI take, as a reference does: `sv_SE.person.` reports `path has an empty segment`.
Today `fejkdata 'sv_SE.person.'` reports `no field ""`, while `{/sv_SE.person.}` reports `path has an empty segment`. `grammar.CheckSegments` is the check a reference, a sibling path and a step up `..` run. Today `fejkdata 'sv_SE.person.'` reports `no field ""`, while `{/sv_SE.person.}` reports `path has an empty segment`. `grammar.CheckSegments` is the check a reference, a sibling path and a step up `..` run. The new refusal owes a `CHANGELOG.md` entry.
### 107. Move the template engine into `internal/`, leaving the root the public API and its options. ### 107. Move the template engine into `internal/`, leaving the root the public API and its options.