Prechádzať zdrojové kódy

Painless: Types Section Clean Up (#30283)

Clean up of types section, casting section, and a large number of examples.
Jack Conradson 7 rokov pred
rodič
commit
a96a45c6ae

+ 417 - 133
docs/painless/painless-casting.asciidoc

@@ -1,172 +1,456 @@
 [[painless-casting]]
 === Casting
 
-Casting is the conversion of one type to another. Implicit casts are casts that
-occur automatically, such as during an assignment operation. Explicit casts are
-casts where you use the casting operator to explicitly convert one type to
-another. This is necessary during operations where the cast cannot be inferred.
+A cast converts the value of an original type to the equivalent value of a
+target type. An implicit cast infers the target type and automatically occurs
+during certain <<painless-operators, operations>>. An explicit cast specifies
+the target type and forcefully occurs as its own operation.  Use the *cast
+operator* to specify an explicit cast.
 
-To cast to a new type, precede the expression by the new type enclosed in
-parentheses, for example
-`(int)x`.
+*Errors*
 
-The following sections specify the implicit casts that can be performed and the
-explicit casts that are allowed. The only other permitted cast is casting
-a single character `String` to a `char`.
+* If during a cast there exists no equivalent value for the target type.
+* If an implicit cast is given, but an explicit cast is required.
 
-*Grammar:*
+*Grammar*
 [source,ANTLR4]
 ----
 cast: '(' TYPE ')' expression
 ----
 
-[[numeric-casting]]
-==== Numeric Casting
+*Examples*
 
-The following table shows the allowed implicit and explicit casts between
-numeric types. Read the table by row. To find out if you need to explicitly
-cast from type A to type B, find the row for type A and scan across to the
-column for type B.
+* Valid casts.
++
+[source,Painless]
+----
+<1> int i = (int)5L;
+<2> Map m = new HashMap();
+<3> HashMap hm = (HashMap)m;
+----
++
+<1> declare `int i`;
+    explicit cast `long 5` to `int 5` -> `int 5`;
+    assign `int 5` to `i`
+<2> declare `Map m`;
+    allocate `HashMap` instance -> `HashMap reference`;
+    implicit cast `HashMap reference` to `Map reference` -> `Map reference`;
+    assign `Map reference` to `m`
+<3> declare `HashMap hm`;
+    access `m` -> `Map reference`;
+    explicit cast `Map reference` to `HashMap reference` -> `HashMap reference`;
+    assign `HashMap reference` to `hm`
+
+[[numeric-type-casting]]
+==== Numeric Type Casting
 
-IMPORTANT: Explicit casts between numeric types can result in some data loss. A
-smaller numeric type cannot necessarily accommodate the value from a larger
-numeric type. You might also lose precision when casting from integer types
-to floating point types.
+A <<primitive-types, numeric type>> cast converts the value of an original
+numeric type to the equivalent value of a target numeric type. A cast between
+two numeric type values results in data loss when the value of the original
+numeric type is larger than the target numeric type can accommodate. A cast
+between an integer type value and a floating point type value can result in
+precision loss.
+
+The allowed casts for values of each numeric type are shown as a row in the
+following table:
 
 |====
-|       | byte     | short    | char     | int      | long     | float    | double
-| byte  |          | implicit | implicit | implicit | implicit | implicit | implicit
-| short | explicit |          | explicit | implicit | implicit | implicit | implicit
-| char  | explicit | explicit |          | implicit | implicit | implicit | implicit
-| int   | explicit | explicit | explicit |          | implicit | implicit | implicit
-| long  | explicit | explicit | explicit | explicit |          | implicit | implicit
-| float | explicit | explicit | explicit | explicit | explicit |          | implicit
+|        | byte     | short    | char     | int      | long     | float    | double
+| byte   |          | implicit | implicit | implicit | implicit | implicit | implicit
+| short  | explicit |          | explicit | implicit | implicit | implicit | implicit
+| char   | explicit | explicit |          | implicit | implicit | implicit | implicit
+| int    | explicit | explicit | explicit |          | implicit | implicit | implicit
+| long   | explicit | explicit | explicit | explicit |          | implicit | implicit
+| float  | explicit | explicit | explicit | explicit | explicit |          | implicit
 | double | explicit | explicit | explicit | explicit | explicit | explicit |
 |====
 
+*Examples*
+
+* Valid numeric type casts.
++
+[source,Painless]
+----
+<1> int a = 1;
+<2> long b = a;
+<3> short c = (short)b;
+<4> double e = (double)a;
+----
++
+<1> declare `int a`;
+    assign `int 1` to `a`
+<2> declare `long b`;
+    access `a` -> `int 1`;
+    implicit cast `int 1` to `long 1` -> `long 1`;
+    assign `long 1` to `b`
+<3> declare `short c`;
+    access `b` -> `long 1`;
+    explicit cast `long 1` to `short 1` -> `short 1`;
+    assign `short 1` value to `c`
+<4> declare `double e`;
+    access `a` -> `int 1`;
+    explicit cast `int 1` to `double 1.0`;
+    assign `double 1.0` to `e`;
+    (note the explicit cast is extraneous since an implicit cast is valid)
++
+* Invalid numeric type casts resulting in errors.
++
+[source,Painless]
+----
+<1> int a = 1.0; // error
+<2> int b = 2;
+<3> byte c = b;  // error
+----
++
+<1> declare `int i`;
+    *error* -> cannot implicit cast `double 1.0` to `int 1`;
+    (note an explicit cast is valid)
+<2> declare `int b`;
+    assign `int 2` to `b`
+<3> declare byte `c`;
+    access `b` -> `int 2`;
+    *error* -> cannot implicit cast `int 2` to `byte 2`;
+    (note an explicit cast is valid)
+
+[[reference-type-casting]]
+==== Reference Type Casting
+
+A <<reference-types, reference type>> cast converts the value of an original
+reference type to the equivalent value of a target reference type. An implicit
+cast between two reference type values is allowed when the original reference
+type is a descendant of the target type. An explicit cast between two reference
+type values is allowed when the original type is a descendant of the target type
+or the target type is a descendant of the original type.
+
+*Examples*
+
+* Valid reference type casts.
++
+[source,Painless]
+----
+<1> List x;
+<2> ArrayList y = new ArrayList();
+<3> x = y;
+<4> y = (ArrayList)x;
+<5> x = (List)y;
+----
++
+<1> declare `List x`;
+    assign default value `null` to `x`
+<2> declare `ArrayList y`;
+    allocate `ArrayList` instance -> `ArrayList reference`;
+    assign `ArrayList reference` to `y`;
+<3> access `y` -> `ArrayList reference`;
+    implicit cast `ArrayList reference` to `List reference` -> `List reference`;
+    assign `List reference` to `x`;
+    (note `ArrayList` is a descendant of `List`)
+<4> access `x` -> `List reference`;
+    explicit cast `List reference` to `ArrayList reference`
+            -> `ArrayList reference`;
+    assign `ArrayList reference` to `y`;
+<5> access `y` -> `ArrayList reference`;
+    explicit cast `ArrayList reference` to `List reference` -> `List reference`;
+    assign `List reference` to `x`;
+    (note the explicit cast is extraneous, and an implicit cast is valid)
++
+* Invalid reference type casts resulting in errors.
++
+[source,Painless]
+----
+<1> List x = new ArrayList();
+<2> ArrayList y = x;          // error
+<3> Map m = (Map)x;           // error
+----
++
+<1> declare `List x`;
+    allocate `ArrayList` instance -> `ArrayList reference`;
+    implicit cast `ArrayList reference` to `List reference` -> `List reference`;
+    assign `List reference` to `x`
+<2> declare `ArrayList y`;
+    access `x` -> `List reference`;
+    *error* -> cannot implicit cast `List reference` to `ArrayList reference`;
+    (note an explicit cast is valid since `ArrayList` is a descendant of `List`)
+<3> declare `ArrayList y`;
+    access `x` -> `List reference`;
+    *error* -> cannot explicit cast `List reference` to `Map reference`;
+    (note no cast would be valid since neither `List` nor `Map` is a descendant
+            of the other)
+
+[[dynamic-type-casting]]
+==== Dynamic Type Casting
+
+A <<dynamic-types, dynamic (`def`) type>> cast converts the value of an original
+`def` type to the equivalent value of any target type or converts the value of
+any original type to the equivalent value of a target `def` type.
+
+An implicit cast from any original type value to a `def` type value is always
+allowed. An explicit cast from any original type value to a `def` type value is
+always allowed but never necessary.
+
+An implicit or explicit cast from an original `def` type value to
+any target type value is allowed if and only if the cast is normally allowed
+based on the current type value the `def` type value represents.
+
+*Examples*
 
-Example(s)
-[source,Java]
-----
-int a = 1;            // Declare int variable a and set it to the literal
-                      //   value 1
-long b = a;           // Declare long variable b and set it to int variable
-                      //    a with an implicit cast to convert from int to long
-short c = (short)b;   // Declare short variable c, explicitly cast b to a
-                      //   short, and assign b to c
-byte d = a;           // ERROR: Casting an int to a byte requires an explicit
-                      //   cast
-double e = (double)a; // Explicitly cast int variable a to a double and assign
-                      //   it to the double variable e. The explicit cast is
-                      //   allowed, but it is not necessary.
-----
-
-[[reference-casting]]
-==== Reference Casting
-
-A reference type can be implicitly cast to another reference type as long as
-the type being cast _from_ is a descendant of the type being cast _to_.  A
-reference type can be explicitly cast _to_ if the type being cast to is a
-descendant of the type being cast _from_.
-
-*Examples:*
-[source,Java]
-----
-List x;                        // Declare List variable x
-ArrayList y = new ArrayList(); // Declare ArrayList variable y and assign it a
-                               //   newly allocated ArrayList [1]
-x = y;                         // Assign Arraylist y to List x using an
-                               //   implicit cast
-y = (ArrayList)x;              // Explicitly cast List x to an ArrayList and
-                               //  assign it to ArrayList y
-x = (List)y;                   // Set List x to ArrayList y using an explicit
-                               //   cast (the explicit cast is not necessary)
-y = x;                         // ERROR: List x cannot be implicitly cast to
-                               //   an ArrayList, an explicit cast is required
-Map m = y;                     // ERROR: Cannot implicitly or explicitly cast [2]
-                               //   an ArrayList to a Map, no relationship
-                               //   exists between the two types.
-----
-[1] `ArrayList` is a descendant of the `List` type.
-[2] `Map` is unrelated to the `List` and `ArrayList` types.
-
-[[def-type-casting]]
-==== def Type Casting
-All primitive and reference types can always be implicitly cast to
-`def`. While it is possible to explicitly cast to `def`, it is not necessary.
-
-However, it is not always possible to implicitly cast a `def` to other
-primitive and reference types. An explicit cast is required if an explicit
-cast would normally be required between the non-def types.
-
-
-*Examples:*
-[source,Java]
-----
-def x;          // Declare def variable x and set it to null
-x = 3;          // Set the def variable x to the literal 3 with an implicit
-                //   cast from int to def
-double a = x;   // Declare double variable a and set it to def variable x,
-                //   which contains a double
-int b = x;      // ERROR: Results in a run-time error because an explicit cast is
-                //   required to cast from a double to an int
-int c = (int)x; // Declare int variable c, explicitly cast def variable x to an
-                //   int, and assign x to c
+* Valid dynamic type casts with any original type to a target `def` type.
++
+[source,Painless]
 ----
+<1> def d0 = 3;
+<2> d0 = new ArrayList();
+<3> Object o = new HashMap();
+<4> def d1 = o;
+<5> int i = d1.size();
+----
++
+<1> declare `def d0`;
+    implicit cast `int 3` to `def`;
+    assign `int 3` to `d0`
+<2> allocate `ArrayList` instance -> `ArrayList reference`;
+    implicit cast `ArrayList reference` to `def` -> `def`;
+    assign `def` to `d0`
+<3> declare `Object o`;
+    allocate `HashMap` instance -> `HashMap reference`;
+    implicit cast `HashMap reference` to `Object reference`
+            -> `Object reference`;
+    assign `Object reference` to `o`
+<4> declare `def d1`;
+    access `o` -> `Object reference`;
+    implicit cast `Object reference` to `def` -> `def`;
+    assign `def` to `d1`
+<5> declare `int i`;
+    access `d1` -> `def`;
+    implicit cast `def` to `HashMap reference` -> HashMap reference`;
+    call `size` on `HashMap reference` -> `int 0`;
+    assign `int 0` to `i`;
+    (note `def` was implicit cast to `HashMap reference` since `HashMap` is the
+            child-most descendant type value that the `def` type value
+            represents)
++
+* Valid dynamic type casts with an original `def` type to any target type.
++
+[source,Painless]
+----
+<1> def d = 1.0;
+<2> int i = (int)d;
+<3> d = 1;
+<4> float f = d;
+<5> d = new ArrayList();
+<6> List l = d;
+----
++
+<1> declare `def d`;
+    implicit cast `double 1.0` to `def` -> `def`;
+    assign `def` to `d`
+<2> declare `int i`;
+    access `d` -> `def`;
+    implicit cast `def` to `double 1.0` -> `double 1.0`;
+    explicit cast `double 1.0` to `int 1` -> `int 1`;
+    assign `int 1` to `i`;
+    (note the explicit cast is necessary since a `double` value cannot be
+     converted to an `int` value implicitly)
+<3> assign `int 1` to `d`;
+    (note the switch in the type `d` represents from `double` to `int`)
+<4> declare `float i`;
+    access `d` -> `def`;
+    implicit cast `def` to `int 1` -> `int 1`;
+    implicit cast `int 1` to `float 1.0` -> `float 1.0`;
+    assign `float 1.0` to `f`
+<5> allocate `ArrayList` instance -> `ArrayList reference`;
+    assign `ArrayList reference` to `d`;
+    (note the switch in the type `d` represents from `int` to `ArrayList`)
+<6> declare `List l`;
+    access `d` -> `def`;
+    implicit cast `def` to `ArrayList reference` -> `ArrayList reference`;
+    implicit cast `ArrayList reference` to `List reference` -> `List reference`;
+    assign `List reference` to `l`
++
+* Invalid dynamic type casts resulting in errors.
++
+[source,Painless]
+----
+<1> def d = 1;
+<2> short s = d;       // error
+<3> d = new HashMap();
+<4> List l = d;        // error
+----
+<1> declare `def d`;
+    implicit cast `int 1` to `def` -> `def`;
+    assign `def` to `d`
+<2> declare `short s`;
+    access `d` -> `def`;
+    implicit cast `def` to `int 1` -> `int 1`;
+    *error* -> cannot implicit cast `int 1` to `short 1`;
+    (note an explicit cast is valid)
+<3> allocate `HashMap` instance -> `HashMap reference`;
+    implicit cast `HashMap reference` to `def` -> `def`;
+    assign `def` to `d`
+<4> declare `List l`;
+    access `d` -> `def`;
+    implicit cast `def` to `HashMap reference`;
+    *error* -> cannot implicit cast `HashMap reference` to `List reference`;
+    (note no cast would be valid since neither `HashMap` nor `List` is a
+            descendant of the other)
+
+[[string-character-casting]]
+==== String to Character Casting
+
+Use the *cast operator* to convert a <<string-type, `String` type>> value into a
+<<primitive-types, `char` type>> value.
+
+*Errors*
+
+* If the `String` type value isn't one character in length.
+* If the `String` type value is `null`.
+
+*Examples*
+
+* Casting string literals into `char` type values.
++
+[source,Painless]
+----
+<1> char c = (char)"C"
+<2> c = (char)'c'
+----
++
+<1> declare `char c`;
+    explicit cast `String "C"` to `char C` -> `char C`;
+    assign `char C` to `c`
+<2> explicit cast `String 'c'` to `char c` -> `char c`;
+    assign `char c` to `c`
++
+* Casting a `String` reference into a `char` value.
++
+[source,Painless]
+----
+<1> String s = "s";
+<2> char c = (char)s;
+----
+<1> declare `String s`;
+    assign `String "s"` to `s`;
+<2> declare `char c`
+    access `s` -> `String "s"`;
+    explicit cast `String "s"` to `char s` -> `char s`;
+    assign `char s` to `c`
 
 [[boxing-unboxing]]
 ==== Boxing and Unboxing
 
-Boxing is where a cast is used to convert a primitive type to its corresponding
-reference type. Unboxing is the reverse, converting a reference type to the
-corresponding primitive type.
+Boxing is a special type of cast used to convert a primitive type to its
+corresponding reference type. Unboxing is the reverse used to convert a
+reference type to its corresponding primitive type.
+
+Implicit boxing/unboxing occurs during the following operations:
+
+* Conversions between a `def` type and a primitive type will be implicitly
+  boxed/unboxed as necessary, though this is referred to as an implicit cast
+  throughout the documentation.
+* Method/function call arguments will be implicitly boxed/unboxed as necessary.
+* A primitive type value will be implicitly boxed when a reference type method
+  call is invoked on it.
 
-There are two places Painless performs implicit boxing and unboxing:
+Explicit boxing/unboxing is not allowed. Use the reference type API to
+explicitly convert a primitive type value to its respective reference type
+value and vice versa.
 
-* When you call methods, Painless automatically boxes and unboxes arguments
-so you can specify either primitive types or their corresponding reference
-types.
-* When you use the `def` type, Painless automatically boxes and unboxes as
-needed when converting to and from `def`.
+*Errors*
 
-The casting operator does not support any way to explicitly box a primitive
-type or unbox a reference type.
+* If an explicit cast is made to box/unbox a primitive type.
 
-If a primitive type needs to be converted to a reference type, the Painless
-reference type API supports methods that can do that. However, under normal
-circumstances this should not be necessary.
+*Examples*
 
-*Examples:*
-[source,Java]
+* Uses of implicit boxing/unboxing.
++
+[source,Painless]
 ----
-Integer x = 1;               // ERROR: not a legal implicit cast
-Integer y = (Integer)1;      // ERROR: not a legal explicit cast
-int a = new Integer(1);      // ERROR: not a legal implicit cast
-int b = (int)new Integer(1); // ERROR: not a legal explicit cast
+<1> List l = new ArrayList();
+<2> l.add(1);
+<3> Integer I = Integer.valueOf(0);
+<4> int i = l.get(i);
 ----
++
+<1> declare `List l`;
+    allocate `ArrayList` instance -> `ArrayList reference`;
+    assign `ArrayList reference` to `l`;
+<2> access `l` -> `List reference`;
+    implicit cast `int 1` to `def` -> `def`;
+    call `add` on `List reference` with arguments (`def`);
+    (note internally `int 1` is boxed to `Integer 1` to store as a `def` type
+            value)
+<3> declare `Integer I`;
+    call `valueOf` on `Integer` with arguments of (`int 0`) -> `Integer 0`;
+    assign `Integer 0` to `I`;
+<4> declare `int i`;
+    access `I` -> `Integer 0`;
+    unbox `Integer 0` -> `int 0`;
+    access `l` -> `List reference`;
+    call `get` on `List reference` with arguments (`int 0`) -> `def`;
+    implicit cast `def` to `int 1` -> `int 1`;
+    assign `int 1` to `i`;
+    (note internally `int 1` is unboxed from `Integer 1` when loaded from a
+            `def` type value)
++
+* Uses of invalid boxing/unboxing resulting in errors.
++
+[source,Painless]
+----
+<1> Integer x = 1;                   // error
+<2> Integer y = (Integer)1;          // error
+<3> int a = Integer.valueOf(1);      // error
+<4> int b = (int)Integer.valueOf(1); // error
+----
++
+<1> declare `Integer x`;
+    *error* -> cannot implicit box `int 1` to `Integer 1` during assignment
+<2> declare `Integer y`;
+    *error* -> cannot explicit box `int 1` to `Integer 1` during assignment
+<3> declare `int a`;
+    call `valueOf` on `Integer` with arguments of (`int 1`) -> `Integer 1`;
+    *error* -> cannot implicit unbox `Integer 1` to `int 1` during assignment
+<4> declare `int a`;
+    call `valueOf` on `Integer` with arguments of (`int 1`) -> `Integer 1`;
+    *error* -> cannot explicit unbox `Integer 1` to `int 1` during assignment
 
 [[promotion]]
 ==== Promotion
 
-Promotion is where certain operations require types to be either a minimum
-numerical type or for two (or more) types to be equivalent.
-The documentation for each operation that has these requirements
-includes promotion tables that describe how this is handled.
+Promotion is when a single value is implicitly cast to a certain type or
+multiple values are implicitly cast to the same type as required for evaluation
+by certain operations. Each operation that requires promotion has a promotion
+table that shows all required implicit casts based on the type(s) of value(s). A
+value can be promoted to a `def` type at compile-time; however, the promoted
+type value is derived from what the `def` type value represents at run-time.
 
-When an operation promotes a type or types, the resultant type
-of the operation is the promoted type.  Types can be promoted to def
-at compile-time; however, at run-time, the resultant type will be the
-promotion of the types the `def` is representing.
+*Errors*
 
-*Examples:*
-[source,Java]
-----
-2 + 2.0     // Add the literal int 2 and the literal double 2.0. The literal
-            //   2 is promoted to a double and the resulting value is a double.
+* If a specific operation cannot find an allowed promotion type for the type(s)
+  of value(s) given.
 
-def x = 1;  // Declare def variable x and set it to the literal int 1 through
-            //   an implicit cast
-x + 2.0F    // Add def variable x and the literal float 2.0.
-            // At compile-time the types are promoted to def.
-            // At run-time the types are promoted to float.
+*Examples*
+
+* Uses of promotion.
++
+[source,Painless]
+----
+<1> double d = 2 + 2.0;
+<2> def x = 1;
+<3> float f = x + 2.0F;
 ----
+<1> declare `double d`;
+    promote `int 2` and `double 2.0 @0` -> `double 2.0 @0`;
+    implicit cast `int 2` to `double 2.0 @1` -> `double 2.0 @1`;
+    add `double 2.0 @1` and `double 2.0 @0` -> `double 4.0`;
+    assign `double 4.0` to `d`
+<2> declare `def x`;
+    implicit cast `int 1` to `def` -> `def`;
+    assign `def` to `x`;
+<3> declare `float f`;
+    access `x` -> `def`;
+    implicit cast `def` to `int 1` -> `int 1`;
+    promote `int 1` and `float 2.0` -> `float 2.0`;
+    implicit cast `int 1` to `float 1.0` -> `float `1.0`;
+    add `float 1.0` and `float 2.0` -> `float 3.0`;
+    assign `float 3.0` to `f`;
+    (note this example illustrates promotion done at run-time as promotion
+            done at compile-time would have resolved to a `def` type value)

+ 6 - 6
docs/painless/painless-comments.asciidoc

@@ -1,12 +1,12 @@
 [[painless-comments]]
 === Comments
 
-Use the `//` token anywhere on a line to specify a single-line comment. All
-characters from the `//` token to the end of the line are ignored. Use an
-opening `/*` token and a closing `*/` token to specify a multi-line comment.
-Multi-line comments can start anywhere on a line, and all characters in between
-the `/*` token and `*/` token are ignored. Comments can be included anywhere
-within a script.
+Use a comment to annotate or explain code within a script. Use the `//` token
+anywhere on a line to specify a single-line comment. All characters from the
+`//` token to the end of the line are ignored. Use an opening `/*` token and a
+closing `*/` token to specify a multi-line comment. Multi-line comments can
+start anywhere on a line, and all characters in between the `/*` token and `*/`
+token are ignored. Comments can be included anywhere within a script.
 
 *Grammar*
 [source,ANTLR4]

+ 4 - 4
docs/painless/painless-identifiers.asciidoc

@@ -1,10 +1,10 @@
 [[painless-identifiers]]
 === Identifiers
 
-Specify identifiers to <<declaration, declare>>, <<assignment, assign>>, and
-<<painless-operators, use>> variables, <<dot-operator, access fields>>, and
-<<dot-operator, call methods>>. <<painless-keywords, Keywords>> and
-<<painless-types, types>> cannot be used as identifiers.
+Use an identifier as a named token to specify a
+<<painless-variables, variable>>, <<painless-types, type>>,
+<<dot-operator, field>>, <<dot-operator, method>>, or function.
+<<painless-keywords, Keywords>> cannot be used as identifiers.
 
 *Grammar*
 [source,ANTLR4]

+ 3 - 3
docs/painless/painless-keywords.asciidoc

@@ -1,9 +1,9 @@
 [[painless-keywords]]
 === Keywords
 
-The keywords in the table below are reserved for built-in language
-features. These keywords cannot be used as
-<<painless-identifiers, identifiers>> or <<painless-types, types>>.
+Keywords are reserved tokens for built-in language features and cannot be used
+as <<painless-identifiers, identifiers>> within a script. The following are
+keywords:
 
 [cols="^1,^1,^1,^1,^1"]
 |====

+ 1 - 1
docs/painless/painless-lang-spec.asciidoc

@@ -6,7 +6,7 @@ Painless syntax is similar to Java syntax along with some additional
 features such as dynamic typing, Map and List accessor shortcuts, and array
 initializers. As a direct comparison to Java, there are some important
 differences, especially related to the casting model. For more detailed
-conceptual information about the basic constructs that Java and Painless share,
+conceptual information about the basic constructs that Painless and Java share,
 refer to the corresponding topics in the
 https://docs.oracle.com/javase/specs/jls/se8/html/index.html[Java Language
 Specification].

