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:
2026-09-25 18:53:42 +02:00
committed by Lillem4n
parent 4b087df57e
commit 65091233a8
8 changed files with 72 additions and 74 deletions
+2 -2
View File
@@ -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
View File
@@ -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))
+5 -5
View File
@@ -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
} }
+11 -11
View File
@@ -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)
} }
+16 -16
View File
@@ -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
View File
@@ -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)}
+3 -5
View File
@@ -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
+31 -31
View File
@@ -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
} }