diff --git a/calc.go b/calc.go index 1cf8ff5..818349b 100644 --- a/calc.go +++ b/calc.go @@ -26,7 +26,7 @@ func checkCalcFields(args []string, fields map[string]node) error { // checkCalcNames holds each calc of t reading a name to the checks checkCalcFields makes of a field. func checkCalcNames(path string, t *template) error { for _, o := range t.compiled.ops { - if o.Fn != "calc" || !slices.ContainsFunc(o.operands, func(a arm) bool { return a.kind == namedRead }) { + if o.Fn != builtinfunc.Calc || !slices.ContainsFunc(o.operands, func(a arm) bool { return a.kind == namedRead }) { continue } if err := checkOperands(o.Args[0], builtinfunc.ParsedCalc(o.Args[0]), operandNodes(o)); err != nil { diff --git a/format.go b/format.go index b090de6..65de46e 100644 --- a/format.go +++ b/format.go @@ -12,7 +12,7 @@ import ( // operands fields holds. func checkFunc(tok grammar.Token, fields map[string]node) error { err := builtinfunc.Check(tok.Fn, tok.Args) - if err == nil && tok.Fn == "calc" { + if err == nil && tok.Fn == builtinfunc.Calc { err = checkCalcFields(tok.Args, fields) } if err != nil { diff --git a/internal/builtinfunc/builtinfunc.go b/internal/builtinfunc/builtinfunc.go index ed0c716..b6baf16 100644 --- a/internal/builtinfunc/builtinfunc.go +++ b/internal/builtinfunc/builtinfunc.go @@ -43,6 +43,10 @@ type builtin struct { prints datatype.DataType } +// Calc is the name of the builtin whose operands the caller checks and proves, as nodes only +// it holds. +const Calc = "calc" + // rng is the randomness a builtin sample draws from, which *drawstate.State satisfies. type rng interface { IntN(n int) int diff --git a/internal/builtinfunc/builtins.go b/internal/builtinfunc/builtins.go index 83f3443..8696650 100644 --- a/internal/builtinfunc/builtins.go +++ b/internal/builtinfunc/builtins.go @@ -66,7 +66,7 @@ var builtins = withTransforms(map[string]builtin{ }}, "date": {arity: -1, checkArgs: dateArgs, prep: datePrep}, "time": {arity: -1, checkArgs: timeArg, prep: timePrep}, - "calc": {arity: -1, checkArgs: checkCalc, prep: calcPrep, operands: calcOperands, noRefOperands: true}, + Calc: {arity: -1, checkArgs: checkCalc, prep: calcPrep, operands: calcOperands, noRefOperands: true}, // seq is the one stateful builtin: a per-generator counter from 1, advancing on // each call. An optional name selects an independent counter; no name uses the // default one. Deterministic by construction, so seeded output stays stable. diff --git a/internal/datatype/datatype.go b/internal/datatype/datatype.go index aa90ef5..f8b9200 100644 --- a/internal/datatype/datatype.go +++ b/internal/datatype/datatype.go @@ -31,4 +31,9 @@ func (d DataType) String() string { } // Noun is the datatype as an error names what a value is. -func Noun(d DataType) string { return nouns[d] } +func Noun(d DataType) string { + if d < 0 || d >= Count { + return d.String() + } + return nouns[d] +} diff --git a/todo.md b/todo.md index 2d0344e..9e98ad7 100644 --- a/todo.md +++ b/todo.md @@ -380,7 +380,7 @@ Require the path step to reach a sibling category. ### 46. Report the same error every load for a table with two bad options, and for a folder with two unnamed rows files. -`readTableOptions` and `walkDir` in `internal/datafiles` return on the first in Go's map order. +`readTableOptions` in `table.go` and `walkDir` in `internal/datafiles` return on the first in Go's map order. ### 107. Move the template engine into `internal/`, leaving the root the public API and its options. diff --git a/valueproof.go b/valueproof.go index 7bc59cd..40bf2b3 100644 --- a/valueproof.go +++ b/valueproof.go @@ -173,7 +173,7 @@ func (p *valueProof) proveTemplate(t *template) proven.Value { leaves = append(leaves, a.leaves...) } return p.proveUnion(leaves) - case name == "calc": + case name == builtinfunc.Calc: return p.proveCalc(o) case builtinfunc.IsTransform(name): return proven.Unproven(fmt.Sprintf("{%s} rewrites text rather than printing a value; write the values it would print", body))