doc/ref: change meaning of numbers and lists, remove %

- The current definitions for numbers work great for
unification, but are horrible for subsumption.
This especially breaks down when reasoning over types
and optimizing representations. The crux is `decimal`
integer literals being both float and int. The main change
is that these are now only always integers.
In practice this is cumbersome, so users will more likely
want to use `number` rather than `float`.

- As a result, the meaning of % makes little sense.
  We remove it for now at least.

- Also to aleviate subsumption issue, keeping lists open in
binary operation was somewhat unusual, as operations
require picking a default, which closes it usually.

Change-Id: Ifa5266dae8902472deb48886768f56baf78b5db9
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/1960
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/binop.go b/cue/binop.go
index b06ceb5..f5a6068 100644
--- a/cue/binop.go
+++ b/cue/binop.go
@@ -911,9 +911,6 @@
 			ctx.Quo(&n.v, &x.v, &y.v)
 			ctx.Reduce(&n.v, &n.v)
 			n.k = floatKind
-		case opRem:
-			ctx.Rem(&n.v, &x.v, &y.v)
-			n.k = floatKind
 		case opIDiv:
 			intOp(ctx, n, (*big.Int).Div, x, y)
 		case opIMod:
@@ -995,7 +992,7 @@
 			d := apd.New(int64(y.d), 0)
 			ctx.Quo(&n.v, &n.v, d)
 			return n
-		case opRem:
+		case opIRem:
 			n := &numLit{
 				numBase: newNumBase(nil, newNumInfo(intKind, 0, 10, false)),
 			}
@@ -1016,7 +1013,7 @@
 			f, _ := y.v.Float64()
 			d := time.Duration(float64(x.d) * f)
 			return &durationLit{binSrc(src.Pos(), op, x, other), d}
-		case opRem:
+		case opIRem:
 			d := x.d % time.Duration(y.intValue(ctx))
 			return &durationLit{binSrc(src.Pos(), op, x, other), d}
 		}
diff --git a/cue/op.go b/cue/op.go
index 5b4300d..8becafc 100644
--- a/cue/op.go
+++ b/cue/op.go
@@ -191,7 +191,6 @@
 	opSub: "-",
 	opMul: "*",
 	opQuo: "/",
-	opRem: "%",
 
 	opIDiv: "div",
 	opIMod: "mod",
@@ -209,7 +208,6 @@
 	token.SUB: opSub, // -
 	token.MUL: opMul, // *
 	token.QUO: opQuo, // /
-	token.REM: opRem, // %
 
 	token.IDIV: opIDiv, // div
 	token.IMOD: opIMod, // mod
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index 34798b2..63b3520 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -141,13 +141,11 @@
 			sum: -1 + +2        // 1
 			div1: 2.0 / 3 * 6   // 4
 			div2: 2 / 3 * 6     // 4
-			rem: 2 % 3          // 2
 			b: 1 != 4
 
 			v1: 1.0T/2.0
 			v2: 2.0 == 2
 			v3: 2.0/3.0
-			v4: 2.0%3.0
 			v5: i1 div i2
 
 			e0: 2 + "a"
@@ -165,12 +163,10 @@
 			`sum: 1, ` +
 			`div1: 4.00000000000000000000000, ` +
 			`div2: 4.00000000000000000000000, ` +
-			`rem: 2, ` +
 			`b: true, ` +
 			`v1: 5e+11, ` +
 			`v2: true, ` +
 			`v3: 0.666666666666666666666667, ` +
-			`v4: 2.0, ` +
 			`v5: 0, ` +
 
 			`e0: _|_((2 + "a"):invalid operation 2 + "a" (mismatched types int and string)), ` +
@@ -587,10 +583,9 @@
 				v2: 2.0 == 2
 				n1: 1
 				v5: 2.0 / n1
