diff --git a/docs/painless/painless-casting.asciidoc b/docs/painless/painless-casting.asciidoc index ec4f9919bd0..a3624f90831 100644 --- a/docs/painless/painless-casting.asciidoc +++ b/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 <>. 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` -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. +[[numeric-type-casting]] +==== Numeric Type Casting + +A <> 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* -Example(s) -[source,Java] +* Valid numeric type casts. ++ +[source,Painless] ---- -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. +<1> int a = 1; +<2> long b = a; +<3> short c = (short)b; +<4> double e = (double)a; ---- - -[[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] ++ +<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] ---- -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> int a = 1.0; // error +<2> int b = 2; +<3> byte c = b; // error ---- -[1] `ArrayList` is a descendant of the `List` type. -[2] `Map` is unrelated to the `List` and `ArrayList` types. ++ +<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) -[[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. +[[reference-type-casting]] +==== Reference Type Casting -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. +A <> 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* -*Examples:* -[source,Java] +* Valid reference type casts. ++ +[source,Painless] ---- -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 +<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 <> 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* + +* 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 <> value into a +<> 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. -There are two places Painless performs implicit boxing and unboxing: +Implicit boxing/unboxing occurs during the following operations: -* 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`. +* 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. -The casting operator does not support any way to explicitly box a primitive -type or unbox a reference type. +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. -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. +*Errors* -*Examples:* -[source,Java] +* If an explicit cast is made to box/unbox a primitive type. + +*Examples* + +* 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] +* If a specific operation cannot find an allowed promotion type for the type(s) + of value(s) given. + +*Examples* + +* Uses of promotion. ++ +[source,Painless] ---- -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. - -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. +<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) diff --git a/docs/painless/painless-comments.asciidoc b/docs/painless/painless-comments.asciidoc index 588e464d97f..bde30e37e04 100644 --- a/docs/painless/painless-comments.asciidoc +++ b/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] diff --git a/docs/painless/painless-identifiers.asciidoc b/docs/painless/painless-identifiers.asciidoc index 17073e3d4c4..7762f56cb7b 100644 --- a/docs/painless/painless-identifiers.asciidoc +++ b/docs/painless/painless-identifiers.asciidoc @@ -1,10 +1,10 @@ [[painless-identifiers]] === Identifiers -Specify identifiers to <>, <>, and -<> variables, <>, and -<>. <> and -<> cannot be used as identifiers. +Use an identifier as a named token to specify a +<>, <>, +<>, <>, or function. +<> cannot be used as identifiers. *Grammar* [source,ANTLR4] diff --git a/docs/painless/painless-keywords.asciidoc b/docs/painless/painless-keywords.asciidoc index cb3bafbd20f..39a2201fd2b 100644 --- a/docs/painless/painless-keywords.asciidoc +++ b/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 -<> or <>. +Keywords are reserved tokens for built-in language features and cannot be used +as <> within a script. The following are +keywords: [cols="^1,^1,^1,^1,^1"] |==== diff --git a/docs/painless/painless-lang-spec.asciidoc b/docs/painless/painless-lang-spec.asciidoc index ba6595000ae..5e6b84d8c57 100644 --- a/docs/painless/painless-lang-spec.asciidoc +++ b/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]. diff --git a/docs/painless/painless-literals.asciidoc b/docs/painless/painless-literals.asciidoc index 441cb264f1e..ebf7eaa07b6 100644 --- a/docs/painless/painless-literals.asciidoc +++ b/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 +<>. [[integers]] ==== Integers -Use integer literals to specify an integer value in decimal, octal, or hex -notation of the <> `int`, `long`, `float`, +Use an integer literal to specify an integer type value in decimal, octal, or +hex notation of a <> `int`, `long`, `float`, or `double`. Use the following single letter designations to specify the -<>: `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 -<> `float` or `double`. Use the following -single letter designations to specify the <>: -`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 +<> `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 <> values with +Use a string literal to specify a <> 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 <> to convert string literals or -<> values into <> values. -<> values converted into -<> values must be exactly one character in length -or an error will occur. - -*Examples* - -* Casting string literals into <> values. -+ -[source,Painless] ----- -(char)"C" -(char)'c' ----- -+ -* Casting a <> value into a <> value. -+ -[source,Painless] ----- -String s = "s"; -char c = (char)s; ----- +A character literal cannot be specified directly. Instead, use the +<> to convert a `String` type value +into a `char` type value. diff --git a/docs/painless/painless-operators.asciidoc b/docs/painless/painless-operators.asciidoc index 915d811fa44..8329686f663 100644 --- a/docs/painless/painless-operators.asciidoc +++ b/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 <>. +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. diff --git a/docs/painless/painless-types.asciidoc b/docs/painless/painless-types.asciidoc index 9d575a2069a..a897b8e8a04 100644 --- a/docs/painless/painless-types.asciidoc +++ b/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 <>. Each type +belongs to one of the following categories: <>, +<>, or <>. [[primitive-types]] ==== Primitive Types -Primitive types are allocated directly onto the stack according to the standard -Java memory model. +A primitive type represents basic data built natively into the JVM and is +allocated to non-heap memory. Declare a primitive type +<>, 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. -Primitive types can behave as their corresponding (<>) -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. +A primitive type has a corresponding reference type (also known as a boxed +type). Use the <> or +<> on a primitive type value to force +evaluation as its corresponding reference type value. -Painless supports the following primitive types. +The following primitive types are available: -byte:: -An 8-bit, signed, two's complement integer. -Range: [-128, 127]. -Default value: 0. -Reference type: Byte. +[horizontal] +`byte`:: +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. +`short`:: +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. +`char`:: +16-bit, unsigned, 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. +`int`:: +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. +`long`:: +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. +`float`:: +32-bit, signed, single-precision, IEEE 754 floating point number +* 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. +`double`:: +64-bit, signed, double-precision, IEEE 754 floating point number +* 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. +`boolean`:: +logical quantity with two possible values of `true` and `false` +* default value: `false` +* reference type: `Boolean` +*Examples* -*Examples:* -[source,Java] +* 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. +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. -Reference types can have both static and non-static members: +A reference type instance is a single set of data for one reference type +object allocated to the heap. Use the +<> to allocate a reference type +instance. Use a reference type instance to load from, store to, and manipulate +complex data. -* 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. +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. -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. +Declare a reference type <>, 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 can contain: +A reference type object defines zero-to-many of each of the following: -* 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. +static member field:: -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 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 <> 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 +<> 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 <> 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 <> 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 +<> 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. +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 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]. +*Examples* -For more information about working with reference types, see -<> and <>. - -*Examples:* -[source,Java] +* Reference types evaluated in several different operations. ++ +[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 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] +---- +<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` -Directly accessing static pieces of a reference type. +[[dynamic-types]] +==== Dynamic Types -[source,Java] +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 <>, 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* + +* If a `def` type value represents an inappropriate type for evaluation of an + operation at run-time. + +*Examples* + +* General uses of the `def` type. ++ +[source,Painless] ---- -Integer.MAX_VALUE // a static field access -Long.parseLong("123L") // a static function call +<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] +---- +<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 <> to directly evaluate a +`String` type value. While not required, the +<> 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. + +*Examples* + +* Use of the `void` type in a function. ++ +[source,Painless] +---- +void addToList(List l, def d) { + l.add(d); +} +---- [[array-type]] ==== Array Type -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`). +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. -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. +Use the <> or the +<> to allocate an array +type instance. Declare an array type <>, 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. -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. +Use the <> to retrieve the length of an +array type value as an int type value. Use the +<> to load from and store to individual +values within an array type value. -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 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[][]`. -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. +*Examples* -*Grammar:* -[source,ANTLR4] +* 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` diff --git a/docs/painless/painless-variables.asciidoc b/docs/painless/painless-variables.asciidoc index 9756676a08b..8b8782b1511 100644 --- a/docs/painless/painless-variables.asciidoc +++ b/docs/painless/painless-variables.asciidoc @@ -1,29 +1,31 @@ [[painless-variables]] === Variables -<> variables to <> values for -<> in expressions. Specify variables as a -<>, <>, or -<>. 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 +<>. [[declaration]] ==== Declaration -Declare variables before use with the format of <> -<>. Specify a comma-separated list of -<> following the <> -to declare multiple variables in a single statement. Use an -<> 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 -<>. +Declare a variable before use with the format of <> +followed by <>. Declare an +<> 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 <> +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; +<6> float[] f; +<7> Map[][] m; ---- + -<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` +<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 -<> are the same or the resultant -<> can be implicitly <> to -the variable <>. Otherwise, an error will occur. -<> values are shallow-copied when assigned. +<> are the same or the resultant type can be +<> 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 <>. +* 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` + -* <> 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 -<>. +* 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 <> using the -<>. +* 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 -<>. +* 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)