cue: expose difference between float, int, and number
Having this explicit difference is useful when
analyzing for code generation. This does not
necessarily mean this difference needs to be
exposed in the language itself.
This does not implement the proposed spec
change where integer literals are only literals.
Change-Id: Id2c45208409d43278a72c4171a1a1c28cd093d21
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/1963
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cue/types.go b/cue/types.go
index 5c19d62..22bd98a 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -43,8 +43,13 @@
// BoolKind indicates a boolean value.
BoolKind
- // NumberKind represents any kind of number.
- NumberKind
+ // IntKind represents an integral number.
+ IntKind
+
+ // FloatKind represents a decimal float point number that cannot be
+ // converted to an integer. The underlying number may still be integral,
+ // but resulting from an operation that enforces the float type.
+ FloatKind
// StringKind indicates any kind of string.
StringKind
@@ -59,6 +64,9 @@
ListKind
nextKind
+
+ // NumberKind represents any kind of number.
+ NumberKind = IntKind | FloatKind
)
// An structValue represents a JSON object.
@@ -483,6 +491,9 @@
// are not concrete. For instance, it will return BottomKind for the bounds
// >=0.
func (v Value) Kind() Kind {
+ if v.path == nil {
+ return BottomKind
+ }
k := v.eval(v.ctx()).kind()
if k.isGround() {
switch {
@@ -490,6 +501,10 @@
return NullKind
case k.isAnyOf(boolKind):
return BoolKind
+ case k&numKind == (intKind):
+ return IntKind
+ case k&numKind == (floatKind):
+ return FloatKind
case k.isAnyOf(numKind):
return NumberKind
case k.isAnyOf(bytesKind):
@@ -516,8 +531,10 @@
vk |= NullKind
case boolKind:
vk |= BoolKind
- case intKind, floatKind:
- vk |= NumberKind
+ case intKind:
+ vk |= IntKind
+ case floatKind:
+ vk |= FloatKind
case stringKind:
vk |= StringKind
case bytesKind:
diff --git a/cue/types_test.go b/cue/types_test.go
index f7ddab0..3d199d7 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -97,8 +97,8 @@
incompleteKind: NumberKind,
}, {
value: `2.0`,
- kind: NumberKind,
- incompleteKind: NumberKind,
+ kind: FloatKind,
+ incompleteKind: FloatKind,
}, {
value: `2.0Mi`,
kind: NumberKind,
@@ -114,7 +114,7 @@
}, {
value: `float`,
kind: BottomKind,
- incompleteKind: NumberKind,
+ incompleteKind: FloatKind,
}, {
value: `"str"`,
kind: StringKind,
@@ -247,6 +247,7 @@
exp int
fmt byte
prec int
+ kind Kind
err string
}{{
value: "1",
@@ -255,6 +256,7 @@
exp: 0,
float64: 1,
fmt: 'g',
+ kind: NumberKind,
}, {
value: "-1",
float: "-1",
@@ -262,6 +264,7 @@
exp: 0,
float64: -1,
fmt: 'g',
+ kind: NumberKind,
}, {
value: "1.0",
float: "1.0",
@@ -269,6 +272,7 @@
exp: -1,
float64: 1.0,
fmt: 'g',
+ kind: FloatKind,
}, {
value: "2.6",
float: "2.6",
@@ -276,6 +280,7 @@
exp: -1,
float64: 2.6,
fmt: 'g',
+ kind: FloatKind,
}, {
value: "20.600",
float: "20.60",
@@ -284,6 +289,7 @@
float64: 20.60,
prec: 2,
fmt: 'f',
+ kind: FloatKind,
}, {
value: "1/0",
float: "∞",
@@ -291,6 +297,7 @@
prec: 2,
fmt: 'f',
err: ErrAbove.Error(),
+ kind: FloatKind,
}, {
value: "-1/0",
float: "-∞",
@@ -298,6 +305,7 @@
prec: 2,
fmt: 'f',
err: ErrBelow.Error(),
+ kind: FloatKind,
}, {
value: "1.797693134862315708145274237317043567982e+308",
float: "1.8e+308",
@@ -307,6 +315,7 @@
prec: 2,
fmt: 'g',
err: ErrAbove.Error(),
+ kind: FloatKind,
}, {
value: "-1.797693134862315708145274237317043567982e+308",
float: "-1.8e+308",
@@ -315,6 +324,7 @@
float64: math.Inf(-1),
prec: 2,
fmt: 'g',
+ kind: FloatKind,
err: ErrBelow.Error(),
}, {
value: "4.940656458412465441765687928682213723650e-324",
@@ -324,6 +334,7 @@
float64: 0,
prec: 4,
fmt: 'g',
+ kind: FloatKind,
err: ErrBelow.Error(),
}, {
value: "-4.940656458412465441765687928682213723650e-324",
@@ -333,12 +344,13 @@
float64: 0,
prec: -1,
fmt: 'g',
+ kind: FloatKind,
err: ErrAbove.Error(),
}}
for _, tc := range testCases {
t.Run(tc.value, func(t *testing.T) {
n := getInstance(t, tc.value).Value()
- if n.Kind() != NumberKind {
+ if n.Kind() != tc.kind {
t.Fatal("Not a number")
}