-				e1: 2.0 % (3&int)
 				e2: int & 4.0/2.0
 				`,
-		out: `<0>{v1: 5e+11, v2: true, n1: 1, v5: 2, e1: 2.0, e2: _|_((int & (4.0 / 2.0)):conflicting values int and (4.0 / 2.0) (mismatched types int and float))}`,
+		out: `<0>{v1: 5e+11, v2: true, n1: 1, v5: 2, e2: _|_((int & (4.0 / 2.0)):conflicting values int and (4.0 / 2.0) (mismatched types int and float))}`,
 	}, {
 		desc: "inequality",
 		in: `
diff --git a/cue/scanner/scanner.go b/cue/scanner/scanner.go
index 3834033..19881fb 100644
--- a/cue/scanner/scanner.go
+++ b/cue/scanner/scanner.go
@@ -932,8 +932,9 @@
 			} else {
 				tok = token.QUO
 			}
-		case '%':
-			tok = token.REM
+		// We no longer use %, but seems like a useful token to use for
+		// something else at some point.
+		// case '%':
 		case '<':
 			if s.ch == '-' {
 				s.next()
diff --git a/cue/scanner/scanner_test.go b/cue/scanner/scanner_test.go
index 6748ce2..b182e21 100644
--- a/cue/scanner/scanner_test.go
+++ b/cue/scanner/scanner_test.go
@@ -127,7 +127,6 @@
 	{token.SUB, "-", operator},
 	{token.MUL, "*", operator},
 	{token.QUO, "/", operator},
-	{token.REM, "%", operator},
 
 	{token.AND, "&", operator},
 	{token.OR, "|", operator},
@@ -364,7 +363,6 @@
 	"-\n",
 	"*\n",
 	"/\n",
-	"%\n",
 
 	"&\n",
 	// "&^\n",
diff --git a/cue/token/token.go b/cue/token/token.go
index e94859f..3edfed1 100644
--- a/cue/token/token.go
+++ b/cue/token/token.go
@@ -49,7 +49,6 @@
 	MUL // *
 	POW // ^
 	QUO // /
-	REM // %
 
 	IQUO // quo
 	IREM // rem
@@ -111,10 +110,9 @@
 	EOF:     "EOF",
 	COMMENT: "COMMENT",
 
-	IDENT: "IDENT",
-	INT:   "INT",
-	FLOAT: "FLOAT",
-	// DURATION:      "DURATION", // TODO
+	IDENT:         "IDENT",
+	INT:           "INT",
+	FLOAT:         "FLOAT",
 	STRING:        "STRING",
 	INTERPOLATION: "INTERPOLATION",
 	ATTRIBUTE:     "ATTRIBUTE",
@@ -124,7 +122,6 @@
 	MUL: "*",
 	POW: "^",
 	QUO: "/",
-	REM: "%",
 
 	IQUO: "quo",
 	IREM: "rem",
@@ -228,7 +225,7 @@
 		return 5
 	case ADD, SUB:
 		return 6
-	case MUL, QUO, REM, IDIV, IMOD, IQUO, IREM:
+	case MUL, QUO, IDIV, IMOD, IQUO, IREM:
 		return 7
 	}
 	return lowestPrec
