Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33
@@ -896,7 +896,7 @@ checksum.go the check characters a derivation appends, and the IBAN they sit
|
|||||||
transform.go the builtins that rewrite an operand's value, and the ASCII folding
|
transform.go the builtins that rewrite an operand's value, and the ASCII folding
|
||||||
calc.go the {calc()} arithmetic evaluator: parser, eval, validation
|
calc.go the {calc()} arithmetic evaluator: parser, eval, validation
|
||||||
datatype.go column datatypes: DataType, where datatype and null may sit, a column's datatype
|
datatype.go column datatypes: DataType, where datatype and null may sit, a column's datatype
|
||||||
value.go the value proof: what a typed column or calc operand holds, checked at load
|
valueproof.go the value proof: what a typed column or calc operand holds, checked at load
|
||||||
data.go the load path: fs.FS folders/files -> merged namespace tree, and the bind pipeline every entry point runs
|
data.go the load path: fs.FS folders/files -> merged namespace tree, and the bind pipeline every entry point runs
|
||||||
cmd/fejkdata/ the fejkdata CLI
|
cmd/fejkdata/ the fejkdata CLI
|
||||||
data/ shipped data (JSON, and a TSV per table), embedded at build: locale folders, geo, misc
|
data/ shipped data (JSON, and a TSV per table), embedded at build: locale folders, geo, misc
|
||||||
|
|||||||
+1
-1
@@ -77,7 +77,7 @@ func checkColumns(s nodeScope) error {
|
|||||||
items, _ := columnItems(column)
|
items, _ := columnItems(column)
|
||||||
for _, it := range items {
|
for _, it := range items {
|
||||||
if r := it.link.readsColumn; r != nil {
|
if r := it.link.readsColumn; r != nil {
|
||||||
if err := check(r.a.key[1:], r.a.tail[0], r.column); err != nil {
|
if err := check(r.a.head[1:], r.a.tail[0], r.column); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -90,7 +90,7 @@ func checkOwnFamily(t *template) error {
|
|||||||
seen[n] = true
|
seen[n] = true
|
||||||
for _, e := range renderEdges(n) {
|
for _, e := range renderEdges(n) {
|
||||||
if e.readsRef() {
|
if e.readsRef() {
|
||||||
if head, isTable := n.(*template).head(e.read.key).(*table); isTable && head.familyRoot() == own.familyRoot() {
|
if head, isTable := n.(*template).head(e.read.head).(*table); isTable && head.familyRoot() == own.familyRoot() {
|
||||||
return e, head, true
|
return e, head, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,7 @@ func readsTable(n node, a arm) bool {
|
|||||||
if !isTemplate {
|
if !isTemplate {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
_, isTable := t.head(a.key).(*table)
|
_, isTable := t.head(a.head).(*table)
|
||||||
return isTable
|
return isTable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -246,25 +246,25 @@ func checkArm(name string, fields map[string]node, wholeToken bool) error {
|
|||||||
if err := checkSegments(a); err != nil {
|
if err := checkSegments(a); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
field, ok := fields[a.key]
|
field, ok := fields[a.head]
|
||||||
if !ok {
|
if !ok {
|
||||||
if a.key == "" {
|
if a.head == "" {
|
||||||
return fmt.Errorf("a name is never empty, so this token can name no field")
|
return fmt.Errorf("a name is never empty, so this token can name no field")
|
||||||
}
|
}
|
||||||
if isOption(a.key) {
|
if isOption(a.head) {
|
||||||
return fmt.Errorf("%q is an option and can never be a field", a.key)
|
return fmt.Errorf("%q is an option and can never be a field", a.head)
|
||||||
}
|
}
|
||||||
if len(fields) == 0 {
|
if len(fields) == 0 {
|
||||||
hint := ""
|
hint := ""
|
||||||
if wholeToken && hintableRef(name) {
|
if wholeToken && hintableRef(name) {
|
||||||
hint = fmt.Sprintf(" — write {/%s} to reference the data", name)
|
hint = fmt.Sprintf(" — write {/%s} to reference the data", name)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("no field %q; a token names a sibling field, and this template has none%s", a.key, hint)
|
return fmt.Errorf("no field %q; a token names a sibling field, and this template has none%s", a.head, hint)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("no field %q", a.key)
|
return fmt.Errorf("no field %q", a.head)
|
||||||
}
|
}
|
||||||
if err := checkPath(field, a.tail, a.key); err != nil {
|
if err := checkPath(field, a.tail, a.head); err != nil {
|
||||||
return fmt.Errorf("field %q: %w", a.key, err)
|
return fmt.Errorf("field %q: %w", a.head, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -302,45 +302,45 @@ func checkNoRepeatedArm(body string, names []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// arm is one alternative of a {a|b} token or one operand, split into the key
|
// arm is one alternative of a {a|b} token or one operand, split into the head
|
||||||
// `template.head` resolves and the tail of a dotted path into it. A non-empty tail
|
// `template.head` resolves and the tail of a dotted path into it. A non-empty tail
|
||||||
// is what makes the arm a held draw: its head is drawn once per expansion (see
|
// is what makes the arm a held draw: its head is drawn once per expansion (see
|
||||||
// compileOps).
|
// compileOps).
|
||||||
type arm struct {
|
type arm struct {
|
||||||
spelling string // as written, for messages
|
spelling string // as written, for messages
|
||||||
key string
|
head string
|
||||||
tail []string
|
tail []string
|
||||||
levels []string // the key at each level the tail passes through, the arm's key first
|
levels []string // the path at each level the tail passes through, the head first
|
||||||
path string // key and tail, the one path every way of writing this read shares
|
path string // head and tail, the one path every way of writing this read shares
|
||||||
}
|
}
|
||||||
|
|
||||||
// splitArm splits one name into key and tail. refs maps a reference to what
|
// splitArm splits one name into head and tail. refs maps a reference to what
|
||||||
// linkRefs resolved it to; before linking, a reference is whole.
|
// linkRefs resolved it to; before linking, a reference is whole.
|
||||||
func splitArm(name string, refs map[string]refBinding) arm {
|
func splitArm(name string, refs map[string]refBinding) arm {
|
||||||
if isRef(name) {
|
if isRef(name) {
|
||||||
b, linked := refs[name]
|
b, linked := refs[name]
|
||||||
if !linked || len(b.tail) == 0 {
|
if !linked || len(b.tail) == 0 {
|
||||||
key := name
|
head := name
|
||||||
if linked {
|
if linked {
|
||||||
key = b.key
|
head = b.key
|
||||||
}
|
}
|
||||||
return arm{spelling: name, key: key, path: key}
|
return arm{spelling: name, head: head, path: head}
|
||||||
}
|
}
|
||||||
return pathArm(name, b.key, b.tail)
|
return pathArm(name, b.key, b.tail)
|
||||||
}
|
}
|
||||||
segs, err := splitPath(name)
|
segs, err := splitPath(name)
|
||||||
if err != nil || len(segs) == 1 {
|
if err != nil || len(segs) == 1 {
|
||||||
return arm{spelling: name, key: name, path: name}
|
return arm{spelling: name, head: name, path: name}
|
||||||
}
|
}
|
||||||
return pathArm(name, segs[0], segs[1:])
|
return pathArm(name, segs[0], segs[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func pathArm(name, key string, segs []string) arm {
|
func pathArm(name, head string, segs []string) arm {
|
||||||
levels := []string{key}
|
levels := []string{head}
|
||||||
for i := 0; i < len(segs)-1; i++ {
|
for i := 0; i < len(segs)-1; i++ {
|
||||||
levels = append(levels, key+"."+strings.Join(segs[:i+1], "."))
|
levels = append(levels, head+"."+strings.Join(segs[:i+1], "."))
|
||||||
}
|
}
|
||||||
return arm{spelling: name, key: key, tail: segs, levels: levels, path: key + "." + strings.Join(segs, ".")}
|
return arm{spelling: name, head: head, tail: segs, levels: levels, path: head + "." + strings.Join(segs, ".")}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkSegments rejects an unfinished path: "{a.}" and "{a..b}" each have a
|
// checkSegments rejects an unfinished path: "{a.}" and "{a..b}" each have a
|
||||||
@@ -350,7 +350,7 @@ func checkSegments(a arm) error {
|
|||||||
if len(a.tail) == 0 {
|
if len(a.tail) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if a.key == "" {
|
if a.head == "" {
|
||||||
return fmt.Errorf("path has an empty segment")
|
return fmt.Errorf("path has an empty segment")
|
||||||
}
|
}
|
||||||
for _, seg := range a.tail {
|
for _, seg := range a.tail {
|
||||||
@@ -379,13 +379,13 @@ type op struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// formatOps is a compiled format: its ops, the size of its literal text, and the
|
// formatOps is a compiled format: its ops, the size of its literal text, and the
|
||||||
// names it holds. pathKeys maps each key a path starts from to the first such path;
|
// names it holds. pathHeads maps each head a path starts from to the first such path;
|
||||||
// held is every such key plus the fields an operand reads; holder maps each held
|
// held is every such head plus the fields an operand reads; holder maps each held
|
||||||
// name to the first reader holding it, for error messages.
|
// name to the first reader holding it, for error messages.
|
||||||
type formatOps struct {
|
type formatOps struct {
|
||||||
ops []op
|
ops []op
|
||||||
grow int
|
grow int
|
||||||
pathKeys map[string]string
|
pathHeads map[string]string
|
||||||
held map[string]bool
|
held map[string]bool
|
||||||
holder map[string]string
|
holder map[string]string
|
||||||
heldLocal bool // some held name is not a reference path, so expand makes a hold
|
heldLocal bool // some held name is not a reference path, so expand makes a hold
|
||||||
@@ -396,19 +396,19 @@ func (c *formatOps) holdName(a arm, label string) {
|
|||||||
c.held = map[string]bool{}
|
c.held = map[string]bool{}
|
||||||
c.holder = map[string]string{}
|
c.holder = map[string]string{}
|
||||||
}
|
}
|
||||||
c.held[a.key] = true
|
c.held[a.head] = true
|
||||||
if !isRef(a.key) || len(a.tail) == 0 {
|
if !isRef(a.head) || len(a.tail) == 0 {
|
||||||
c.heldLocal = true
|
c.heldLocal = true
|
||||||
}
|
}
|
||||||
if _, named := c.holder[a.key]; !named {
|
if _, named := c.holder[a.head]; !named {
|
||||||
c.holder[a.key] = label
|
c.holder[a.head] = label
|
||||||
}
|
}
|
||||||
if len(a.tail) > 0 {
|
if len(a.tail) > 0 {
|
||||||
if c.pathKeys == nil {
|
if c.pathHeads == nil {
|
||||||
c.pathKeys = map[string]string{}
|
c.pathHeads = map[string]string{}
|
||||||
}
|
}
|
||||||
if _, named := c.pathKeys[a.key]; !named {
|
if _, named := c.pathHeads[a.head]; !named {
|
||||||
c.pathKeys[a.key] = a.spelling
|
c.pathHeads[a.head] = a.spelling
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ type renderEdge struct {
|
|||||||
operand string
|
operand string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e renderEdge) readsRef() bool { return isRef(e.read.key) }
|
func (e renderEdge) readsRef() bool { return isRef(e.read.head) }
|
||||||
|
|
||||||
// reached names an edge as the author spelled it.
|
// reached names an edge as the author spelled it.
|
||||||
func (e renderEdge) reached() string {
|
func (e renderEdge) reached() string {
|
||||||
@@ -133,7 +133,7 @@ func renderEdges(n node) []renderEdge {
|
|||||||
case *template:
|
case *template:
|
||||||
var es []renderEdge
|
var es []renderEdge
|
||||||
add := func(a arm, operand string) {
|
add := func(a arm, operand string) {
|
||||||
c := n.head(a.key)
|
c := n.head(a.head)
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,12 @@ type holdSet struct {
|
|||||||
type renderScope struct {
|
type renderScope struct {
|
||||||
set *holdSet
|
set *holdSet
|
||||||
group string
|
group string
|
||||||
row tablePin
|
row renderedRow
|
||||||
|
}
|
||||||
|
|
||||||
|
type renderedRow struct {
|
||||||
|
t *table
|
||||||
|
index int
|
||||||
}
|
}
|
||||||
|
|
||||||
// in is the scope t renders in: its draw 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.
|
||||||
@@ -57,9 +62,9 @@ func (sc renderScope) in(t *template) renderScope {
|
|||||||
func (sc renderScope) at(n node, pins *pinSet) renderScope {
|
func (sc renderScope) at(n node, pins *pinSet) renderScope {
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *tableRow:
|
case *tableRow:
|
||||||
sc.row = tablePin{n.t, pins.mustRow(n.t)}
|
sc.row = renderedRow{n.t, pins.mustRow(n.t)}
|
||||||
case *tableColumn:
|
case *tableColumn:
|
||||||
sc.row = tablePin{n.t, pins.mustRow(n.t)}
|
sc.row = renderedRow{n.t, pins.mustRow(n.t)}
|
||||||
}
|
}
|
||||||
return sc
|
return sc
|
||||||
}
|
}
|
||||||
@@ -69,7 +74,7 @@ func (sc renderScope) rowOf(t *table) int {
|
|||||||
if sc.row.t != t {
|
if sc.row.t != t {
|
||||||
panic(internalError("a column of %s renders in a scope holding no row of it", t.segment))
|
panic(internalError("a column of %s renders in a scope holding no row of it", t.segment))
|
||||||
}
|
}
|
||||||
return sc.row.row
|
return sc.row.index
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc renderScope) groupHold() *groupHold {
|
func (sc renderScope) groupHold() *groupHold {
|
||||||
|
|||||||
+1
-1
@@ -761,7 +761,7 @@ func TestRepeatedBareTokenOfAHeldNameIsRejected(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestReadFieldPanicsOnAPathItCannotHold(t *testing.T) {
|
func TestReadFieldPanicsOnAPathItCannotHold(t *testing.T) {
|
||||||
a := arm{spelling: "w.x", key: "w", tail: []string{"x"}, path: "w.x"}
|
a := arm{spelling: "w.x", head: "w", tail: []string{"x"}, path: "w.x"}
|
||||||
for name, format := range map[string]string{
|
for name, format := range map[string]string{
|
||||||
"unheld arm with a path": `{"format":"{w}","w":{"format":"{x}","x":"1"}}`,
|
"unheld arm with a path": `{"format":"{w}","w":{"format":"{x}","x":"1"}}`,
|
||||||
"held arm with no hold": `{"format":"{w.x}","w":{"format":"{x}","x":"1"}}`,
|
"held arm with no hold": `{"format":"{w.x}","w":{"format":"{x}","x":"1"}}`,
|
||||||
|
|||||||
+19
-19
@@ -15,9 +15,9 @@ func heldCheck(path string, n node) error {
|
|||||||
if !ok || len(t.compiled.held) == 0 {
|
if !ok || len(t.compiled.held) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
readers := pathKeyReaders(t.compiled.ops, t.compiled.pathKeys)
|
readers := pathHeadReaders(t.compiled.ops, t.compiled.pathHeads)
|
||||||
for _, name := range heldNames(t) {
|
for _, name := range heldNames(t) {
|
||||||
if _, isPath := t.compiled.pathKeys[name]; isPath && isRef(name) {
|
if _, isPath := t.compiled.pathHeads[name]; isPath && isRef(name) {
|
||||||
continue // held for the render: drawFence compares every read of it, by path, across the render and its groups
|
continue // held for the render: drawFence compares every read of it, by path, across the render and its groups
|
||||||
}
|
}
|
||||||
if err := checkNameHeld(t, name, readers); err != nil {
|
if err := checkNameHeld(t, name, readers); err != nil {
|
||||||
@@ -36,8 +36,8 @@ func heldNames(t *template) []string {
|
|||||||
names = append(names, name)
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
sort.Slice(names, func(i, j int) bool {
|
sort.Slice(names, func(i, j int) bool {
|
||||||
_, pi := t.compiled.pathKeys[names[i]]
|
_, pi := t.compiled.pathHeads[names[i]]
|
||||||
_, pj := t.compiled.pathKeys[names[j]]
|
_, pj := t.compiled.pathHeads[names[j]]
|
||||||
if pi != pj {
|
if pi != pj {
|
||||||
return !pi
|
return !pi
|
||||||
}
|
}
|
||||||
@@ -51,12 +51,12 @@ func heldNames(t *template) []string {
|
|||||||
// produces.
|
// produces.
|
||||||
func heldNodes(t *template, name string, readers []reader) map[node]bool {
|
func heldNodes(t *template, name string, readers []reader) map[node]bool {
|
||||||
held := map[node]bool{}
|
held := map[node]bool{}
|
||||||
if _, isPath := t.compiled.pathKeys[name]; !isPath {
|
if _, isPath := t.compiled.pathHeads[name]; !isPath {
|
||||||
operandDraw(t.head(name), held)
|
operandDraw(t.head(name), held)
|
||||||
return held
|
return held
|
||||||
}
|
}
|
||||||
for _, r := range readers {
|
for _, r := range readers {
|
||||||
if r.a.key == name {
|
if r.a.head == name {
|
||||||
coverPath(t.head(name), r.a.tail, held)
|
coverPath(t.head(name), r.a.tail, held)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,10 +71,10 @@ func checkNameHeld(t *template, name string, readers []reader) error {
|
|||||||
if len(held) == 0 {
|
if len(held) == 0 {
|
||||||
return nil // a fixed head holds nothing to reach
|
return nil // a fixed head holds nothing to reach
|
||||||
}
|
}
|
||||||
reader, isPath := t.compiled.pathKeys[name]
|
reader, isPath := t.compiled.pathHeads[name]
|
||||||
seen := map[node]bool{}
|
seen := map[node]bool{}
|
||||||
for _, e := range renderEdges(t) {
|
for _, e := range renderEdges(t) {
|
||||||
if e.read.key == name || !renders(e.to, held, seen) {
|
if e.read.head == name || !renders(e.to, held, seen) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if isPath {
|
if isPath {
|
||||||
@@ -89,7 +89,7 @@ func checkNameHeld(t *template, name string, readers []reader) error {
|
|||||||
func operandReader(t *template, name string) string {
|
func operandReader(t *template, name string) string {
|
||||||
for _, o := range t.compiled.ops {
|
for _, o := range t.compiled.ops {
|
||||||
for _, a := range o.operands {
|
for _, a := range o.operands {
|
||||||
if a.key == name {
|
if a.head == name {
|
||||||
return o.fn
|
return o.fn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,8 +175,8 @@ func renders(n node, want, seen map[node]bool) bool {
|
|||||||
// disagree. Reads are compared in sorted order, so which pair is reported does not
|
// disagree. Reads are compared in sorted order, so which pair is reported does not
|
||||||
// depend on where the tokens sit.
|
// depend on where the tokens sit.
|
||||||
// docs/decisions.md#a-bare-reference-draws-each-time-a-reference-path-is-held
|
// docs/decisions.md#a-bare-reference-draws-each-time-a-reference-path-is-held
|
||||||
func checkNoOverlap(ops []op, pathKeys map[string]string) error {
|
func checkNoOverlap(ops []op, pathHeads map[string]string) error {
|
||||||
names := pathKeyReaders(ops, pathKeys)
|
names := pathHeadReaders(ops, pathHeads)
|
||||||
// Stable over one format-order scan, so two readers of one name (a token and a
|
// Stable over one format-order scan, so two readers of one name (a token and a
|
||||||
// calc operand both naming "p") are reported as the format writes them.
|
// calc operand both naming "p") are reported as the format writes them.
|
||||||
sort.SliceStable(names, func(i, j int) bool { return names[i].a.path < names[j].a.path })
|
sort.SliceStable(names, func(i, j int) bool { return names[i].a.path < names[j].a.path })
|
||||||
@@ -190,25 +190,25 @@ func checkNoOverlap(ops []op, pathKeys map[string]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// reader is one way a format reaches a path key, and how to name it.
|
// reader is one way a format reaches a path head, and how to name it.
|
||||||
type reader struct {
|
type reader struct {
|
||||||
a arm
|
a arm
|
||||||
label string
|
label string
|
||||||
}
|
}
|
||||||
|
|
||||||
// pathKeyReaders lists every way a format reaches a sibling path key, in the order the
|
// pathHeadReaders lists every way a format reaches a sibling path head, in the order the
|
||||||
// format writes them. An operand renders its field, so it names a level exactly
|
// format writes them. An operand renders its field, so it names a level exactly
|
||||||
// as a token does; one scan finds both, which is what puts them in one order.
|
// as a token does; one scan finds both, which is what puts them in one order.
|
||||||
func pathKeyReaders(ops []op, pathKeys map[string]string) []reader {
|
func pathHeadReaders(ops []op, pathHeads map[string]string) []reader {
|
||||||
var names []reader
|
var names []reader
|
||||||
for _, o := range ops {
|
for _, o := range ops {
|
||||||
for _, a := range o.operands {
|
for _, a := range o.operands {
|
||||||
if _, isPathKey := pathKeys[a.key]; isPathKey && !isRef(a.key) {
|
if _, isPathHead := pathHeads[a.head]; isPathHead && !isRef(a.head) {
|
||||||
names = append(names, reader{a, fmt.Sprintf("%s operand %q", o.fn, a.spelling)})
|
names = append(names, reader{a, fmt.Sprintf("%s operand %q", o.fn, a.spelling)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, a := range o.arms {
|
for _, a := range o.arms {
|
||||||
if _, isPathKey := pathKeys[a.key]; isPathKey && !isRef(a.key) {
|
if _, isPathHead := pathHeads[a.head]; isPathHead && !isRef(a.head) {
|
||||||
names = append(names, reader{a, "token {" + a.spelling + "}"})
|
names = append(names, reader{a, "token {" + a.spelling + "}"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,11 +224,11 @@ func checkNoRepeatedRead(c formatOps) error {
|
|||||||
count := map[string]int{}
|
count := map[string]int{}
|
||||||
for _, o := range c.ops {
|
for _, o := range c.ops {
|
||||||
for _, a := range o.arms {
|
for _, a := range o.arms {
|
||||||
if len(a.tail) > 0 || !c.held[a.key] {
|
if len(a.tail) > 0 || !c.held[a.head] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if count[a.key]++; count[a.key] > 1 {
|
if count[a.head]++; count[a.head] > 1 {
|
||||||
return fmt.Errorf("token {%s} is repeated, and %s holds %q to one draw per expansion; write {%s} once", a.spelling, c.holder[a.key], a.key, a.spelling)
|
return fmt.Errorf("token {%s} is repeated, and %s holds %q to one draw per expansion; write {%s} once", a.spelling, c.holder[a.head], a.head, a.spelling)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,12 +81,12 @@ type templateLink struct {
|
|||||||
|
|
||||||
func (*template) isNode() {}
|
func (*template) isNode() {}
|
||||||
|
|
||||||
// head is the node an arm's key names: a sibling field, or a reference's category.
|
// head is the node an arm's head names: a sibling field, or a reference's category.
|
||||||
func (t *template) head(key string) node {
|
func (t *template) head(name string) node {
|
||||||
if isRef(key) {
|
if isRef(name) {
|
||||||
return t.link.refHeads[key]
|
return t.link.refHeads[name]
|
||||||
}
|
}
|
||||||
return t.fields[key]
|
return t.fields[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
// compile converts parsed JSON — a category or an inline template — into a node tree,
|
// compile converts parsed JSON — a category or an inline template — into a node tree,
|
||||||
@@ -229,7 +229,7 @@ func (t *template) fixedText() (string, bool) {
|
|||||||
// compiled reads.
|
// compiled reads.
|
||||||
func compileFormat(toks []formatToken, refs map[string]refBinding) (formatOps, error) {
|
func compileFormat(toks []formatToken, refs map[string]refBinding) (formatOps, error) {
|
||||||
c := compileOps(toks, refs)
|
c := compileOps(toks, refs)
|
||||||
if err := checkNoOverlap(c.ops, c.pathKeys); err != nil {
|
if err := checkNoOverlap(c.ops, c.pathHeads); err != nil {
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
return c, checkNoRepeatedRead(c)
|
return c, checkNoRepeatedRead(c)
|
||||||
|
|||||||
+1
-1
@@ -143,7 +143,7 @@ func (s *readSurvey) walk(n node, at surveyAt) {
|
|||||||
func (s *readSurvey) edge(from node, e renderEdge, at surveyAt) {
|
func (s *readSurvey) edge(from node, e renderEdge, at surveyAt) {
|
||||||
if e.readsRef() {
|
if e.readsRef() {
|
||||||
a := e.read
|
a := e.read
|
||||||
tr := tableReadOf(from.(*template).head(a.key), a, e.to)
|
tr := tableReadOf(from.(*template).head(a.head), a, e.to)
|
||||||
if k := (readKey{at.group, a.path, s.pinsID(&at.pins), s.pinsID(&at.wholePins)}); !s.read[k] {
|
if k := (readKey{at.group, a.path, s.pinsID(&at.pins), s.pinsID(&at.wholePins)}); !s.read[k] {
|
||||||
s.read[k] = true
|
s.read[k] = true
|
||||||
s.reads = append(s.reads, pathRead{at, a, tr})
|
s.reads = append(s.reads, pathRead{at, a, tr})
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@ func wantGathered(t *testing.T, tables map[string]*table, label string, gathered
|
|||||||
}
|
}
|
||||||
seen := map[traceKey]bool{}
|
seen := map[traceKey]bool{}
|
||||||
trace := func(group, table string, row int, a arm) {
|
trace := func(group, table string, row int, a arm) {
|
||||||
if !isRef(a.key) {
|
if !isRef(a.head) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r := pathRead{at: surveyAt{group: group}, a: a}
|
r := pathRead{at: surveyAt{group: group}, a: a}
|
||||||
|
|||||||
+1
-1
@@ -146,7 +146,7 @@ func columnReadOf(t *template, link templateLink) *columnRead {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
a := splitArm(name, link.refs)
|
a := splitArm(name, link.refs)
|
||||||
target, isTemplate := link.refHeads[a.key].(*template)
|
target, isTemplate := link.refHeads[a.head].(*template)
|
||||||
if !isTemplate || !target.isRecord || len(a.tail) != 1 {
|
if !isTemplate || !target.isRecord || len(a.tail) != 1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func render(s *session, n node, sc renderScope) string {
|
|||||||
case *nullItem:
|
case *nullItem:
|
||||||
return ""
|
return ""
|
||||||
case *table:
|
case *table:
|
||||||
sc.row = tablePin{n, n.drawRow(s)}
|
sc.row = renderedRow{n, n.drawRow(s)}
|
||||||
return expand(s, n.formatTemplate, sc)
|
return expand(s, n.formatTemplate, sc)
|
||||||
case *tableRow:
|
case *tableRow:
|
||||||
return expand(s, n.t.formatTemplate, sc)
|
return expand(s, n.t.formatTemplate, sc)
|
||||||
@@ -159,17 +159,17 @@ func expand(s *session, t *template, sc renderScope) string {
|
|||||||
// read one row, either read twice gives one value, and a shown operand is the operand
|
// read one row, either read twice gives one value, and a shown operand is the operand
|
||||||
// computed. Every other name is drawn afresh, so {word} {word} still draws twice.
|
// computed. Every other name is drawn afresh, so {word} {word} still draws twice.
|
||||||
func readField(s *session, t *template, held *hold, sc renderScope, a arm) readValue {
|
func readField(s *session, t *template, held *hold, sc renderScope, a arm) readValue {
|
||||||
if isRef(a.key) && len(a.tail) > 0 {
|
if isRef(a.head) && len(a.tail) > 0 {
|
||||||
return readReference(s, t, sc, a)
|
return readReference(s, t, sc, a)
|
||||||
}
|
}
|
||||||
if sc.set.trace != nil {
|
if sc.set.trace != nil {
|
||||||
traceRead(sc.set.trace, t, sc, a)
|
traceRead(sc.set.trace, t, sc, a)
|
||||||
}
|
}
|
||||||
if !t.compiled.held[a.key] {
|
if !t.compiled.held[a.head] {
|
||||||
if len(a.tail) > 0 {
|
if len(a.tail) > 0 {
|
||||||
panic(internalError("%q reads a path into %q, which the expansion does not hold", a.spelling, a.key))
|
panic(internalError("%q reads a path into %q, which the expansion does not hold", a.spelling, a.head))
|
||||||
}
|
}
|
||||||
return readValue{text: render(s, t.head(a.key), sc)}
|
return readValue{text: render(s, t.head(a.head), sc)}
|
||||||
}
|
}
|
||||||
if held == nil {
|
if held == nil {
|
||||||
panic(internalError("%q reads a name the expansion holds, with no hold to keep it in", a.spelling))
|
panic(internalError("%q reads a name the expansion holds, with no hold to keep it in", a.spelling))
|
||||||
@@ -191,7 +191,7 @@ func readHeld(s *session, t *template, held *hold, pins *pinSet, sc renderScope,
|
|||||||
if r, done := held.value[a.path]; done {
|
if r, done := held.value[a.path]; done {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
leaf := drawPath(t.head(a.key), a.tail, a.key, &pathDraw{s: s, held: held, pins: pins, a: &a})
|
leaf := drawPath(t.head(a.head), a.tail, a.head, &pathDraw{s: s, held: held, pins: pins, a: &a})
|
||||||
if pins != nil {
|
if pins != nil {
|
||||||
sc = sc.at(leaf, pins)
|
sc = sc.at(leaf, pins)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -330,8 +330,8 @@ func (t *table) compileRowFormat(format string) error {
|
|||||||
}
|
}
|
||||||
for _, name := range tok.names {
|
for _, name := range tok.names {
|
||||||
a := splitArm(name, nil)
|
a := splitArm(name, nil)
|
||||||
if _, ok := t.col[a.key]; !ok && !isRef(a.key) && a.key != "" {
|
if _, ok := t.col[a.head]; !ok && !isRef(a.head) && a.head != "" {
|
||||||
return fmt.Errorf("format names no column %q of %s; the columns are %v", a.key, t.file, t.header)
|
return fmt.Errorf("format names no column %q of %s; the columns are %v", a.head, t.file, t.header)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1063,7 +1063,7 @@ func TestProbeReportsTheTablesADrawPins(t *testing.T) {
|
|||||||
{"region[12].name", "region=region[12]"},
|
{"region[12].name", "region=region[12]"},
|
||||||
} {
|
} {
|
||||||
a := splitArm(c.path, nil)
|
a := splitArm(c.path, nil)
|
||||||
head := f.root.children[a.key].(*table)
|
head := f.root.children[a.head].(*table)
|
||||||
tr := tableReadOf(head, a, nil)
|
tr := tableReadOf(head, a, nil)
|
||||||
var sels []string
|
var sels []string
|
||||||
for _, s := range tr.sels {
|
for _, s := range tr.sels {
|
||||||
@@ -1075,7 +1075,7 @@ func TestProbeReportsTheTablesADrawPins(t *testing.T) {
|
|||||||
probed, drawn := tr.pins, tr.drawn
|
probed, drawn := tr.pins, tr.drawn
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
var pins pinSet
|
var pins pinSet
|
||||||
drawPath(head, a.tail, a.key, &pathDraw{s: f.rand, pins: &pins})
|
drawPath(head, a.tail, a.head, &pathDraw{s: f.rand, pins: &pins})
|
||||||
want := map[*table]bool{}
|
want := map[*table]bool{}
|
||||||
pins.each(func(tbl *table, _ int) {
|
pins.each(func(tbl *table, _ int) {
|
||||||
if _, selected := probed.pinned(tbl); !selected {
|
if _, selected := probed.pinned(tbl); !selected {
|
||||||
|
|||||||
@@ -28,9 +28,6 @@ ranking `readSurvey.walk` hardest and all seven the unit they would least modify
|
|||||||
|
|
||||||
This round, in order:
|
This round, in order:
|
||||||
|
|
||||||
- Rename `arm.key` to `head`, as `template.head` reads it, leaving `key` the table's key
|
|
||||||
column; give `renderScope.row` a type that is not `tablePin`, since it pins nothing;
|
|
||||||
and name `value.go` by the `valueProof` it holds.
|
|
||||||
- Delete the prose the seats found empty: "kept whole on purpose"
|
- Delete the prose the seats found empty: "kept whole on purpose"
|
||||||
(`format.go`, `calc.go`), the second copy of the concrete-session rationale
|
(`format.go`, `calc.go`), the second copy of the concrete-session rationale
|
||||||
(`table.drawRow`, `pick`), the doc comments restating `isSelector`, `selectorOf`,
|
(`table.drawRow`, `pick`), the doc comments restating `isSelector`, `selectorOf`,
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ func (p *valueProof) proveTemplate(t *template) proven {
|
|||||||
case o.kind == nameRead:
|
case o.kind == nameRead:
|
||||||
var leaves []node
|
var leaves []node
|
||||||
for _, a := range o.arms {
|
for _, a := range o.arms {
|
||||||
leaves = append(leaves, pathLeaves(t.head(a.key), a.tail)...)
|
leaves = append(leaves, pathLeaves(t.head(a.head), a.tail)...)
|
||||||
}
|
}
|
||||||
return p.proveUnion(leaves)
|
return p.proveUnion(leaves)
|
||||||
case name == "calc":
|
case name == "calc":
|
||||||
Reference in New Issue
Block a user