UEL

The Unscrambl Chai Expression Language (UEL)

Introduction

The Unscrambl Chai Expression Language (UEL) is a simple expression language used to specify derived attribute expressions. It is based on the Drive UEL.

Types

An expression in UEL can have one of the following primitive types: String (a string), Bool (a Boolean), Int16 (a 16-bit integer), Int32 (a 32-bit integer), Int64 (a 64-bit integer), or Double (a double precision floating point number). It can also have a list type, where the element type of the list is one of the primitive types: List(String), List(Bool), List(Int16), List(Int32), List(Int64), List(Double). It can also have a temporal type where the element type of the expression can be DateTime.

Literals

Bool, Double, Int32, Int64, and String literals can be present in an expression.

  • A Bool literal can be either true or false.

  • A Double literal is a decimal number that either contains the decimal separator (for example, 3.5, .5, 3.) or is given in the scientific notation (for example, 1e-4, 1.5e3). A Double literal must be between (2 - 2 ^ -52) * 2 ^ 1023 (~ 1.79 * 10 ^ 308) and -(2 - 2 ^ -52) * 2 ^ 1023 (~ -1.79 * 10 ^ 308). The magnitude of a literal cannot be less than 2 ^ -1074 (~ 4.94 * 10 ^ -324).

  • Integer literals without a suffix are of the Int32 type (e.g., 14). An Int32 literal must be between 2 ^ 31 - 1 (= 2147483647) and -2 ^ 31 (= -2147483648).

  • The L suffix is used to create Int64 literals (e.g., 14L). An Int64 literal must be between 2 ^ 63 - 1 (= 9223372036854775807L) and -2 ^ 63 (= -9223372036854775808L).

  • A String literal appears within double quotes, as in "I'm a string literal". The escape character \ can be used to represent new line (\n), tab (\t), double quote (\") characters, as well as the escape character itself (\\).

Arithmetic operations

An expression in UEL can contain the basic arithmetic operations: addition (+), subtraction (-), multiplication (*), and division (/), and modulo (%) with the usual semantics. These are binary operations that expect sub-expressions on each side. An expression in UEL can also contain the unary minus (-) operation that expects a sub-expression on the right. Parentheses (()) are used for adjusting precedence.

Addition operation corresponds to concatenation for the String type and is the only available operation on this type. Bool type does not support arithmetic operations. Division applies integer division on integer types and floating point division on Double types.

Examples:

  • A simple arithmetic expression: (3 + 4 * 5.0) / 2

  • A simple expression involving a String literal: "area code\tcountry"

  • A unary minus expression: -(3 + 5.0)

Comparison operations

An expression in UEL can contain the basic comparison operations: greater than (>), greater than or equal (>=), less than (<), less than or equal (<=), equals (==), and not equals (!=) with the usual semantics. These are binary operations. With the exception of equals and not equals, they expect sub-expressions of numerical types or strings on each side. For strings, the comparison is based on lexicographic order. For equals and not equals, the left and the right sub-expressions must be of compatible types with respect to UEL coercion rules. The result of the comparison is always of type Bool.

Examples:

  • An expression using comparisons: 3 > 5

  • An expression using String comparisons: "abc" < "def"

  • An expression using inequality comparison: "abc" != "def"

Logical operations

An expression in UEL can contain the basic logical operations: logical and (&&) and logical or (||) with the usual semantics. These are binary operations that expect sub-expressions of type Bool on each side. Shortcutting is used to evaluate the logical operations. For logical and, if the sub-expression on the left evaluates to false, then the sub-expression on the right is not evaluated and the result is false. For logical or, if the sub-expression on the left evaluates to true, then the sub-expression on the right is not evaluated and the result is true. An expression in UEL can also contain the not (!) operator, which is a unary operator that expects a sub-expression of type Bool on the right.

Examples:

  • A simple logical expression: 3 > 5 || 2 < 4

  • A logical expression that uses not: ! (3 > 5)

Ternary operation

An expression in UEL can make use of the the ternary operation: condition ?  choice1 : choice2. The condition of the ternary operation is expected to be of type Bool and the two choices are expected to be sub-expressions with compatible types. The ternary operation is lazily evaluated. If the condition evaluates to true, then the result is the first choice, without the sub- expression for the second choice being evaluated. If the condition evaluates to false, then the result is the second choice, without the sub-expression for the first choice being evaluated.

Examples:

  • A simple ternary operation: 3 < 10 ? "smallerThan10" : "notSmallerThan10"

List Operations

A list is constructed by specifying a sequence of comma (,) separated values of the element type, surrounded by brackets ([]) . For instance, an example literal for List(Double) is [3.5, 6.7, 8.3] and an example for List(String) is ["Unscrambl", "Drive"]. An empty list requires casting to define its type. As an example, an empty List(String) can be specified as List(String)([]).

An expression in UEL can contain a few basic list operations: containment (in), non-containment (not in).

Containment yields a Boolean answer, identifying whether an element is contained within a list. For instance, 3 in [2, 5, 3] yields true, whereas 8 in [2, 5, 3] yields false. Non-containment is the negated version of the containment. For instance, 3 not in [2, 5, 3] yields false, whereas 8 not in [2, 5, 3] yields true.

Precedence and Associativity of Operations

Operations

Associativity

()

[]

unary -, !

*, /, %

left

+, -

left

>, >=, <, <=

left

==, !=

left

in

&&

left

||

left

?:

Attributes

Expressions in UEL can also contain attributes. Attributes are identifiers that are defined in the Querybot Entity Relationship Model. Each attribute has a type and can appear in anywhere a sub-expression of that type is expected.

Attribute names that contain alpha-numeric characters and underscores can be used as is in the expression. Attributes names that contain spaces or any other unicode characters have to be enclosed in an attribute identifier $"attribute with spaces".

Examples:

  • A string formed by concatenating an attribute named first name and an attribute named last name from the current entity: $"first name" + $"last name"

  • An arithmetic expression involving an attribute and Int32 literals: numSeconds  / (24 * 60 * 60)

  • A floating-point arithmetic expression, where the floating point literal is of type Double: cost / 1000.0

  • A Boolean expression checking if a String literal is found in an attribute named places of type List(String): "airport" in places

  • A ternary expression indicating if the cost of a product is high or low cost < 100 ? "Low" : "High"

Null values

Attributes in UEL are nullable, that is, attributes can take null values. To denote a null value, the null keyword is used.

Only the following actions are legal on the expressions with null values:

  • Built-in function calls: A nullable function parameter can take a null value. E.g.: the first parameter in the string.concat(null) call

  • Ternary operations: The values returned from choices can be null. E.g.: amount > 100 ? "High" : null where amount is an attribute of the String type

  • List items: Null values can be list items. E.g.: [null, 3, 5, 6, null]

  • List containment check operations: Null values can be used on the left hand side. However, the result will be null independent of the contents of the list. E.g.: null not in [null, 3, 5, 6, null]

  • Equality check operations: Null values can be compared for equality and non-equality. E.g.: $"purchase notes" == null where purchase notes is an attribute. (Note: For comparisons with null values, the isNull operator can also be used. Examples: isNull(null) yields true isNull($"purchase notes") yields false if the purchase notes attribute is not null)

Database specific behaviors

  • String concatenation: The string.concat function and the string addition operation can output different results for inputs containing NULL database values. Some databases flag this operation as invalid and return NULL. Other databases discard the NULL values and concatenate the remaining strings. E.g.: "string" + null + "concatenation" yields either stringconcatenation or NULL E.g.: name + "," + $"nullable profession". When nullable profession attribute is not NULL the result is the same for all databases E.g. Tom Hanks,Actor. When nullable profession attribute is NULL the result can either be NULL or Tom Hanks,

  • Modulo operator:

    • The modulo operation yields the remainder from the division of the first operand by the second. If both operands have the same sign, then the result is the same for all databases. E.g.: 11 % 4 = 3 and -11 % -4 = -3 If the operands have opposite signs, then the result may differ across databases. E.g.: 11 % -4 may either be 3 or -1. -11 % 4 may either be -3 or 2

    • Some databases support using floating point data types for the modulo operator. Others disallow this behavior and throw an error.

    • For modulo by zero case, most databases throw division by zero error. Other databases may return NULL or the first argument.

  • datetime.add: Adding or subtracting months from a date or date-time value is almost the same for all databases. E.g.: "2010-03-01" + 1 month = "2010-04-01" and "2010-03-31" + 1 month = "2010-04-30" If the initial date or date-time value is the last day of the month, some databases skip to the last day of the resulting month. E.g.: "2010-04-30" + 1 month is either 2010-05-31 or 2010-05-30

  • Rounding: The number.round function can output different results in case of the supplied value is halfway between two integers (E.g. -0.5, 1.5). Some databases round halves to the nearest even integer (E.g. -0.5 -> 0, 1.5 -> 2), and some databases round halves away from zero (E.g. -0.5 -> -1, 1.5 -> 2). Some databases choose different strategies depending on the input data type. The behavior of this function matches the behavior of the underlying database.

Built-in functions

UEL Builtin Functions