diff --git a/docs/arcaflow/workflows/expressions.md b/docs/arcaflow/workflows/expressions.md index 5e3aa255..c21d8d78 100644 --- a/docs/arcaflow/workflows/expressions.md +++ b/docs/arcaflow/workflows/expressions.md @@ -15,16 +15,18 @@ This page explains the language elements of expressions. Literals represent values described in an expression, as opposed to values referenced from other sources. -### String +### String values -string literals start and end with a matched pair of either single quotes `'` or double quotes `"`, and have zero or more characters between the quotes. +Normal string literals start and end with a matched pair of either single quotes `'` or double quotes `"` and have zero or more characters between the quotes. Strings may have escaped values. The most important ones are for backslashes (`\\` for `\`) or for embedded newlines `\n`. +You do not need to escape double quotes in a single-quote string, or single-quotes in a double quote string. However, + Here is the list of supported escape characters: | Escape | Result | | ------ | ------ | -| `\\` | `\` backslash character) | +| `\\` | `\` backslash character | | `\t` | tab character | | `\n` | newline character | | `\r` | carriage return character | @@ -39,7 +41,51 @@ For example, to have the following text represented in a single string: You would need the expression `"test\ntest2/\\"` -### Integer +#### String-expressions in YAML + +If an expression has a string literal in it, then you must account for it in the YAML. + +For example, to include a double-quoted string in an expression, you must either add single quotes around expression, or use block flow scalars. A single apostrophe within an expression needs to be added twice in a row to count as one while in an inlined string. + +Here is an example of the following value represented in a few of the various ways: +> just a string with sub-quotes "hello" and an apostrophe ' + +Inlined: +``` +some_value: !expr '"just a string with sub-quotes \"hello\" and an apostrophe \'' "' +``` + +With Block Flow Scalar: +``` +some_value_1: !expr |- + 'just a string with sub-quotes "hello" and an apostrophe \' ' +some_value_2: !expr |- + "just a string with sub-quotes \"hello\" and an apostrophe ' " +``` + +See [Raw String](#raw-string) to see how to do this without escaping. + +### Raw String values + +Raw string literals start and end with back-tick characters "`". + +The main point of a raw string is that it does not escape characters. This means that you can put `'` and `"` characters in withine escaping them. + +Here is an example of the following value represented using raw strings: +> just a string with sub-quotes "hello" and an apostrophe ' + +Inlined: +``` +some_value: !expr '`just a string with sub-quotes "hello" and an apostrophe \'' `' +``` + +With Block Flow Scalar: +``` +some_value: !expr |- + `just a string with sub-quotes "hello" and an apostrophe ' ` +``` + +### Integer numbers Integers are whole non-negative base-10 numbers. They may not start with `0`, unless the value is `0`. For example, `001` is not a valid integer literal. @@ -48,21 +94,21 @@ Examples: - `1` - `503` -Integer literals can be a part of an expression that can be made negative by prefixing them with the [negation operator `-`](#negation). +Negative values are constructed by applying the [negation operator `-`](#negation) to a literal numeric value. ### Floating point numbers -Float literals are non-negative floating point double precision decimal numbers. +Floating point literals are non-negative double-precision floating point numbers. Supported formats include: - number characters followed by a period followed by zero or more number characters: `1.1` or `1.` - base-10 exponential scientific notation formats like `5.0e5` and `5.0E-5` -Float literals can be a part of an expression that can be made negative by prefixing them with the [negation operator `-`](#negation). +Negative values are constructed by applying the [negation operator `-`](#negation) to a literal numeric value. -### Boolean +### Boolean values -boolean literals have two valid values: +Boolean literals have two valid values: - `true` - `false` @@ -99,8 +145,7 @@ The bracket accessor is used for referencing values in maps or lists. #### List access -For list access, you specify the index of the value you want to access. -Lists are zero-indexed (so the first value has an index of 0). +For list access, you specify the index of the value you want to access. The index should be an expression yielding a non-negative integer value, where zero corresponds to the first value in the list. If you have a list named `foo`: @@ -122,7 +167,7 @@ Maps, also known as dictionaries in some languages, are key-value pair data stru To use a map in an expression, the expression to the left of the brackets must be a reference to a map. That is then followed by a pair of brackets with a sub-expression between them. That sub-expression must evaluate to a valid key in the map. -Here is an example of a map whose key are strings, and whose values are integers. The map is stored in a field called `foo` in the root-level object: +Here is an example of a map with string keys and integer values. The map is stored in a field called `foo` in the root-level object: ```yml foo: @@ -142,7 +187,23 @@ Functions are built-in tasks with pre-defined behavior and a known input and out Functions are defined by the engine. Functions: -TO BE DEFINED BEFORE MERGE. +| function definition | return type | description | +| ---------------------------- | ----------- | ---------------------------------------------------- | +| `intToFloat(integer)` | float | Converts an integer type into a floating point type. | +| `floatToInt(float)` | integer | Converts a float type into an integer type by discarding the fraction. In other words, it is rounded to the nearest integer towards zero.
For example, `5.5` becomes `5`, and `-1.9` becomes `-1`" | +| `intToString(integer)` | string | Converts an integer to a string whose characters represent that integer in base-10.
For example, an input of `55` will output `"55"` | +| `floatToString(float)` | string | Converts a floating point number to a string whose characters represent that number in base-10 as as simple decimal.
For example, an input of `5000.5` will output `"5000.5"` | +| `boolToString(boolean)` | string | Returns `"true"` for `true`, and `"false"` for `false`. | +| `stringToInt(string)` | integer | Interprets the string as a base-10 integer. Will fail if the input is not a valid integer. | +| `stringToFloat(string)` | float | Converts the input string to a 64-bit floating-point number