+ 16 - 36
docs/painless/painless-literals.asciidoc

@@ -1,18 +1,19 @@
 [[painless-literals]]
 === Literals
 
-Use literals to specify different types of values directly in a script.
+Use a literal to specify a value directly in an
+<<painless-operators, operation>>.
 
 [[integers]]
 ==== Integers
 
-Use integer literals to specify an integer value in decimal, octal, or hex
-notation of the <<primitive-types, primitive types>> `int`, `long`, `float`,
+Use an integer literal to specify an integer type value in decimal, octal, or
+hex notation of a <<primitive-types, primitive type>> `int`, `long`, `float`,
 or `double`. Use the following single letter designations to specify the
-<<primitive-types, primitive type>>: `l` or `L` for `long`, `f` or `F` for
-`float`, and `d` or `D` for `double`. If not specified, the type defaults to
-`int`.  Use `0` as a prefix to specify an integer literal as octal, and use
-`0x` or `0X` as a prefix to specify an integer literal as hex.
+primitive type: `l` or `L` for `long`, `f` or `F` for `float`, and `d` or `D`
+for `double`. If not specified, the type defaults to `int`.  Use `0` as a prefix
+to specify an integer literal as octal, and use `0x` or `0X` as a prefix to
+specify an integer literal as hex.
 
 *Grammar*
 [source,ANTLR4]
