A column of one reference alone takes the datatype and null of the column it reads #15
@@ -312,9 +312,9 @@ order.id: datatype integer: "1{digits(2)}" is not one value; write one literal o
|
|||||||
|
|
||||||
A column of one reference alone to another record's column — `"score": "{/src.score}"`
|
A column of one reference alone to another record's column — `"score": "{/src.score}"`
|
||||||
— is that column: it takes the column's datatype and is null where the column is, and
|
— is that column: it takes the column's datatype and is null where the column is, and
|
||||||
a struct field tagged `src.score` is nil there. A `datatype` of its own types a string
|
a struct field tagged `src.score` is nil there. A `datatype` of its own types the
|
||||||
column's values, and over a typed column is refused naming the datatype it takes. Any
|
column's values where they prove it, and one restating the datatype it takes is refused.
|
||||||
other read renders the column's text, a null as `""`.
|
Any other read renders the column's text, a null as `""`.
|
||||||
|
|
||||||
A typed column's `{calc()}` must be proven to print a number: each operand a number
|
A typed column's `{calc()}` must be proven to print a number: each operand a number
|
||||||
literal, an `{int()}`, `{float()}`, `{seq()}` or `{digits()}` call, a calc, or a read of
|
literal, an `{int()}`, `{float()}`, `{seq()}` or `{digits()}` call, a calc, or a read of
|
||||||
@@ -549,10 +549,11 @@ tokens add cost in proportion to the output.
|
|||||||
tag of one reference alone, `{/users}`, is refused naming the path `users`: both
|
tag of one reference alone, `{/users}`, is refused naming the path `users`: both
|
||||||
render the same text, and only the path names a record. `IsTemplate` exports the
|
render the same text, and only the path names a record. `IsTemplate` exports the
|
||||||
rule, so the CLI, struct tags and any other caller read one.
|
rule, so the CLI, struct tags and any other caller read one.
|
||||||
- **An inline template skips the cycle fence, and only that one.** `New` proves the
|
- **An inline template skips the cycle fence.** `New` proves the loaded tree
|
||||||
loaded tree acyclic, an inline node is a finite tree of its own, and nothing in
|
acyclic, an inline node is a finite tree of its own, and nothing in the tree can
|
||||||
the tree can reference it, so no render of it reaches itself. Every other fence
|
reference it, so no render of it reaches itself. Every other fence runs over both,
|
||||||
runs over both, from one `checkScope`.
|
from one `checkScope`, except that struct tags leave column agreement to their Go
|
||||||
|
types.
|
||||||
- **An inline template that does not compile is misuse (exit 2), including a
|
- **An inline template that does not compile is misuse (exit 2), including a
|
||||||
reference that resolves to nothing** — the whole argument is the spelling under
|
reference that resolves to nothing** — the whole argument is the spelling under
|
||||||
test, and `NewTemplate` compiles, links and validates as one step. An unknown
|
test, and `NewTemplate` compiles, links and validates as one step. An unknown
|
||||||
@@ -672,9 +673,9 @@ tokens add cost in proportion to the output.
|
|||||||
one comparison, and `1{digits(2)}` is a second spelling of `{int(100,199)}`.
|
one comparison, and `1{digits(2)}` is a second spelling of `{int(100,199)}`.
|
||||||
- **A column of one reference alone is the column it reads.** `{/src.score}` renders
|
- **A column of one reference alone is the column it reads.** `{/src.score}` renders
|
||||||
exactly what `src.score` draws, so it takes that column's datatype and null rather
|
exactly what `src.score` draws, so it takes that column's datatype and null rather
|
||||||
than restating them, and a `datatype` restating a typed column is a second spelling.
|
than restating them, and a `datatype` restating the one it takes is a second
|
||||||
Over a string column a `datatype` still types the values — the one way to type a
|
spelling. Any other `datatype` still types the values — the one way to type a column
|
||||||
column someone else wrote.
|
someone else wrote.
|
||||||
- **A typed column's calc is refused unless proven.** Operand bounds must keep each
|
- **A typed column's calc is refused unless proven.** Operand bounds must keep each
|
||||||
divisor from zero and the result finite; what they cannot show is refused rather
|
divisor from zero and the result finite; what they cannot show is refused rather
|
||||||
than trusted, since a bare `NaN` breaks the JSON and SQL it lands in.
|
than trusted, since a bare `NaN` breaks the JSON and SQL it lands in.
|
||||||
|
|||||||
+24
-2
@@ -165,11 +165,33 @@ func disagreement(a *template, da DataType, b *template, db DataType) error {
|
|||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case da != DataTypeString && db != DataTypeString:
|
case da != DataTypeString && db != DataTypeString:
|
||||||
return fmt.Errorf("its items hold %s and %s; a column holds one datatype", da, db)
|
return bothTyped(a, da, b, db)
|
||||||
case typed.datatype == DataTypeString && (&valueProof{}).columnItem(bare).not[want] != "":
|
case typed.datatype == DataTypeString && (&valueProof{}).columnItem(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, write {"format":"{text}","text":%q}`, bare.format, dataTypeNouns[want], typed.format, typed.format)
|
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)
|
||||||
}
|
}
|
||||||
return fmt.Errorf(`item %q declares no datatype beside one declaring %s; a column holds one, so give it "datatype": %q`, bare.format, want, want)
|
return fmt.Errorf(`item %q declares no datatype beside one declaring %s; a column holds one, so give it "datatype": %q`, bare.format, want, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bothTyped names the fix for two items holding different datatypes: reading one that takes its
|
||||||
|
// datatype from the column it reads as text, since only a declared datatype can be edited away.
|
||||||
|
func bothTyped(a *template, da DataType, b *template, db DataType) error {
|
||||||
|
read := a
|
||||||
|
if a.datatype != DataTypeString {
|
||||||
|
read = b
|
||||||
|
}
|
||||||
|
if 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, so to read %q as text, %s", da, db, read.format, asText(read))
|
||||||
|
}
|
||||||
|
|
||||||
|
// asText names the spelling that reads the column a column-read item reads as text, keeping the
|
||||||
|
// other keys an object item carries.
|
||||||
|
func asText(t *template) string {
|
||||||
|
if t.fromString {
|
||||||
|
return fmt.Sprintf(`write {"format":"{text}","text":%q}`, t.format)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(`set its "format" to "{text}" and add "text": %q`, t.format)
|
||||||
|
}
|
||||||
|
|||||||
@@ -177,7 +177,6 @@ func inlineScope(n node, label string) nodeScope {
|
|||||||
return func(fn func(path string, m node) error) error { return eachNode(n, label, fn) }
|
return func(fn func(path string, m node) error) error { return eachNode(n, label, fn) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkScope runs every fence over a scope: checkColumns, then checkRenders.
|
|
||||||
func checkScope(s nodeScope) error {
|
func checkScope(s nodeScope) error {
|
||||||
if err := checkColumns(s); err != nil {
|
if err := checkColumns(s); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ type valueProof struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checkDatatype rejects a typed column item some render of which is not text of its datatype,
|
// checkDatatype rejects a typed column item some render of which is not text of its datatype,
|
||||||
// and one declaring a datatype over the typed column it is.
|
// and one restating the datatype of the column it is.
|
||||||
func (p *valueProof) checkDatatype(path string, n node) error {
|
func (p *valueProof) checkDatatype(path string, n node) error {
|
||||||
t, ok := n.(*template)
|
t, ok := n.(*template)
|
||||||
if !ok || t.datatype == DataTypeString {
|
if !ok || t.datatype == DataTypeString {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if d := readDatatype(t); d != DataTypeString {
|
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.columnItem(t).not[t.datatype]; reason != "" {
|
||||||
|
|||||||
Reference in New Issue
Block a user