Accepts decimal and hexadecimal floating-point numbers as defined by the [Go syntax for floating point literals](https://go.dev/ref/spec#Floating-point_literals).
If the input is well-formed and near a valid floating-point number, stringToFloat returns the nearest floating-point number rounded using IEEE754 unbiased rounding.
Returns an error when an invalid input is received. | +| `stringToBool(string)` | boolean | Interprets the input as a boolean.
Case insensitively accepts `"1"`, `"t"`, and `"true"` for `true`.\n
Case insensitively accepts `"0"`, '"f"', and '"false"' for `false`.
Returns an error for any other input. | +| `ceil(float)` | float | Returns the least integer value greater than or equal to the input.
For example `ceil(1.5)` outputs `2.0`, and `ceil(-1.5)` outputs `-1.0`
Special cases are:
  ceil(±0) = ±0
  ceil(±Inf) = ±Inf
  ceil(NaN) = NaN" | +| `floor(float)` | float | Returns the greatest integer value less than or equal to the input.
For example `floor(1.5)` outputs `1.0`, and `floor(-1.5)` outputs `-2.0`
Special cases are:
  floor(±0) = ±0
  floor(±Inf) = ±Inf
  floor(NaN) = NaN | +| `round(float)` | float | Returns the nearest integer to the input, rounding half away from zero.
For example `round(1.5)` outputs `2.0`, and `round(-1.5)` outputs `-2.0`
Special cases are:
  round(±0) = ±0
  round(±Inf) = ±Inf
  round(NaN) = NaN" | +| `abs(float)` | float | Returns the absolute value of x.
Special cases are:
  abs(±Inf) = +Inf
  abs(NaN) = NaN | +| `toLower(string)` | string | Outputs the input with Unicode letters mapped to their lower case. | +| `toUpper(string)` | string | Outputs the input with Unicode letters mapped to their upper case. | +| `splitString(string, string)`| list[string]| Splits the given string with the given separator.
  Param 1: The string to split.
  Param 2: The separator." | The syntax for a function has multiple parts. First, you have the function's identifying name, followed by `(`, followed by 0 or more comma-separated expressions, followed by `)`. @@ -156,6 +217,8 @@ thisIsAFunction("this is a string literal for the first parameter", $.a.b) Binary Operations have an expression to the left and right, with an operator in between. The order of operations determines which operators run first. See [Order of Operations](#order-of-operations) +The left and right types **must** match. To convert types, see the list of [available functions](#functions). + | Operator| Description | | --------|--------------------| | `+` | [Addition/Concatenation](#additionconcatenation) | @@ -191,7 +254,7 @@ For example, the expression `2 + 2` would output the integer `4`. ### Subtraction -When the `-` operator is used with a numerical operands, subtracts the value of the right operand from the value of the left. The operator requires numerical operands with the same type. You cannot mix float and integer operands. +When the `-` operator is applied to numerical operands, the result is the value of the right operand subtracted from the value of the left. The operator requires numerical operands with the same type. You cannot mix float and integer operands. For example, the expression `6 - 4` would output the integer `2`. The expression `$.a - $.b` would evaluate the values of `a` and `b` within the root, and subtract the value of `$.b` from `$.a`. @@ -204,15 +267,15 @@ For example, the expression `3 * 3` would output the integer `9`. ### Division -When the `/` operator is used with a numerical operands, divides the value of the left operand into the right operand. The operator requires numerical operands with the same type. +When the `/` operator is used with a numerical operands, it outputs the value of the right expression divided by the value of the left. The operator requires numerical operands with the same type. -The output type matches the input type. Integer division results in the value being rounded down into the resultant integer. If a non-whole number output is required, or if different rounding logic is required, convert the inputs into floating point number with TO BE ADDED BEFORE MERGE. +The output type matches the input type. Integer division results in the value being rounded towards zero. If a non-whole number output is required, or if different rounding logic is required, convert the inputs into floating point number with the [`intToFloat` function](#functions), or back into integers with the [`floatToInt` function](#functions). Different types of rounding can be achieved for floating point numbers with the [functions `ceil`, `floor`, and `round`](#functions). -For example, the expression `3 / 3` would output the integer `1`. +For example, the expression `-3 / 2` would output the integer `-1`. ### Modulus -When the `%` operator is used with a numerical operands, it outputs the remainder of the division of the left operand into the right operand. The operator requires numerical operands with the same type. +When the `%` operator is used with a numerical operands, it outputs the remainder of value of the right expression divided by the value of the left. The operator requires numerical operands with the same type. For example, the expression `5 % 3` would output the integer `2`. @@ -224,14 +287,16 @@ The mathematical expression 23 is represented in the expression langu ### Equal To -The `==` operator checks for equality between the left and right type. It returns true when the left and right match. The type must be the same for both operands, so the expression `1 == 1.0` would fail. +The `==` operator checks for equality between the left and right type. It returns true when the left and right match. The type must be the same for both operands, so the expression `1 == 1.0` would produce a type error. You may use functions to convert types. For example, `intToFloat(1) == 1.0`. See [functions](#functions) for more type conversions. + The operator currently supports the types `integer`, `float`, `string`, and `boolean`. If another type is required, please create an issue with the expected behavior of the operator with the needed type. For example, `2 == 2` results in `true`, and `"a" == "b"` results in `false`. ### Not Equal To -The `!=` operator is the inverse of the [==](#equals ) operator. It returns true when the values do not match. The type must be the same for both operands, so the expression `1 != 1.0` would fail +The `!=` operator is the inverse of the [==](#equals ) operator. It returns true when the values do not match. The type must be the same for both operands, so the expression `1 != 1.0` would produce a type error. You may use functions to convert types. For example, `intToFloat(1) != 1.0`. See [functions](#functions) for more type conversions. + The operator currently supports the types `integer`, `float`, `string`, and `boolean`. If another type is required, please create an issue with the expected behavior of the operator with the needed type. For example, `2 != 2` results in `false`, and `"a" != "b"` results in `true`.