@@ -46,11 +47,10 @@ HEX:     '-'? '0' [xX] [0-9a-fA-F]+ [lL]?;
 [[floats]]
 ==== Floats
 
-Use floating point literals to specify a floating point value of the
-<<primitive-types, primitive types>> `float` or `double`. Use the following
-single letter designations to specify the <<primitive-types, primitive type>>:
-`f` or `F` for `float` and `d` or `D` for `double`. If not specified, the type defaults
-to `double`.
+Use a floating point literal to specify a floating point type value of a
+<<primitive-types, primitive type>> `float` or `double`. Use the following
+single letter designations to specify the primitive type: `f` or `F` for `float`
+and `d` or `D` for `double`. If not specified, the type defaults to `double`.
 
 *Grammar*
 [source,ANTLR4]
@@ -81,7 +81,7 @@ EXPONENT: ( [eE] [+\-]? [0-9]+ );
 [[strings]]
 ==== Strings
 
-Use string literals to specify <<string-type, String>> values with
+Use a string literal to specify a <<string-type, `String` type>> value with
 either single-quotes or double-quotes. Use a `\"` token to include a
 double-quote as part of a double-quoted string literal. Use a `\'` token to
 include a single-quote as part of a single-quoted string literal.  Use a `\\`
@@ -117,26 +117,6 @@ STRING: ( '"'  ( '\\"'  | '\\\\' | ~[\\"] )*? '"'  )
 [[characters]]
 ==== Characters
 
-Use the <<painless-casting, casting operator>> to convert string literals or
-<<string-type, String>> values into <<primitive-types, char>> values.
-<<string-type, String>> values converted into
-<<primitive-types, char>> values must be exactly one character in length
-or an error will occur.
-
-*Examples*
-
-* Casting string literals into <<primitive-types, char>> values.
-+
-[source,Painless]
-----
-(char)"C"
-(char)'c'
-----
-+
-* Casting a <<string-type, String>> value into a <<primitive-types, char>> value.
-+
-[source,Painless]
-----
-String s = "s";
-char c = (char)s;
-----
+A character literal cannot be specified directly. Instead, use the
+<<string-character-casting, cast operator>> to convert a `String` type value
+into a `char` type value.

+ 45 - 2
docs/painless/painless-operators.asciidoc

@@ -240,6 +240,7 @@ operator.  See Function Calls [MARK] for more information.
 The brackets operator `[]` is used to create and access arrays, lists, and maps.
 The braces operator `{}` is used to intialize arrays.
 
+[[array-initialization]]
 ===== Creating and Initializing Arrays
 
 You create and initialize arrays using the brackets `[]` and braces `{}`
@@ -248,9 +249,49 @@ initialize each dimension with are specified as a comma-separated list enclosed
 in braces. For example, `new int[] {1, 2, 3}` creates a one dimensional `int`
 array with a size of 3 and the values 1, 2, and 3.
 
-For more information about allocating and initializing arrays, see <<array-type,
-Array Type>>.
+To allocate an array, you use the `new` keyword followed by the type and a
+set of brackets for each dimension. You can explicitly define the size of each dimension by specifying an expression within the brackets, or initialize each
+dimension with the desired number of values. The allocated size of each
+dimension is its permanent size.
 
+To initialize an array, specify the values you want to initialize
+each dimension with as a comma-separated list of expressions enclosed in braces.
+For example, `new int[] {1, 2, 3}` creates a one-dimensional `int` array with a
+size of 3 and the values 1, 2, and 3.
+
+When you initialize an array, the order of the expressions is maintained. Each expression used as part of the initialization is converted to the
+array's type. An error occurs if the types do not match.
+
+*Grammar:*
+[source,ANTLR4]
+----
+declare_array: TYPE ('[' ']')+;
+
+array_initialization: 'new' TYPE '[' ']' '{' expression (',' expression) '}'
+                    | 'new' TYPE '[' ']' '{' '}';
+----
+
+*Examples:*
+[source,Java]
+----
+int[] x = new int[5];          // Declare int array x and assign it a newly
+                               //   allocated int array with a size of 5
+def[][] y = new def[5][5];     // Declare the 2-dimensional def array y and
+                               //   assign it a newly allocated 2-dimensional
+                               //   array where both dimensions have a size of 5
+int[] x = new int[] {1, 2, 3}; // Declare int array x and set it to an int
+                               //   array with values 1, 2, 3 and a size of 3
+int i = 1;
+long l = 2L;
+float f = 3.0F;
+double d = 4.0;
+String s = "5";
+def[] da = new def[] {i, l, f*d, s}; // Declare def array da and set it to
+                                     // a def array with a size of 4 and the
+                                     // values i, l, f*d, and s
+----
+
+[[array-access]]
 ===== Accessing Array Elements
 
 Elements in an array are stored and accessed using the brackets `[]` operator.
@@ -298,6 +339,7 @@ return d[z];              // Access the 1st element of array d using the
 NOTE: The use of the `def` type in the second example means that the types
 cannot be resolved until runtime.
 
+[[array-length]]
 ===== Array Length
 
 Arrays contain a special member known as 'length' that is a read-only value that contains the size of the array.  This member can be accessed from an array using the dot operator.
@@ -727,6 +769,7 @@ def e;                   // declares the def variable e
 e = new HashMap(m);      // sets e to a newly allocated HashMap using the constructor with a single argument m
 ----
 
+[[new-array]]
 ==== New Array
 
 An array type instance can be allocated using the new operator. The format starts with the new operator followed by the type followed by a series of opening and closing braces each containing an expression for the size of the dimension.

+ 421 - 224
docs/painless/painless-types.asciidoc

@@ -1,269 +1,466 @@
 [[painless-types]]
 === Types
 
-Painless supports both dynamic and static types. Static types are split into
-_primitive types_ and _reference types_.
-
-[[dynamic-types]]
-==== Dynamic Types
-
-Painless supports one dynamic type: `def`.  The `def` type can represent any
-primitive or reference type. When you use the `def` type, it mimics the exact
-behavior of whatever type it represents at runtime. The default value for the
-def type is `null.`
-
-Internally, if the `def` type represents a primitive type, it is converted to the
-corresponding reference type. It still behaves like the primitive type, however,
-including within the casting model. The `def` type can be assigned to different
-types during the course of script execution.
-
-IMPORTANT: Because a `def` type variable can be assigned to different types
-during execution, type conversion errors that occur when using the `def` type
-happen at runtime.
-
-Using the `def` type can have a slight impact on performance. If performance is
-critical, it's better to declare static types.
-
-*Examples:*
-[source,Java]
-----
-def x = 1;               // Declare def variable x and set it to the
-                         //   literal int 1
-def l = new ArrayList(); // Declare def variable l and set it a newly 
-                         //   allocated ArrayList
-----
+A type is a classification of data used to define the properties of a value.
+These properties specify what data a value represents and the rules for how a
+value is evaluated during an <<painless-operators, operation>>. Each type
+belongs to one of the following categories: <<primitive-types, primitive>>,
+<<reference-types, reference>>, or <<dynamic-types, dynamic>>.
 
 [[primitive-types]]
 ==== Primitive Types
 
-Primitive types are allocated directly onto the stack according to the standard
-Java memory model.
-
-Primitive types can behave as their corresponding (<<boxing-unboxing, boxed>>)
-reference type. This means any piece of a reference type can be accessed or
-called through the primitive type. Operations performed in this manner convert
-the primitive type to its corresponding reference type at runtime and perform
-the field access  or method call without needing to perform any other
-operations.
-
-Painless supports the following primitive types.
-
-byte::
-An 8-bit, signed, two's complement integer.
-Range: [-128, 127].
-Default value: 0.
-Reference type: Byte.
-
-short:: 
-A 16-bit, signed, two's complement integer.
-Range: [-32768, 32767].
-Default value: 0.
-Reference type: Short.
-
-char::
-A 16-bit Unicode character.
-Range: [0, 65535].
-Default value: 0 or `\u0000`.
-Reference type: Character.
-
-int::
-A 32-bit, signed, two's complement integer.
-Range: [-2^32, 2^32-1].
-Default value: 0.
-Reference type: Integer.
-
-long::
-A 64-bit, signed, two's complement integer.
-Range: [-2^64, 2^64-1].
-Default value: 0.
-Reference type: Long.
-
-float::
-A 32-bit, single-precision, IEEE 754 floating point number.
-Range:  Depends on multiple factors.
-Default value: 0.0.
-Reference type: Float.
-
-double::
-A 64-bit, double-precision, IEEE 754 floating point number.
-Range: Depends on multiple factors.
-Default value: 0.0.
-Reference type: Double.
-
-boolean::
-A logical quanity with two possible values: true and false.
-Range: true/false.
-Default value: false.
-Reference type: Boolean.
-
-
-*Examples:*
-[source,Java]
+A primitive type represents basic data built natively into the JVM and is
+allocated to non-heap memory. Declare a primitive type
+<<painless-variables, variable>>, and assign it a primitive type value for
+evaluation during later operations. The default value for a newly-declared
+primitive type variable is listed as part of the definitions below. A primitive
+type value is copied during an assignment or as an argument for a
+method/function call.
+
+A primitive type has a corresponding reference type (also known as a boxed
+type). Use the <<field-access, field access operator>> or
+<<method-access, method call operator>> on a primitive type value to force
+evaluation as its corresponding reference type value.
+
+The following primitive types are available:
+
+[horizontal]
+`byte`::
+8-bit, signed, two's complement integer
+* range: [`-128`, `127`]
+* default value: `0`
+* reference type: `Byte`
+
+`short`::
+16-bit, signed, two's complement integer
+* range: [`-32768`, `32767`]
+* default value: `0`
+* reference type: `Short`
+
+`char`::
+16-bit, unsigned, Unicode character
+* range: [`0`, `65535`]
+* default value: `0` or `\u0000`
+* reference type: `Character`
+
+`int`::
+32-bit, signed, two's complement integer
+* range: [`-2^32`, `2^32-1`]
+* default value: `0`
+* reference type: `Integer`
+
+`long`::
+64-bit, signed, two's complement integer
+* range: [`-2^64`, `2^64-1`]
+* default value: `0`
+* reference type: `Long`
+
+`float`::
+32-bit, signed, single-precision, IEEE 754 floating point number
+* default value: `0.0`
+* reference type: `Float`
+
+`double`::
+64-bit, signed, double-precision, IEEE 754 floating point number
+* default value: `0.0`
+* reference type: `Double`
+
+`boolean`::
+logical quantity with two possible values of `true` and `false`
+* default value: `false`
+* reference type: `Boolean`
+
+*Examples*
+
+* Primitive types used in declaration, declaration and assignment.
++
+[source,Painless]
 ----
-int i = 1;        // Declare variable i as an int and set it to the
-                  //   literal 1
-double d;         // Declare variable d as a double and set it to the
-                  //   default value of 0.0
-boolean b = true; // Declare variable b as a boolean and set it to true
+<1> int i = 1;
+<2> double d;
+<3> boolean b = true;
 ----
-
-Using methods from the corresponding reference type on a primitive type.
-
-[source,Java]
++
+<1> declare `int i`;
+    assign `int 1` to `i`
+<2> declare `double d`;
+    assign default `double 0.0` to `d`
+<3> declare `boolean b`;
+    assign `boolean true` to `b`
++
+* Method call on a primitive type using the corresponding reference type.
++
+[source,Painless]
 ----
-int i = 1;    // Declare variable i as an int and set it to the
-              //   literal 1
-i.toString(); // Invokes the Integer method toString on variable i
+<1> int i = 1;
+<2> i.toString();
 ----
++
+<1> declare `int i`;
+    assign `int 1` to `i`
+<2> access `i` -> `int 1`;
+    box `int 1` -> `Integer 1 reference`;
+    call `toString` on `Integer 1 reference` -> `String '1'`
 
 [[reference-types]]
 ==== Reference Types
 
-Reference types are similar to Java classes and can contain multiple pieces
-known as _members_. However, reference types do not support access modifiers.
-You allocate reference type instances on the heap using the `new` operator.
-
-Reference types can have both static and non-static members:
-
-* Static members are shared by all instances of the same reference type and
-can be accessed without allocating an instance of the reference type. For
-example `Integer.MAX_VALUE`.
-* Non-static members are specific to an instance of the reference type
-and can only be accessed through the allocated instance.
-
-The default value for a reference type is `null`, indicating that no memory has
-been allocated for it. When you assign `null` to a reference type, its previous
-value is discarded and garbage collected in accordance with the Java memory
-model as long as there are no other references to that value.
-
-A reference type can contain:
-
-* Zero to many primitive types. Primitive type members can be static or
-non-static and read-only or read-write.
-* Zero to many reference types. Reference type members can be static or
-non-static and read-only or read-write.
-* Methods that call an internal function to return a value and/or manipulate
-the primitive or reference type members. Method members can be static or
-non-static.
-* Constructors that call an internal function to return a newly-allocated
-reference type instance. Constructors are non-static methods that can
-optionally manipulate the primitive and reference type members.
-
-Reference types support a Java-style inheritance model. Consider types A and B.
-Type A is considered to be a parent of B, and B a child of A, if B inherits
-(is able to access as its own) all of A's fields and methods. Type B is
+A reference type is a named construct (object), potentially representing
+multiple pieces of data (member fields) and logic to manipulate that data
+(member methods), defined as part of the application programming interface
+(API) for scripts.
+
+A reference type instance is a single set of data for one reference type
+object allocated to the heap. Use the
+<<constructor-call, new instance operator>> to allocate a reference type
+instance. Use a reference type instance to load from, store to, and manipulate
+complex data.
+
+A reference type value refers to a reference type instance, and multiple
+reference type values may refer to the same reference type instance. A change to
+a reference type instance will affect all reference type values referring to
+that specific instance.
+
+Declare a reference type <<painless-variables, variable>>, and assign it a
+reference type value for evaluation during later operations. The default value
+for a newly-declared reference type variable is `null`. A reference type value
+is shallow-copied during an assignment or as an argument for a method/function
+call. Assign `null` to a reference type variable to indicate the reference type
+value refers to no reference type instance. The JVM will garbage collect a
+reference type instance when it is no longer referred to by any reference type
+values. Pass `null` as an argument to a method/function call to indicate the
+argument refers to no reference type instance.
+
+A reference type object defines zero-to-many of each of the following:
+
+static member field::
+
+A static member field is a named and typed piece of data. Each reference type
+*object* contains one set of data representative of its static member fields.
+Use the <<field-access, field access operator>> in correspondence with the
+reference type object name to access a static member field for loading and
+storing to a specific reference type *object*. No reference type instance
+allocation is necessary to use a static member field.
+
+non-static member field::
+
+A non-static member field is a named and typed piece of data. Each reference
+type *instance* contains one set of data representative of its reference type
+object's non-static member fields. Use the
+<<field-access, field access operator>> for loading and storing to a non-static
+member field of a specific reference type *instance*. An allocated reference
+type instance is required to use a non-static member field.
+
+static member method::
+
+A static member method is a function called on a reference type *object*. Use
+the <<method-access, method call operator>> in correspondence with the reference
+type object name to call a static member method. No reference type instance
+allocation is necessary to use a static member method.
+
+non-static member method::
+
+A non-static member method is a function called on a reference type *instance*.
+A non-static member method called on a reference type instance can load from and
+store to non-static member fields of that specific reference type instance. Use
+the <<method-access, method call operator>> in correspondence with a specific
+reference type instance to call a non-static member method. An allocated
+reference type instance is required to use a non-static member method.
+
+constructor::
+
+A constructor is a special type of function used to allocate a reference type
+*instance* defined by a specific reference type *object*. Use the
+<<constructor-call, new instance operator>> to allocate a reference type
+instance.
+
+A reference type object follows a basic inheritance model. Consider types A and
+B. Type A is considered to be a parent of B, and B a child of A, if B inherits
+(is able to access as its own) all of A's non-static members. Type B is
 considered a descendant of A if there exists a recursive parent-child
 relationship from B to A with none to many types in between. In this case, B
-inherits all of A's fields and methods along with all of the fields and
-methods of the types in between. Type B is also considered to be a type A
-in both relationships.
-
-For the complete list of Painless reference types and their supported methods,
-see the https://www.elastic.co/guide/en/elasticsearch/reference/current/painless-api-reference.html[Painless API Reference].
+inherits all of A's non-static members along with all of the non-static members
+of the types in between. Type B is also considered to be a type A in both
+relationships.
 
-For more information about working with reference types, see
-<<field-access, Accessing Fields>> and <<method-access, Calling Methods>>.
+*Examples*
 
-*Examples:*
-[source,Java]
+* Reference types evaluated in several different operations.
++
+[source,Painless]
+----
+<1> List l = new ArrayList();
+<2> l.add(1);
+<3> int i = l.get(0) + 2;
+----
++
+<1> declare `List l`;
+    allocate `ArrayList` instance -> `ArrayList reference`;
+    implicit cast `ArrayList reference` to `List reference` -> `List reference`;
+    assign `List reference` to `l`
+<2> access `l` -> `List reference`;
+    implicit cast `int 1` to `def` -> `def`
+    call `add` on `List reference` with arguments (`def`)
+<3> declare `int i`;
+    access `l` -> `List reference`;
+    call `get` on `List reference` with arguments (`int 0`) -> `def`;
+    implicit cast `def` to `int 1` -> `int 1`;
+    add `int 1` and `int 2` -> `int 3`;
+    assign `int 3` to `i`
++
+* Sharing a reference type instance.
++
+[source,Painless]
 ----
-ArrayList al = new ArrayList();  // Declare variable al as an ArrayList and
-                                 //   set it to a newly allocated ArrayList
-List l = new ArrayList();        // Declare variable l as a List and set
-                                 //   it to a newly allocated ArrayList, which is
-                                 //   allowed because ArrayList inherits from List
-Map m;                           // Declare variable m as a Map and set it
-                                 //   to the default value of null
+<1> List l0 = new ArrayList();
+<2> List l1 = l0;
+<3> l0.add(1);
+<4> l1.add(2);
+<5> int i = l1.get(0) + l0.get(1);
 ----
++
+<1> declare `List l0`;
+    allocate `ArrayList` instance -> `ArrayList reference`;
+    implicit cast `ArrayList reference` to `List reference` -> `List reference`;
+    assign `List reference` to `l0`
+<2> declare `List l1`;
+    access `l0` -> `List reference`;
+    assign `List reference` to `l1`
+    (note `l0` and `l1` refer to the same instance known as a shallow-copy)
+<3> access `l0` -> `List reference`;
+    implicit cast `int 1` to `def` -> `def`
+    call `add` on `List reference` with arguments (`def`)
+<4> access `l1` -> `List reference`;
+    implicit cast `int 2` to `def` -> `def`
+    call `add` on `List reference` with arguments (`def`)
+<5> declare `int i`;
+    access `l0` -> `List reference`;
+    call `get` on `List reference` with arguments (`int 0`) -> `def @0`;
+    implicit cast `def @0` to `int 1` -> `int 1`;
+    access `l1` -> `List reference`;
+    call `get` on `List reference` with arguments (`int 1`) -> `def @1`;
+    implicit cast `def @1` to `int 2` -> `int 2`;
+    add `int 1` and `int 2` -> `int 3`;
+    assign `int 3` to `i`;
++
+* Using the static members of a reference type.
++
+[source,Painless]
+----
+<1> int i = Integer.MAX_VALUE;
+<2> long l = Long.parseLong("123L");
+----
++
+<1> declare `int i`;
+    access `MAX_VALUE` on `Integer` -> `int 2147483647`;
+    assign `int 2147483647` to `i`
+<2> declare `long l`;
+    call `parseLong` on `Long` with arguments (`long 123`) -> `long 123`;
+    assign `long 123` to `l`
+
+[[dynamic-types]]
+==== Dynamic Types
+
+A dynamic type value can represent the value of any primitive type or
+reference type using a single type name `def`. A `def` type value mimics
+the behavior of whatever value it represents at run-time and will always
+represent the child-most descendant type value of any type value when evaluated
+during operations.
+
+Declare a `def` type <<painless-variables, variable>>, and assign it
+any type of value for evaluation during later operations. The default value
+for a newly-declared `def` type variable is `null`.  A `def` type variable or
+method/function parameter can change the type it represents during the
+compilation and evaluation of a script.
+
+Using the `def` type can have a slight impact on performance. Use only primitive
+types and reference types directly when performance is critical.
+
+*Errors*
 
-Directly accessing static pieces of a reference type.
+* If a `def` type value represents an inappropriate type for evaluation of an
+  operation at run-time.
 
-[source,Java]
+*Examples*
+
+* General uses of the `def` type.
++
+[source,Painless]
+----
+<1> def dp = 1;
+<2> def dr = new ArrayList();
+<3> dr = dp;
+----
++
+<1> declare `def dp`;
+    implicit cast `int 1` to `def` -> `def`;
+    assign `def` to `dp`
+<2> declare `def dr`;
+    allocate `ArrayList` instance -> `ArrayList reference`;
+    implicit cast `ArrayList reference` to `def` -> `def`;
+    assign `def` to `dr`
+<3> access `dp` -> `def`;
+    assign `def` to `dr`;
+    (note the switch in the type `dr` represents from `ArrayList` to `int`)
++
+* A `def` type value representing the child-most descendant of a value.
++
+[source,Painless]
 ----
-Integer.MAX_VALUE      // a static field access
-Long.parseLong("123L") // a static function call
+<1> Object l = new ArrayList();
+<2> def d = l;
+<3> d.ensureCapacity(10);
 ----
++
+<1> declare `Object l`;
+    allocate `ArrayList` instance -> `ArrayList reference`;
+    implicit cast `ArrayList reference` to `Object reference`
+            -> `Object reference`;
+    assign `Object reference` to `l`
+<2> declare `def d`;
+    access `l` -> `Object reference`;
+    implicit cast `Object reference` to `def` -> `def`;
+    assign `def` to `d`;
+<3> access `d` -> `def`;
+    implicit cast `def` to `ArrayList reference` -> `ArrayList reference`;
+    call `ensureCapacity` on `ArrayList reference` with arguments (`int 10`);
+    (note `def` was implicit cast to `ArrayList reference`
+            since ArrayList` is the child-most descendant type value that the
+            `def` type value represents)
 
 [[string-type]]
 ==== String Type
 
-A `String` is a specialized reference type that is immutable and does not have
-to be explicitly allocated. You can directly assign to a `String` without first
-allocating it with the `new` keyword. (Strings can be allocated with the `new`
-keyword, but it's not required.)
+The `String` type is a specialized reference type that does not require
+explicit allocation. Use a <<strings, string literal>> to directly evaluate a
+`String` type value. While not required, the
+<<constructor-call, new instance operator>> can allocate `String` type
+instances.
 
-When assigning a value to a `String`, you must enclose the text in single or
-double quotes. Strings are allocated according to the standard Java Memory Model.
-The default value for a `String` is `null.`
+*Examples*
 
-*Examples:*
-[source,Java]
+* General use of the `String` type.
++
+[source,Painless]
 ----
-String r = "some text";             // Declare String r and set it to the
-                                    //   String "some text"
-String s = 'some text';             // Declare String s and set it to the
-                                    //   String 'some text'
-String t = new String("some text"); // Declare String t and set it to the
-                                    //   String "some text"
-String u;                           // Declare String u and set it to the
-                                    //   default value null
+<1> String r = "some text";
+<2> String s = 'some text';
+<3> String t = new String("some text");
+<4> String u;
 ----
++
+<1> declare `String r`;
+    assign `String "some text"` to `r`
+<2> declare `String s`;
+    assign `String 'some text'` to `s`
+<3> declare `String t`;
+    allocate `String` instance with arguments (`String "some text"`)
+            -> `String "some text"`;
+    assign `String "some text"` to `t`
+<4> declare `String u`;
+    assign default `null` to `u`
 
 [[void-type]]
 ==== void Type
 
-The `void` type represents the concept of no type. In Painless, `void` declares
-that a function has no return value.
+The `void` type represents the concept of a lack of type. Use the `void` type to
+indicate a function returns no value.
 
-[[array-type]]
-==== Array Type
+*Examples*
 
-Arrays contain a series of elements of the same type that can be allocated
-simultaneously. Painless supports both single and multi-dimensional arrays for
-all types except void (including `def`).
-
-You declare an array by specifying a type followed by a series of empty brackets,
-where each set of brackets represents a dimension. Declared arrays have a default
-value of `null` and are themselves a reference type.
-
-To allocate an array, you use the `new` keyword followed by the type and a
-set of brackets for each dimension. You can explicitly define the size of each dimension by specifying an expression within the brackets, or initialize each
-dimension with the desired number of values. The allocated size of each
-dimension is its permanent size.
-
-To initialize an array, specify the values you want to initialize
-each dimension with as a comma-separated list of expressions enclosed in braces.
-For example, `new int[] {1, 2, 3}` creates a one-dimensional `int` array with a
-size of 3 and the values 1, 2, and 3.
+* Use of the `void` type in a function.
++
+[source,Painless]
+----
+void addToList(List l, def d) {
+    l.add(d);
+}
+----
 
-When you initialize an array, the order of the expressions is maintained. Each expression used as part of the initialization is converted to the
-array's type. An error occurs if the types do not match.
+[[array-type]]
+==== Array Type
 
-*Grammar:*
-[source,ANTLR4]
+An array type is a specialized reference type where an array type instance
+represents a series of values allocated to the heap.  All values in an array
+type instance are of the same type.  Each value is assigned an index from within
+the range `[0, length)` where length is the total number of values allocated for
+the array type instance.
+
+Use the <<new-array, new array operator>> or the
+<<array-initialization, array initialization operator>> to allocate an array
+type instance. Declare an array type <<painless-variables, variable>>, and
+assign it an array type value for evaluation during later operations. The
+default value for a newly-declared array type variable is `null`. An array type
+value is shallow-copied during an assignment or as an argument for a
+method/function call. Assign `null` to an array type variable to indicate the
+array type value refers to no array type instance. The JVM will garbage collect
+an array type instance when it is no longer referred to by any array type
+values. Pass `null` as an argument to a method/function call to indicate the
+argument refers to no array type instance.
+
+Use the <<array-length, array length operator>> to retrieve the length of an
+array type value as an int type value. Use the
+<<array-access, array access operator>> to load from and store to individual
+values within an array type value.
+
+When an array type instance is allocated with multiple dimensions using the
+range `[2, d]` where `d >= 2`, each dimension in the range `[1, d-1]` is also
+an array type. The array type of each dimension, `n`, is an array type with the
+number of dimensions equal to `d-n`. For example, consider `int[][][]` with 3
+dimensions. The 3rd dimension, `d-3`, is the primitive type `int`.  The 2nd
+dimension, `d-2`, is the array type `int[]`. And the 1st dimension, `d-1` is
+the array type `int[][]`.
+
+*Examples*
+
+* General use of single-dimensional arrays.
++
+[source,Painless]
 ----
-declare_array: TYPE ('[' ']')+;
-
-array_initialization: 'new' TYPE '[' ']' '{' expression (',' expression) '}'
-                    | 'new' TYPE '[' ']' '{' '}';
+<1> int[] x;
+<2> float[] y = new float[10];
+<3> def z = new float[5];
+<4> y[9] = 1.0F;
+<5> z[0] = y[9];
 ----
-
-*Examples:*
-[source,Java]
++
+<1> declare `int[] x`;
+    assign default `null` to `x`
+<2> declare `float[] y`;
+    allocate `1-d float array` instance with `length [10]`
+            -> `1-d float array reference`;
+    assign `1-d float array reference` to `y`
+<3> declare `def z`;
+    allocate `1-d float array` instance with `length [5]`
+            -> `1-d float array reference`;
+    implicit cast `1-d float array reference` to `def` -> `def`;
+    assign `def` to `z`
+<4> access `y` -> `1-d float array reference`;
+    assign `float 1.0` to `index [9]` of `1-d float array reference`
+<5> access `y` -> `1-d float array reference @0`;
+    access `index [9]` of `1-d float array reference @0` -> `float 1.0`;
+    access `z` -> `def`;
+    implicit cast `def` to `1-d float array reference @1`
+            -> `1-d float array reference @1`;
+    assign `float 1.0` to `index [0]` of `1-d float array reference @1`
++
+* Use of a multi-dimensional array.
++
+[source,Painless]
 ----
-int[] x = new int[5];          // Declare int array x and assign it a newly
-                               //   allocated int array with a size of 5
-def[][] y = new def[5][5];     // Declare the 2-dimensional def array y and
-                               //   assign it a newly allocated 2-dimensional
-                               //   array where both dimensions have a size of 5
-int[] x = new int[] {1, 2, 3}; // Declare int array x and set it to an int
-                               //   array with values 1, 2, 3 and a size of 3
-int i = 1;
-long l = 2L;
-float f = 3.0F;
-double d = 4.0;
-String s = "5";
-def[] da = new def[] {i, l, f*d, s}; // Declare def array da and set it to
-                                     // a def array with a size of 4 and the
-                                     // values i, l, f*d, and s
+<1> int[][][] ia3 = new int[2][3][4];
+<2> ia3[1][2][3] = 99;
+<3> int i = ia3[1][2][3];
 ----
++
+<1> declare `int[][][] ia`;
+    allocate `3-d int array` instance with length `[2, 3, 4]`
+            -> `3-d int array reference`;
+    assign `3-d int array reference` to `ia3`
+<2> access `ia3` -> `3-d int array reference`;
+    assign `int 99` to `index [1, 2, 3]` of `3-d int array reference`
+<3> declare `int i`;
+    access `ia3` -> `3-d int array reference`;
+    access `index [1, 2, 3]` of `3-d int array reference` -> `int 99`;
+    assign `int 99` to `i`

+ 119 - 50
docs/painless/painless-variables.asciidoc

@@ -1,29 +1,31 @@
 [[painless-variables]]
 === Variables
 
-<<declaration, Declare>> variables to <<assignment, assign>> values for
-<<painless-operators, use>> in expressions.  Specify variables as a
-<<primitive-types, primitive type>>, <<reference-types, reference type>>, or
-<<dynamic-types, dynamic type>>.  Variable operations follow the structure of a
-standard JVM in relation to instruction execution and memory usage.
+A variable loads and stores a value for evaluation during
+<<painless-operators, operations>>.
 
 [[declaration]]
 ==== Declaration
 
-Declare variables before use with the format of <<painless-types, type>>
-<<painless-identifiers, identifier>>. Specify a comma-separated list of
-<<painless-identifiers, identifiers>> following the <<painless-types, type>>
-to declare multiple variables in a single statement.  Use an
-<<assignment, assignment>> statement combined with a declaration statement to
-immediately assign a value to a variable. Variables not immediately assigned a
-value will have a default value assigned implicitly based on the
-<<painless-types, type>>.
+Declare a variable before use with the format of <<painless-types, type>>
+followed by <<painless-identifiers, identifier>>. Declare an
+<<array-type, array type>> variable using an opening `[` token and a closing `]`
+token for each dimension directly after the identifier. Specify a
+comma-separated list of identifiers following the type to declare multiple
+variables in a single statement. Use an <<assignment, assignment operator>>
+combined with a declaration to immediately assign a value to a variable.
+A variable not immediately assigned a value will have a default value assigned
+implicitly based on the type.
+
+*Errors*
+
+* If a variable is used prior to or without declaration.
 
 *Grammar*
 [source,ANTLR4]
 ----
 declaration : type ID assignment? (',' ID assignment?)*;
-type: ID ('[' ']')*;
+type: ID ('.' ID)* ('[' ']')*;
 assignment: '=' expression;
 ----
 
@@ -35,27 +37,43 @@ assignment: '=' expression;
 ----
 <1> int x;
 <2> List y;
-<3> int x, y, z;
-<4> def[] d;
+<3> int x, y = 5, z;
+<4> def d;
 <5> int i = 10;
-----
-+
-<1> declare a variable of type `int` and identifier `x`
-<2> declare a variable of type `List` and identifier `y`
-<3> declare three variables of type `int` and identifiers `x`, `y`, `z`
-<4> declare a variable of type `def[]` and identifier `d`
-<5> declare a variable of type `int` and identifier `i`;
-    assign the integer literal `10` to `i`
+<6> float[] f;
+<7> Map[][] m;
+----
++
+<1> declare `int x`;
+    assign default `null` to `x`
+<2> declare `List y`;
+    assign default `null` to `y`
+<3> declare `int x`;
+    assign default `int 0` to `x`;
+    declare `int y`;
+    assign `int 5` to `y`;
+    declare `int z`;
+    assign default `int 0` to `z`;
+<4> declare `def d`;
+    assign default `null` to `d`
+<5> declare `int i`;
+    assign `int 10` to `i`
+<6> declare `float[] f`;
+    assign default `null` to `f`
+<7> declare `Map[][] m`;
+    assign default `null` to `m`
 
 [[assignment]]
 ==== Assignment
 
-Use the `equals` operator (`=`) to assign a value to a variable. Any expression
+Use the *assignment operator* to store a value in a variable. Any operation
 that produces a value can be assigned to any variable as long as the
-<<painless-types, types>> are the same or the resultant
-<<painless-types, type>> can be implicitly <<painless-casting, cast>> to
-the variable <<painless-types, type>>.  Otherwise, an error will occur.
-<<reference-types, Reference type>> values are shallow-copied when assigned.
+<<painless-types, types>> are the same or the resultant type can be
+<<painless-casting, implicitly cast>> to the variable type.
+
+*Errors*
+
+* If the type of value is unable to match the type of variable.
 
 *Grammar*
 [source,ANTLR4]
@@ -65,7 +83,7 @@ assignment: ID '=' expression
 
 *Examples*
 
-* Variable assignment with an <<integers, integer literal>>.
+* Variable assignment with an integer literal.
 +
 [source,Painless]
 ----
@@ -73,10 +91,11 @@ assignment: ID '=' expression
 <2> i = 10;
 ----
 +
-<1> declare `int i`
-<2> assign `10` to `i`
+<1> declare `int i`;
+    assign default `int 0` to `i`
+<2> assign `int 10` to `i`
 +
-* <<declaration, Declaration>> combined with immediate variable assignment.
+* Declaration combined with immediate assignment.
 +
 [source,Painless]
 ----
@@ -84,11 +103,12 @@ assignment: ID '=' expression
 <2> double j = 2.0;
 ----
 +
-<1> declare `int i`; assign `10` to `i`
-<2> declare `double j`; assign `2.0` to `j`
+<1> declare `int i`;
+    assign `int 10` to `i`
+<2> declare `double j`;
+    assign `double 2.0` to `j`
 +
-* Assignment of one variable to another using
-<<primitive-types, primitive types>>.
+* Assignment of one variable to another using primitive types.
 +
 [source,Painless]
 ----
@@ -96,11 +116,13 @@ assignment: ID '=' expression
 <2> int j = i;
 ----
 +
-<1> declare `int i`; assign `10` to `i`
-<2> declare `int j`; assign `j` to `i`
+<1> declare `int i`;
+    assign `int 10` to `i`
+<2> declare `int j`;
+    access `i` -> `int 10`;
+    assign `int 10` to `j`
 +
-* Assignment with <<reference-types, reference types>> using the
-<<constructor-call, new operator>>.
+* Assignment with reference types using the *new instance operator*.
 +
 [source,Painless]
 ----
@@ -108,12 +130,15 @@ assignment: ID '=' expression
 <2> Map m = new HashMap();
 ----
 +
-<1> declare `ArrayList l`; assign a newly-allocated `Arraylist` to `l`
-<2> declare `Map m`; assign a newly-allocated `HashMap` to `m`
-    with an implicit cast to `Map`
+<1> declare `ArrayList l`;
+    allocate `ArrayList` instance -> `ArrayList reference`;
+    assign `ArrayList reference` to `l`
+<2> declare `Map m`;
+    allocate `HashMap` instance -> `HashMap reference`;
+    implicit cast `HashMap reference` to `Map reference` -> `Map reference`;
+    assign `Map reference` to `m`
 +
-* Assignment of one variable to another using
-<<reference-types, reference types>>.
+* Assignment of one variable to another using reference types.
 +
 [source,Painless]
 ----
@@ -123,8 +148,52 @@ assignment: ID '=' expression
 <4> m = k;
 ----
 +
-<1> declare `List l`; assign a newly-allocated `Arraylist` to `l`
-    with an implicit cast to `List`
-<2> declare `List k`; assign a shallow-copy of `l` to `k`
+<1> declare `List l`;
+    allocate `ArrayList` instance -> `ArrayList reference`;
+    implicit cast `ArrayList reference` to `List reference` -> `List reference`;
+    assign `List reference` to `l`
+<2> declare `List k`;
+    access `l` -> `List reference`;
+    assign `List reference` to `k`;
+    (note `l` and `k` refer to the same instance known as a shallow-copy)
 <3> declare `List m`;
-<4> assign a shallow-copy of `k` to `m`
+    assign default `null` to `m`
+<4> access `k` -> `List reference`;
+    assign `List reference` to `m`;
+    (note `l`, `k`, and `m` refer to the same instance)
++
+* Assignment with an array type variable using the *new array operator*.
++
+[source,Painless]
+----
+<1> int[] ia1;
+<2> ia1 = new int[2];
+<3> ia1[0] = 1;
+<4> int[] ib1 = ia1;
+<5> int[][] ic2 = new int[2][5];
+<6> ic2[1][3] = 2;
+<7> ic2[0] = ia1;
+----
++
+<1> declare `int[] ia1`;
+    assign default `null` to `ia1`
+<2> allocate `1-d int array` instance with `length [2]`
+            -> `1-d int array reference`;
+    assign `1-d int array reference` to `ia1`
+<3> access `ia1` -> `1-d int array reference`;
+    assign `int 1` to `index [0]` of `1-d int array reference`
+<4> declare `int[] ib1`;
+    access `ia1` -> `1-d int array reference`;
+    assign `1-d int array reference` to `ib1`;
+    (note `ia1` and `ib1` refer to the same instance known as a shallow copy)
+<5> declare `int[][] ic2`;
+    allocate `2-d int array` instance with `length [2, 5]`
+            -> `2-d int array reference`;
+    assign `2-d int array reference` to `ic2`
+<6> access `ic2` -> `2-d int array reference`;
+    assign `int 2` to `index [1, 3]` of `2-d int array reference`
+<7> access `ia1` -> `1-d int array reference`;
+    access `ic2` -> `2-d int array reference`;
+    assign `1-d int array reference` to
+            `index [0]` of `2-d int array reference`;
+    (note `ia1`, `ib1`, and `index [0]` of `ia2` refer to the same instance)