Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33
@@ -959,7 +959,7 @@ scope.go the scope a render reads names and rows through
|
||||
path.go the dotted-path walks with their selectors, and proving a path resolves
|
||||
render.go Fake, the recursive renderer (choices, format strings, expansions) and the reads its tokens make
|
||||
record.go records: Record, the JSON/CSV/SQL serializers, and their entry points
|
||||
struct.go structs: FakeStruct, fake tags, the one pick a struct's path tags into one category read, and a field's Go type as its column's datatype
|
||||
struct.go structs: FakeStruct, fake tags as one record's columns, and a field's Go type as its column's datatype
|
||||
inline.go inline templates: Template, NewTemplate, FakeTemplate, IsTemplate, and their compile and link
|
||||
format.go the {token} grammar: scanning, tokens, operands, validation, compiling a format
|
||||
pins.go the pin set: the table rows one path or one named pick fixes
|
||||
|
||||
@@ -206,3 +206,11 @@ func linkNodeRefs(scope nodeScope, root map[string]node) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 != nameRead || len(toks[0].names) != 1 || !isRef(toks[0].names[0]) {
|
||||
return "", false
|
||||
}
|
||||
return toks[0].names[0], true
|
||||
}
|
||||
|
||||
@@ -417,3 +417,9 @@ func TestANameReadAloneIsTheRecordColumnItReads(t *testing.T) {
|
||||
t.Errorf("NewRecordTemplate = %v, want a name-read column accepted inline", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAFieldBindingReadOutsideWhatItBindsLoads(t *testing.T) {
|
||||
if _, err := engine(1).NewTemplate(`{"format":"{x.y as n}{n}{n}{x}","x":{"format":"{z}","y":["a","b"],"z":"{n}"}}`); err != nil {
|
||||
t.Errorf("NewTemplate = %v, want a read of n from x.z, outside x.y, accepted", err)
|
||||
}
|
||||
}
|
||||
|
||||
+25
-11
@@ -1,7 +1,6 @@
|
||||
package fejkdata
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -19,7 +18,7 @@ func checkNameReads(path string, t *template) error {
|
||||
}
|
||||
}
|
||||
return namedReads(t, func(o *op, a *arm) error {
|
||||
if a.named.bindsField() && rendersInside(a.named.head, 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)
|
||||
}
|
||||
for _, leaf := range a.leaves {
|
||||
@@ -31,17 +30,32 @@ func checkNameReads(path string, t *template) error {
|
||||
})
|
||||
}
|
||||
|
||||
// rendersInside reports whether t sits in what n contains.
|
||||
func rendersInside(n node, t *template) bool {
|
||||
return eachNode(n, "", func(_ string, m node) error {
|
||||
if m == t {
|
||||
return errFound
|
||||
// rendersInside reports whether t sits in what one of nodes contains.
|
||||
func rendersInside(nodes []node, t *template) bool {
|
||||
seen := map[node]bool{}
|
||||
var inside func(n node) bool
|
||||
inside = func(n node) bool {
|
||||
if n == node(t) {
|
||||
return true
|
||||
}
|
||||
return nil
|
||||
}) != nil
|
||||
if seen[n] {
|
||||
return false
|
||||
}
|
||||
seen[n] = true
|
||||
for _, c := range contained(n) {
|
||||
if inside(c.node) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
for _, n := range nodes {
|
||||
if inside(n) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var errFound = errors.New("found")
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -251,11 +251,3 @@ func refTokens(toks []formatToken) []string {
|
||||
}
|
||||
return refs
|
||||
}
|
||||
|
||||
// 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 != nameRead || len(toks[0].names) != 1 || !isRef(toks[0].names[0]) {
|
||||
return "", false
|
||||
}
|
||||
return toks[0].names[0], true
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
| 95 | 0.3.0 | principle | **Hold a struct's columns and the field each fills in one slice.** | 1 | 2 | 1 | 5 | 3.2 | 9.5 |
|
||||
| 19 | 0.3.0 | | **Fill the 398 Swedish localities weighted 200 from SCB småorter.** | 2 | 4 | 4 | 6 | 14.1 | 9.2 |
|
||||
| 56 | 0.3.0 | defect | **Build on a manual run of `test.yml`.** | 1 | 1 | 1 | 4 | 1 | 8.8 |
|
||||
| 83 | 0.3.0 | principle | **Read a name binding's head and tail from its binder's link, and delete `linkBindings`.** | 4 | 3 | 1 | 6 | 3.2 | 8.1 |
|
||||
| 83 | 0.3.0 | principle | **Read a name binding's head and tail from its binder's link, and have each link pass return what it compiles, so `linkBindings` goes and no pass reads what an earlier one wrote in place.** | 4 | 4 | 1 | 6 | 3.2 | 7.8 |
|
||||
| 63 | 0.4.0 | | **Pair a street with its exact postnummer.** | 3 | 6 | 5 | 8 | 4.1 | 19.1 |
|
||||
| 62 | 0.4.0 | | **Add `{btc()}` and `{eth()}`.** | 2 | 4 | 4 | 6 | 4 | 15.2 |
|
||||
| 59 | 0.4.0 | | **Promise in `Generator`'s godoc that its renders run one at a time.** | 1 | 1 | 5 | 5 | 12 | 13.2 |
|
||||
@@ -446,9 +446,9 @@ Its doc says render failure, and it wraps what `NewTemplate` and `NewRecordTempl
|
||||
|
||||
`workflow_dispatch` leaves `github.event.before` empty, so the diff compares `HEAD` with itself and skips `docker build`.
|
||||
|
||||
### 83. Read a name binding's head and tail from its binder's link, and delete `linkBindings`.
|
||||
### 83. Read a name binding's head and tail from its binder's link, and have each link pass return what it compiles, so `linkBindings` goes and no pass reads what an earlier one wrote in place.
|
||||
|
||||
`linkBindings` writes `nameBinding.head` and `tail` in `linkRefs`'s first pass and `linkName` reads them in its second, so swapping the passes hands `linkName` a nil head. Technical principle: one owner per value.
|
||||
`linkBindings` writes `nameBinding.head` and `tail` in `linkRefs`'s first pass and `linkName` reads them in its second, so swapping the passes hands `linkName` a nil head. `linkPasses` does the same: `linkColumnRead` reads the `arm.named` that `linkNames` wrote, and `checkNameReads` the arms both filled. Technical principle: one owner per value.
|
||||
|
||||
### 63. Pair a street with its exact postnummer.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user