Give rows, field, walk, column, expr and of one meaning each: rowCount, readRows, gatherFields, addField, addTag, renderCount and the prove methods
This commit is contained in:
@@ -124,8 +124,8 @@ type categoryFiles struct {
|
|||||||
tsv map[string]bool
|
tsv map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// rows reads the rows file a category names beside it.
|
// readRows reads the rows file a category names beside it.
|
||||||
func (c *categoryFiles) rows(name string) (string, error) {
|
func (c *categoryFiles) readRows(name string) (string, error) {
|
||||||
if _, present := c.tsv[name]; !present {
|
if _, present := c.tsv[name]; !present {
|
||||||
return "", fmt.Errorf("rows names %s, which is not beside it in %s", name, c.src.name(c.dir))
|
return "", fmt.Errorf("rows names %s, which is not beside it in %s", name, c.src.name(c.dir))
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -166,7 +166,7 @@ func disagreement(a *template, da DataType, b *template, db DataType) error {
|
|||||||
switch {
|
switch {
|
||||||
case da != DataTypeString && db != DataTypeString:
|
case da != DataTypeString && db != DataTypeString:
|
||||||
return bothTyped(a, da, b, db)
|
return bothTyped(a, da, b, db)
|
||||||
case typed.datatype == DataTypeString && (&valueProof{}).columnItem(bare).not[want] != "":
|
case typed.datatype == DataTypeString && (&valueProof{}).proveColumnItem(bare).not[want] != "":
|
||||||
return fmt.Errorf(`item %q is not %s, the datatype item %q takes from the column it reads; to read that column as text, %s`, bare.format, dataTypeNouns[want], typed.format, asText(typed))
|
return fmt.Errorf(`item %q is not %s, the datatype item %q takes from the column it reads; to read that column as text, %s`, bare.format, dataTypeNouns[want], typed.format, asText(typed))
|
||||||
case bare.fromString: // an object may carry a weight, which this spelling would drop
|
case bare.fromString: // an object may carry a weight, which this spelling would drop
|
||||||
return fmt.Errorf(`item %q declares no datatype, and a column holds one; write it as {"format":%q,"datatype":%q}`, bare.format, bare.format, want)
|
return fmt.Errorf(`item %q declares no datatype, and a column holds one; write it as {"format":%q,"datatype":%q}`, bare.format, bare.format, want)
|
||||||
@@ -184,7 +184,7 @@ func bothTyped(a *template, da DataType, b *template, db DataType) error {
|
|||||||
switch {
|
switch {
|
||||||
case read.datatype != DataTypeString:
|
case read.datatype != DataTypeString:
|
||||||
return fmt.Errorf("its items hold %s and %s; a column holds one datatype", da, db)
|
return fmt.Errorf("its items hold %s and %s; a column holds one datatype", da, db)
|
||||||
case (&valueProof{}).columnItem(read).not[other] == "":
|
case (&valueProof{}).proveColumnItem(read).not[other] == "":
|
||||||
return fmt.Errorf("its items hold %s and %s; a column holds one datatype, so %s", da, db, typedAs(read, other))
|
return fmt.Errorf("its items hold %s and %s; a column holds one datatype, so %s", da, db, typedAs(read, other))
|
||||||
}
|
}
|
||||||
return fmt.Errorf("its items hold %s and %s; a column holds one datatype, so to read %q as text, %s", da, db, read.format, asText(read))
|
return fmt.Errorf("its items hold %s and %s; a column holds one datatype, so to read %q as text, %s", da, db, read.format, asText(read))
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ func contained(n node) []namedNode {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var out []namedNode
|
var out []namedNode
|
||||||
for r := 0; r < n.t.rows(); r++ {
|
for r := 0; r < n.t.rowCount(); r++ {
|
||||||
if cell := n.t.cellTemplate(r, n.i); cell != nil {
|
if cell := n.t.cellTemplate(r, n.i); cell != nil {
|
||||||
out = append(out, namedNode{node: cell})
|
out = append(out, namedNode{node: cell})
|
||||||
}
|
}
|
||||||
@@ -171,13 +171,13 @@ func pathLeaves(n node, tail []string) []node {
|
|||||||
|
|
||||||
type reachMemo map[node]int
|
type reachMemo map[node]int
|
||||||
|
|
||||||
func (m reachMemo) of(n node) int {
|
func (m reachMemo) renderCount(n node) int {
|
||||||
if r, done := m[n]; done {
|
if r, done := m[n]; done {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
r := 1
|
r := 1
|
||||||
for _, e := range renderEdges(n) {
|
for _, e := range renderEdges(n) {
|
||||||
if c := m.of(e.to); c > r {
|
if c := m.renderCount(e.to); c > r {
|
||||||
r = c
|
r = c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,8 +192,8 @@ func (m reachMemo) of(n node) int {
|
|||||||
// path, so nested repeats cannot build what one repeat may not.
|
// path, so nested repeats cannot build what one repeat may not.
|
||||||
// docs/decisions.md#the-repeat-cap-bounds-renders-not-bytes
|
// docs/decisions.md#the-repeat-cap-bounds-renders-not-bytes
|
||||||
func repeatCheck(path string, n node, mem reachMemo) error {
|
func repeatCheck(path string, n node, mem reachMemo) error {
|
||||||
if t, ok := n.(*template); ok && t.repeat > 1 && mem.of(n) > MaxRepeat {
|
if t, ok := n.(*template); ok && t.repeat > 1 && mem.renderCount(n) > MaxRepeat {
|
||||||
return fmt.Errorf("%s: repeat %d multiplies to %d renders along one path, above the maximum %d", path, t.repeat, mem.of(n), MaxRepeat)
|
return fmt.Errorf("%s: repeat %d multiplies to %d renders along one path, above the maximum %d", path, t.repeat, mem.renderCount(n), MaxRepeat)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ func (sc *structCompile) record(t reflect.Type, label string) (*structShape, err
|
|||||||
sc.visiting[t] = true
|
sc.visiting[t] = true
|
||||||
defer delete(sc.visiting, t)
|
defer delete(sc.visiting, t)
|
||||||
c := &structFields{structCompile: sc, t: t, label: label, tags: map[string]any{}, shape: &structShape{}}
|
c := &structFields{structCompile: sc, t: t, label: label, tags: map[string]any{}, shape: &structShape{}}
|
||||||
if err := c.walk(t, nil); err != nil {
|
if err := c.gatherFields(t, nil); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(c.tags) > 0 {
|
if len(c.tags) > 0 {
|
||||||
@@ -123,19 +123,19 @@ func (sc *structCompile) spend(label string) error {
|
|||||||
return fmt.Errorf(`%s: the struct fields reach more than %d structs; leave a struct field unfilled with fake:"-"`, label, maxStructs)
|
return fmt.Errorf(`%s: the struct fields reach more than %d structs; leave a struct field unfilled with fake:"-"`, label, maxStructs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// walk gathers the fields of struct type t, which sits at index within c.t.
|
// gatherFields gathers the fields of struct type t, which sits at index within c.t.
|
||||||
func (c *structFields) walk(t reflect.Type, index []int) error {
|
func (c *structFields) gatherFields(t reflect.Type, index []int) error {
|
||||||
for i := 0; i < t.NumField(); i++ {
|
for i := 0; i < t.NumField(); i++ {
|
||||||
sf := t.Field(i)
|
sf := t.Field(i)
|
||||||
sf.Index = append(index[:len(index):len(index)], i)
|
sf.Index = append(index[:len(index):len(index)], i)
|
||||||
if err := c.field(sf); err != nil {
|
if err := c.addField(sf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *structFields) field(sf reflect.StructField) error {
|
func (c *structFields) addField(sf reflect.StructField) error {
|
||||||
tag, tagged := sf.Tag.Lookup("fake")
|
tag, tagged := sf.Tag.Lookup("fake")
|
||||||
elem := structOf(sf.Type)
|
elem := structOf(sf.Type)
|
||||||
switch {
|
switch {
|
||||||
@@ -145,7 +145,7 @@ func (c *structFields) field(sf reflect.StructField) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case tagged:
|
case tagged:
|
||||||
return c.column(sf, tag)
|
return c.addTag(sf, tag)
|
||||||
case elem == nil || c.visiting[elem]:
|
case elem == nil || c.visiting[elem]:
|
||||||
return nil
|
return nil
|
||||||
case sf.Anonymous:
|
case sf.Anonymous:
|
||||||
@@ -167,8 +167,8 @@ func structOf(t reflect.Type) reflect.Type {
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
// column adds a tagged field's tag to the record, refusing one another field hides.
|
// addTag adds a tagged field's tag to the record, refusing one another field hides.
|
||||||
func (c *structFields) column(sf reflect.StructField, tag string) error {
|
func (c *structFields) addTag(sf reflect.StructField, tag string) error {
|
||||||
if visible, ok := c.t.FieldByName(sf.Name); !ok || !slices.Equal(visible.Index, sf.Index) {
|
if visible, ok := c.t.FieldByName(sf.Name); !ok || !slices.Equal(visible.Index, sf.Index) {
|
||||||
return fmt.Errorf("%s.%s: hidden by another field named %s, so its fake tag cannot fill it; rename one", c.label, fieldPath(c.t, sf.Index), sf.Name)
|
return fmt.Errorf("%s.%s: hidden by another field named %s, so its fake tag cannot fill it; rename one", c.label, fieldPath(c.t, sf.Index), sf.Name)
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,7 @@ func (c *structFields) embed(sf reflect.StructField, elem reflect.Type) error {
|
|||||||
c.visiting[elem] = true
|
c.visiting[elem] = true
|
||||||
defer delete(c.visiting, elem)
|
defer delete(c.visiting, elem)
|
||||||
tags, nested := len(c.tags), len(c.shape.nested)
|
tags, nested := len(c.tags), len(c.shape.nested)
|
||||||
if err := c.walk(elem, sf.Index); err != nil {
|
if err := c.gatherFields(elem, sf.Index); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if sf.Type.Kind() == reflect.Pointer && !sf.IsExported() && (len(c.tags) > tags || len(c.shape.nested) > nested) {
|
if sf.Type.Kind() == reflect.Pointer && !sf.IsExported() && (len(c.tags) > tags || len(c.shape.nested) > nested) {
|
||||||
@@ -327,7 +327,7 @@ func (p *valueProof) checkField(label string, ft reflect.Type, column node) erro
|
|||||||
elem := ft
|
elem := ft
|
||||||
if ft.Kind() == reflect.Pointer {
|
if ft.Kind() == reflect.Pointer {
|
||||||
elem = ft.Elem()
|
elem = ft.Elem()
|
||||||
} else if p.column(column).null {
|
} else if p.proveColumn(column).null {
|
||||||
return fmt.Errorf("%s: its tag can draw null, which %s cannot hold; make it *%s", label, ft, ft)
|
return fmt.Errorf("%s: its tag can draw null, which %s cannot hold; make it *%s", label, ft, ft)
|
||||||
}
|
}
|
||||||
kind := columnKinds[elem.Kind()]
|
kind := columnKinds[elem.Kind()]
|
||||||
@@ -335,7 +335,7 @@ func (p *valueProof) checkField(label string, ft reflect.Type, column node) erro
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, it := range items {
|
for _, it := range items {
|
||||||
v := p.columnItem(it)
|
v := p.proveColumnItem(it)
|
||||||
if reason := v.not[kind.datatype]; reason != "" {
|
if reason := v.not[kind.datatype]; reason != "" {
|
||||||
return fmt.Errorf("%s (%s): %s", label, ft, reason)
|
return fmt.Errorf("%s (%s): %s", label, ft, reason)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ type row struct{ t *table }
|
|||||||
|
|
||||||
func (*row) isNode() {}
|
func (*row) isNode() {}
|
||||||
|
|
||||||
func (t *table) rows() int { return len(t.cells) / len(t.columns) }
|
func (t *table) rowCount() int { return len(t.cells) / len(t.columns) }
|
||||||
|
|
||||||
func (t *table) cell(row, col int) string { return t.cells[row*len(t.columns)+col] }
|
func (t *table) cell(row, col int) string { return t.cells[row*len(t.columns)+col] }
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ func compileTable(m map[string]any, category string, files *categoryFiles) (*tab
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
data, err := files.rows(o.rows)
|
data, err := files.readRows(o.rows)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -184,7 +184,7 @@ func (t *table) parseRows(data string) error {
|
|||||||
}
|
}
|
||||||
rest = more
|
rest = more
|
||||||
}
|
}
|
||||||
if t.rows() < 2 {
|
if t.rowCount() < 2 {
|
||||||
return fmt.Errorf("has one row, which is a template; write it as one")
|
return fmt.Errorf("has one row, which is a template; write it as one")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -240,8 +240,8 @@ func (t *table) proveNamesInsideParent() error {
|
|||||||
if t.key >= 0 || t.name < 0 {
|
if t.key >= 0 || t.name < 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
inside := make(map[string]int, t.rows())
|
inside := make(map[string]int, t.rowCount())
|
||||||
for r := 0; r < t.rows(); r++ {
|
for r := 0; r < t.rowCount(); r++ {
|
||||||
k := t.cell(r, t.parent) + "\t" + t.cell(r, t.name)
|
k := t.cell(r, t.parent) + "\t" + t.cell(r, t.name)
|
||||||
if first, dup := inside[k]; dup {
|
if first, dup := inside[k]; dup {
|
||||||
return fmt.Errorf("%s line %d: name %q repeats line %d inside %s %q; a name selects one row inside its parent; drop one, or add a key column", t.file, r+2, t.cell(r, t.name), first+2, t.columns[t.parent], t.cell(r, t.parent))
|
return fmt.Errorf("%s line %d: name %q repeats line %d inside %s %q; a name selects one row inside its parent; drop one, or add a key column", t.file, r+2, t.cell(r, t.name), first+2, t.columns[t.parent], t.cell(r, t.parent))
|
||||||
@@ -256,8 +256,8 @@ func (t *table) indexKeys() error {
|
|||||||
if t.key < 0 {
|
if t.key < 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
t.byKey = make(map[string]int, t.rows())
|
t.byKey = make(map[string]int, t.rowCount())
|
||||||
for r := 0; r < t.rows(); r++ {
|
for r := 0; r < t.rowCount(); r++ {
|
||||||
k := t.cell(r, t.key)
|
k := t.cell(r, t.key)
|
||||||
if k == "" {
|
if k == "" {
|
||||||
return fmt.Errorf("%s line %d: the key is empty", t.file, r+2)
|
return fmt.Errorf("%s line %d: the key is empty", t.file, r+2)
|
||||||
@@ -267,7 +267,7 @@ func (t *table) indexKeys() error {
|
|||||||
}
|
}
|
||||||
t.byKey[k] = r
|
t.byKey[k] = r
|
||||||
}
|
}
|
||||||
for r := 0; r < t.rows() && t.name >= 0; r++ {
|
for r := 0; r < t.rowCount() && t.name >= 0; r++ {
|
||||||
if n := t.cell(r, t.name); t.byKey[n] != r {
|
if n := t.cell(r, t.name); t.byKey[n] != r {
|
||||||
if other, isKey := t.byKey[n]; isKey {
|
if other, isKey := t.byKey[n]; isKey {
|
||||||
return fmt.Errorf("%s line %d: name %q is the key of line %d, which a selector reads first, so the name could never select this row", t.file, r+2, n, other+2)
|
return fmt.Errorf("%s line %d: name %q is the key of line %d, which a selector reads first, so the name could never select this row", t.file, r+2, n, other+2)
|
||||||
@@ -282,7 +282,7 @@ func (t *table) sumWeights() error {
|
|||||||
if t.weight < 0 {
|
if t.weight < 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
t.cum = make([]float64, t.rows())
|
t.cum = make([]float64, t.rowCount())
|
||||||
total := 0.0
|
total := 0.0
|
||||||
for r := range t.cum {
|
for r := range t.cum {
|
||||||
w, err := strconv.ParseFloat(t.cell(r, t.weight), 64)
|
w, err := strconv.ParseFloat(t.cell(r, t.weight), 64)
|
||||||
@@ -419,15 +419,15 @@ func (t *table) linkParent(path string, siblings map[string]node) error {
|
|||||||
return fmt.Errorf("%q is named like a column of %q, its ancestor, so %s.%s could read either; rename one", t.category, q.category, q.category, t.category)
|
return fmt.Errorf("%q is named like a column of %q, its ancestor, so %s.%s could read either; rename one", t.category, q.category, q.category, t.category)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
linked := make(map[string]bool, p.rows())
|
linked := make(map[string]bool, p.rowCount())
|
||||||
for r := 0; r < t.rows(); r++ {
|
for r := 0; r < t.rowCount(); r++ {
|
||||||
k := t.cell(r, t.parent)
|
k := t.cell(r, t.parent)
|
||||||
if _, ok := p.byKey[k]; !ok {
|
if _, ok := p.byKey[k]; !ok {
|
||||||
return fmt.Errorf("%s line %d: %s %q is no key of %s", t.file, r+2, name, k, p.file)
|
return fmt.Errorf("%s line %d: %s %q is no key of %s", t.file, r+2, name, k, p.file)
|
||||||
}
|
}
|
||||||
linked[k] = true
|
linked[k] = true
|
||||||
}
|
}
|
||||||
for r := 0; r < p.rows(); r++ {
|
for r := 0; r < p.rowCount(); r++ {
|
||||||
if k := p.cell(r, p.key); !linked[k] {
|
if k := p.cell(r, p.key); !linked[k] {
|
||||||
return fmt.Errorf("%s links no row to %s %q; every %s row needs one, or drop line %d of %s", t.file, name, k, name, r+2, p.file)
|
return fmt.Errorf("%s links no row to %s %q; every %s row needs one, or drop line %d of %s", t.file, name, k, name, r+2, p.file)
|
||||||
}
|
}
|
||||||
@@ -443,15 +443,15 @@ func (t *table) linkParent(path string, siblings map[string]node) error {
|
|||||||
func (t *table) indexed() *tableIndex {
|
func (t *table) indexed() *tableIndex {
|
||||||
t.once.Do(func() {
|
t.once.Do(func() {
|
||||||
if t.name >= 0 {
|
if t.name >= 0 {
|
||||||
t.index.byName = make(map[string][]int, t.rows())
|
t.index.byName = make(map[string][]int, t.rowCount())
|
||||||
for r := 0; r < t.rows(); r++ {
|
for r := 0; r < t.rowCount(); r++ {
|
||||||
n := t.cell(r, t.name)
|
n := t.cell(r, t.name)
|
||||||
t.index.byName[n] = append(t.index.byName[n], r)
|
t.index.byName[n] = append(t.index.byName[n], r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t.parent >= 0 {
|
if t.parent >= 0 {
|
||||||
t.index.children = map[string][]int{}
|
t.index.children = map[string][]int{}
|
||||||
for r := 0; r < t.rows(); r++ {
|
for r := 0; r < t.rowCount(); r++ {
|
||||||
k := t.cell(r, t.parent)
|
k := t.cell(r, t.parent)
|
||||||
t.index.children[k] = append(t.index.children[k], r)
|
t.index.children[k] = append(t.index.children[k], r)
|
||||||
}
|
}
|
||||||
@@ -476,7 +476,7 @@ func (t *table) indexed() *tableIndex {
|
|||||||
// interface so that the walk that draws through it keeps its hold set off the heap.
|
// interface so that the walk that draws through it keeps its hold set off the heap.
|
||||||
func (t *table) draw(s *session) int {
|
func (t *table) draw(s *session) int {
|
||||||
if t.cum == nil {
|
if t.cum == nil {
|
||||||
return s.IntN(t.rows())
|
return s.IntN(t.rowCount())
|
||||||
}
|
}
|
||||||
return pickCum(s, t.cum)
|
return pickCum(s, t.cum)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -982,7 +982,7 @@ func TestEnteredRowsAgreeWithPinning(t *testing.T) {
|
|||||||
}
|
}
|
||||||
var none pinSet
|
var none pinSet
|
||||||
for _, entered := range tables {
|
for _, entered := range tables {
|
||||||
for row := 0; row < entered.rows(); row++ {
|
for row := 0; row < entered.rowCount(); row++ {
|
||||||
whole := drawAt{drawn: entered}
|
whole := drawAt{drawn: entered}
|
||||||
whole.whole.add(entered, row)
|
whole.whole.add(entered, row)
|
||||||
s := none.entered(entered, row)
|
s := none.entered(entered, row)
|
||||||
@@ -993,7 +993,7 @@ func TestEnteredRowsAgreeWithPinning(t *testing.T) {
|
|||||||
t.Errorf("a render draws %s beside %s, which pinning it there refuses", cand.selectorSpelling(r), entered.selectorSpelling(row))
|
t.Errorf("a render draws %s beside %s, which pinning it there refuses", cand.selectorSpelling(r), entered.selectorSpelling(row))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for cr := 0; cr < cand.rows(); cr++ {
|
for cr := 0; cr < cand.rowCount(); cr++ {
|
||||||
beside := s.clone()
|
beside := s.clone()
|
||||||
refused := beside.pinRow(cand, cr) != nil
|
refused := beside.pinRow(cand, cr) != nil
|
||||||
candAt := drawAt{pins: none.entered(cand, cr)}
|
candAt := drawAt{pins: none.entered(cand, cr)}
|
||||||
|
|||||||
@@ -19,11 +19,9 @@ Self-sufficiency 5.6), every seat capped by Locality or Self-sufficiency.
|
|||||||
|
|
||||||
This round, in order:
|
This round, in order:
|
||||||
|
|
||||||
- Give `rows` and `field` one meaning each, per the decision "One name, one meaning":
|
- Rename the shared names left that name two things, per the decision "One name, one
|
||||||
`categoryFiles.rows` returns a TSV file name where `table.rows` counts rows, and
|
meaning": judge each of `check`, `draw`, `drawn`, `head`, `key`, `name`, `named`,
|
||||||
`formatOps.field` compiles a `{field}` token where `structFields.field` reads a Go
|
`names`, `null`, `number`, `record` and `variant`.
|
||||||
struct field. Rename every other shared name that names two things — `walk`,
|
|
||||||
`column`, `expr` and `of` included.
|
|
||||||
- Run the nine-seat panel again and file what it names here.
|
- Run the nine-seat panel again and file what it names here.
|
||||||
|
|
||||||
### Data
|
### Data
|
||||||
|
|||||||
@@ -37,23 +37,23 @@ func (p *valueProof) checkDatatype(path string, n node) error {
|
|||||||
if d := readDatatype(t); d == t.datatype {
|
if d := readDatatype(t); d == t.datatype {
|
||||||
return fmt.Errorf(`%s: %s takes datatype %s from the column it reads; drop "datatype"`, path, t.format, d)
|
return fmt.Errorf(`%s: %s takes datatype %s from the column it reads; drop "datatype"`, path, t.format, d)
|
||||||
}
|
}
|
||||||
if reason := p.columnItem(t).not[t.datatype]; reason != "" {
|
if reason := p.proveColumnItem(t).not[t.datatype]; reason != "" {
|
||||||
return fmt.Errorf("%s: datatype %s: %s", path, t.datatype, reason)
|
return fmt.Errorf("%s: datatype %s: %s", path, t.datatype, reason)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// columnItem proves a column item: what it renders, or, when it is a column it reads, that column.
|
// proveColumnItem proves a column item: what it renders, or, when it is a column it reads, that column.
|
||||||
func (p *valueProof) columnItem(t *template) proven {
|
func (p *valueProof) proveColumnItem(t *template) proven {
|
||||||
if t.readsColumn == nil {
|
if t.readsColumn == nil {
|
||||||
return p.of(t)
|
return p.prove(t)
|
||||||
}
|
}
|
||||||
return p.column(t.readsColumn.column)
|
return p.proveColumn(t.readsColumn.column)
|
||||||
}
|
}
|
||||||
|
|
||||||
// column proves a column over what its items draw, a null item marking it null rather than
|
// proveColumn proves a column over what its items draw, a null item marking it null rather than
|
||||||
// rendering "".
|
// rendering "".
|
||||||
func (p *valueProof) column(n node) proven {
|
func (p *valueProof) proveColumn(n node) proven {
|
||||||
if v, done := p.columns[n]; done {
|
if v, done := p.columns[n]; done {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ func (p *valueProof) column(n node) proven {
|
|||||||
items, nullable := columnItems(n)
|
items, nullable := columnItems(n)
|
||||||
var v proven
|
var v proven
|
||||||
for i, it := range items {
|
for i, it := range items {
|
||||||
if w := p.columnItem(it); i == 0 {
|
if w := p.proveColumnItem(it); i == 0 {
|
||||||
v = w
|
v = w
|
||||||
} else {
|
} else {
|
||||||
v = v.or(w)
|
v = v.or(w)
|
||||||
@@ -74,7 +74,7 @@ func (p *valueProof) column(n node) proven {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *valueProof) of(n node) proven {
|
func (p *valueProof) prove(n node) proven {
|
||||||
if v, done := p.memo[n]; done {
|
if v, done := p.memo[n]; done {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
@@ -84,11 +84,11 @@ func (p *valueProof) of(n node) proven {
|
|||||||
var v proven
|
var v proven
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *choice:
|
case *choice:
|
||||||
v = p.unite(n.items)
|
v = p.proveUnion(n.items)
|
||||||
case *template:
|
case *template:
|
||||||
v = p.template(n)
|
v = p.proveTemplate(n)
|
||||||
case *column:
|
case *column:
|
||||||
v = p.cells(n)
|
v = p.proveCells(n)
|
||||||
case *row:
|
case *row:
|
||||||
v = unproven(fmt.Sprintf("%q renders a row of %s, which is composed text", n.t.format.format, n.t.category))
|
v = unproven(fmt.Sprintf("%q renders a row of %s, which is composed text", n.t.format.format, n.t.category))
|
||||||
default:
|
default:
|
||||||
@@ -98,13 +98,13 @@ func (p *valueProof) of(n node) proven {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// cells proves a table column over every cell it may render.
|
// proveCells proves a table column over every cell it may render.
|
||||||
func (p *valueProof) cells(c *column) proven {
|
func (p *valueProof) proveCells(c *column) proven {
|
||||||
var v proven
|
var v proven
|
||||||
for r := 0; r < c.t.rows(); r++ {
|
for r := 0; r < c.t.rowCount(); r++ {
|
||||||
var w proven
|
var w proven
|
||||||
if cell := c.t.cellTemplate(r, c.i); cell != nil {
|
if cell := c.t.cellTemplate(r, c.i); cell != nil {
|
||||||
w = p.of(cell)
|
w = p.prove(cell)
|
||||||
} else {
|
} else {
|
||||||
w = literalValue(c.t.cell(r, c.i))
|
w = literalValue(c.t.cell(r, c.i))
|
||||||
}
|
}
|
||||||
@@ -117,10 +117,10 @@ func (p *valueProof) cells(c *column) proven {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *valueProof) unite(nodes []node) proven {
|
func (p *valueProof) proveUnion(nodes []node) proven {
|
||||||
v := p.of(nodes[0])
|
v := p.prove(nodes[0])
|
||||||
for _, n := range nodes[1:] {
|
for _, n := range nodes[1:] {
|
||||||
v = v.or(p.of(n))
|
v = v.or(p.prove(n))
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
@@ -140,9 +140,9 @@ func (v proven) or(w proven) proven {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// template proves a template that renders one value: fixed text, or a format that is
|
// proveTemplate proves a template that renders one value: fixed text, or a format that is
|
||||||
// one token alone.
|
// one token alone.
|
||||||
func (p *valueProof) template(t *template) proven {
|
func (p *valueProof) proveTemplate(t *template) proven {
|
||||||
switch {
|
switch {
|
||||||
case t.repeat != 1:
|
case t.repeat != 1:
|
||||||
return unproven(fmt.Sprintf("%q carries a repeat, which composes text rather than one value", t.format))
|
return unproven(fmt.Sprintf("%q carries a repeat, which composes text rather than one value", t.format))
|
||||||
@@ -161,9 +161,9 @@ func (p *valueProof) template(t *template) proven {
|
|||||||
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.key), a.tail)...)
|
||||||
}
|
}
|
||||||
return p.unite(leaves)
|
return p.proveUnion(leaves)
|
||||||
case name == "calc":
|
case name == "calc":
|
||||||
return p.calc(t, body, args)
|
return p.proveCalc(t, body, args)
|
||||||
case builtins[name].number != nil:
|
case builtins[name].number != nil:
|
||||||
return builtins[name].number(body, builtins[name].prints, args)
|
return builtins[name].number(body, builtins[name].prints, args)
|
||||||
case isTransform:
|
case isTransform:
|
||||||
@@ -172,12 +172,12 @@ func (p *valueProof) template(t *template) proven {
|
|||||||
return printing(body, DataTypeString, proven{notOperand: fmt.Sprintf("{%s} prints text, not a number", body)})
|
return printing(body, DataTypeString, proven{notOperand: fmt.Sprintf("{%s} prints text, not a number", body)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *valueProof) calc(t *template, body string, args []string) proven {
|
func (p *valueProof) proveCalc(t *template, body string, args []string) proven {
|
||||||
expr, err := parseCalc(args[0])
|
expr, err := parseCalc(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("fejkdata: calc(%q) reached a proof unparsed: %v", args[0], err))
|
panic(fmt.Sprintf("fejkdata: calc(%q) reached a proof unparsed: %v", args[0], err))
|
||||||
}
|
}
|
||||||
v, doubt := p.expr(expr, t.fields)
|
v, doubt := p.proveExpr(expr, t.fields)
|
||||||
if doubt == "" && !(magnitude(v) <= calcLimit) {
|
if doubt == "" && !(magnitude(v) <= calcLimit) {
|
||||||
doubt = calcText(expr) + " is not proven within 1e300"
|
doubt = calcText(expr) + " is not proven within 1e300"
|
||||||
}
|
}
|
||||||
@@ -191,28 +191,28 @@ func (p *valueProof) calc(t *template, body string, args []string) proven {
|
|||||||
// math.MaxFloat64 that rounding in the bounds cannot hide an overflow.
|
// math.MaxFloat64 that rounding in the bounds cannot hide an overflow.
|
||||||
const calcLimit = 1e300
|
const calcLimit = 1e300
|
||||||
|
|
||||||
// expr bounds a calc expression from its operands, or says why it cannot.
|
// proveExpr bounds a calc expression from its operands, or says why it cannot.
|
||||||
func (p *valueProof) expr(n calcNode, fields map[string]node) (proven, string) {
|
func (p *valueProof) proveExpr(n calcNode, fields map[string]node) (proven, string) {
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case calcNum:
|
case calcNum:
|
||||||
v := float64(n)
|
v := float64(n)
|
||||||
return bounded(v, v, v == math.Trunc(v)), ""
|
return bounded(v, v, v == math.Trunc(v)), ""
|
||||||
case calcVar:
|
case calcVar:
|
||||||
v := p.of(fields[string(n)])
|
v := p.prove(fields[string(n)])
|
||||||
if v.notOperand != "" {
|
if v.notOperand != "" {
|
||||||
return proven{}, fmt.Sprintf("operand %q: %s", string(n), v.notOperand)
|
return proven{}, fmt.Sprintf("operand %q: %s", string(n), v.notOperand)
|
||||||
}
|
}
|
||||||
return proven{lo: v.lo, hi: v.hi, nonZero: v.nonZero, integral: v.integral}, ""
|
return proven{lo: v.lo, hi: v.hi, nonZero: v.nonZero, integral: v.integral}, ""
|
||||||
case calcNeg:
|
case calcNeg:
|
||||||
v, doubt := p.expr(n.x, fields)
|
v, doubt := p.proveExpr(n.x, fields)
|
||||||
v.lo, v.hi = -v.hi, -v.lo
|
v.lo, v.hi = -v.hi, -v.lo
|
||||||
return v, doubt
|
return v, doubt
|
||||||
case calcBin:
|
case calcBin:
|
||||||
l, doubt := p.expr(n.l, fields)
|
l, doubt := p.proveExpr(n.l, fields)
|
||||||
if doubt != "" {
|
if doubt != "" {
|
||||||
return l, doubt
|
return l, doubt
|
||||||
}
|
}
|
||||||
r, doubt := p.expr(n.r, fields)
|
r, doubt := p.proveExpr(n.r, fields)
|
||||||
if doubt != "" {
|
if doubt != "" {
|
||||||
return r, doubt
|
return r, doubt
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user