diff --git a/cue/types_test.go b/cue/types_test.go
index 163e5a4..f84f448 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -2104,9 +2104,6 @@
 		input: "v: 2 / 5",
 		want:  "/ 2 5",
 	}, {
-		input: "v: 2 % 5",
-		want:  "% 2 5",
-	}, {
 		input: "v: 2 quo 5",
 		want:  "quo 2 5",
 	}, {
diff --git a/doc/ref/spec.md b/doc/ref/spec.md
index b822e6c..84ae5db 100644
--- a/doc/ref/spec.md
+++ b/doc/ref/spec.md
@@ -273,7 +273,7 @@
 -     mod   ||    !=    >     :     {     }
 *     quo   &     =~    <=    =     [     ]
 /     rem   |     !~    >=    <-    ...   ,
-%           _|_   !           ;
+            _|_   !           ;
 ```
 <!-- :: for "is-a" definitions -->
 
@@ -866,17 +866,26 @@
 The _decimal floating-point type_ represents the set of all decimal floating-point
 numbers.
 They are two distinct types.
-The predeclared integer and decimal floating-point types are `int` and `float`;
-they are defined types.
+Both are instances instances of a generic `number` type.
+
+<!--
+                    number
+                   /      \
+                int      float
+-->
+
+The predeclared number, integer, decimal floating-point types are
+`number`, `int` and `float`; they are defined types.
+<!--
+TODO: should we drop float? It is somewhat preciser and probably a good idea
+to have it in the programmatic API, but it may be confusing to have to deal
+with it in the language.
+-->
 
 A decimal floating-point literal always has type `float`;
 it is not an instance of `int` even if it is an integral number.
 
-An integer literal has both type `int` and `float`, with the integer variant
-being the default if no other constraints are applied.
-Expressed in terms of disjunction and [type conversion](#conversions),
-the literal `1`, for instance, is defined as `int(1) | float(1)`.
-Hexadecimal, octal, and binary integer literals are always of type `int`.
+Integer literals are always of type `int and don't match type `float`.
 
 Numeric literals are exact values of arbitrary precision.
 If the operation permits it, numbers should be kept in arbitrary precision.
@@ -991,8 +1000,11 @@
 OptionalLabel = ConcreteLabel "?"
 Label         = ConcreteLabel | OptionalLabel | TemplateLabel .
 
-attribute     = "@" identifier "(" attr_elem { "," attr_elem } ")" .
-attr_elem     =  attr_string | identifier "=" attr_string .
+attribute     = "@" identifier "(" attr_elems ")" .
+attr_elems    = attr_elem { "," attr_elem }
+attr_elem     =  attr_string | attr_label | attr_nest .
+attr_label    = identifier "=" attr_string .
+attr_nest     = identifier "(" attr_elems ")" .
 attr_string   = { attr_char } | string_lit .
 attr_char     = /* an arbitrary Unicode code point except newline, ',', '"', `'`, '#', '=', '(', and ')' */ .
 ```
@@ -1300,6 +1312,10 @@
 uint128   >=0 & <=340_282_366_920_938_463_463_374_607_431_768_211_455
 int128    >=-170_141_183_460_469_231_731_687_303_715_884_105_728 &
            <=170_141_183_460_469_231_731_687_303_715_884_105_727
+float32   >=-3.40282346638528859811704183484516925440e+38 &
+          <=3.40282346638528859811704183484516925440e+38
+float64   >=-1.797693134862315708145274237317043567981e+308 &
+          <=1.797693134862315708145274237317043567981e+308
 ```
 
 
@@ -1702,7 +1718,7 @@
 binary_op  = "|" | "&" | "||" | "&&" | "==" | rel_op | add_op | mul_op  .
 rel_op     = "!=" | "<" | "<=" | ">" | ">=" | "=~" | "!~" .
 add_op     = "+" | "-" .
-mul_op     = "*" | "/" | "%" | "div" | "mod" | "quo" | "rem" .
+mul_op     = "*" | "/" | "div" | "mod" | "quo" | "rem" .
 unary_op   = "+" | "-" | "!" | "*" | rel_op .
 ```
 
@@ -1750,7 +1766,7 @@
 
 ```
 Precedence    Operator
-    7             *  /  %  div mod quo rem
+    7             *  / div mod quo rem
     6             +  -
     5             ==  !=  <  <=  >  >= =~ !~
     4             &&
@@ -1778,7 +1794,7 @@
 as the first operand. The three of the four standard arithmetic operators
 `(+, -, *)` apply to integer and decimal floating-point types;
 `+` and `*` also apply to lists and strings.
-`/` and `%` only apply to decimal floating-point types and
+`/` only applies to decimal floating-point types and
 `div`, `mod`, `quo`, and `rem` only apply to integer types.
 
 ```
@@ -1786,13 +1802,18 @@
 -    difference             integers, floats
 *    product                integers, floats, lists, strings, bytes
 /    quotient               floats
-%    remainder              floats
 div  division               integers
 mod  modulo                 integers
 quo  quotient               integers
 rem  remainder              integers
 ```
 
+For any operator that accepts operands of type `float`, any operand may be
+of type `int` or `float`, in which case the result will be `float` if any
+of the operands is `float` or `int` otherwise.
+For `/` the result is always `float`.
+
+
 #### Integer operators
 
 For two integer values `x` and `y`,
@@ -1857,25 +1878,19 @@
 #### List operators
 
 Lists can be concatenated using the `+` operator.
-For lists `a` and `b`,
-```
-a + b
-```
-will produce an open list if `b` is open.
-If list `a` is open, its default value, the shortest variant, is selected.
+Opens list are closed to their default value beforehand.
 
 ```
 [ 1, 2 ]      + [ 3, 4 ]       // [ 1, 2, 3, 4 ]
 [ 1, 2, ... ] + [ 3, 4 ]       // [ 1, 2, 3, 4 ]
-[ 1, 2 ]      + [ 3, 4, ... ]  // [ 1, 2, 3, 4, ... ]
-[ 1, 2, ... ] + [ 3, 4, ... ]  // [ 1, 2, 3, 4, ... ]
+[ 1, 2 ]      + [ 3, 4, ... ]  // [ 1, 2, 3, 4 ]
 ```
 
 Lists can be multiplied with a non-negative`int` using the `*` operator
 to create a repeated the list by the indicated number.
 ```
 3*[1,2]         // [1, 2, 1, 2, 1, 2]
-3*[1, 2, ...]   // [1, 2, 1, 2, 1 ,2, ...]
+3*[1, 2, ...]   // [1, 2, 1, 2, 1 ,2]
 [byte]*4        // [byte, byte, byte, byte]
 0*[1,2]         // []
 ```
@@ -1908,6 +1923,7 @@
 ```
 <!-- jba: Do these work for byte sequences? If not, why not? -->
 
+
 ##### Comparison operators
 
 Comparison operators compare two operands and yield an untyped boolean value.
@@ -2248,15 +2264,21 @@
 string            string length in bytes
 bytes             length of byte sequence
 list              list length, smallest length for an open list
-struct            number of distinct fields
+struct            number of distinct data fields, including optional
 ```
+<!-- TODO: consider not supporting len, but instead rely on more
+precisely named builtin functions:
+  - strings.RuneLen(x)
+  - bytes.Len(x)  // x may be a string
+  - struct.NumFooFields(x)
+  - list.Len(x)
+-->
 
 ```
 Expression           Result
 len("Hellø")         6
 len([1, 2, 3])       3
-len([1, 2, ...])     2
-len({a:1, b:2})      2
+len([1, 2, ...])     >=2
 ```
 
 ### `and`
@@ -2276,10 +2298,12 @@
 the `|` operator to all elements in the list.
 It returns bottom for the empty list.
 
+```
 Expression:          Result
 and([a, b])          a | b
 and([a])             a
 and([])              _|_
+```
 
 
 ## Cycles
diff --git a/doc/tutorial/kubernetes/tut_test.go b/doc/tutorial/kubernetes/tut_test.go
index 4df54c8..defa61b 100644
--- a/doc/tutorial/kubernetes/tut_test.go
+++ b/doc/tutorial/kubernetes/tut_test.go
@@ -255,9 +255,6 @@
 	log.Printf(format, args...)
 }
 
-// TODO:
-// Test manual and quick: evaluation results in output of testdata directory.
-
 func TestEval(t *testing.T) {
 	for _, dir := range []string{"quick", "manual"} {
 		t.Run(dir, func(t *testing.T) {