3.5. Operators and functions #

3.5.1. Ternary operator
3.5.2. Boolean logic operators with two operands
3.5.3. Bitwise operators with two operands
3.5.4. Equality and inequality comparison operators
3.5.5. Other comparison operators
3.5.6. Bitwise shifts
3.5.7. Addition and subtraction
3.5.8. Multiplication, division and modulo (remainder)
3.5.9. Power-of operator
3.5.10. Factorial operator
3.5.11. Unary plus and minus, logical and bitwise NOT, prefix increment and decrement
3.5.12. Postfix increment and decrement
3.5.13. Function calls and implicit multiplication
3.5.14. Parentheses
3.5.15. A note on token matching, precendence and syntax errors

OpenCReports expressions can use several operators and functions. The operator precedence is mostly as expected from the C programming language. One notable exception is implicit multiplication. The precedence classes are as below, in increasing order of precedence.

Note that all of the operators are implemented internally as a function call to the equivalent function. Since every function may be overridden by user functions, the operators may work differently than the documentation.

3.5.1. Ternary operator #

The ternary operator works as in the C, PHP and other languages:

expression1 ? expression2 : expression3

It's evaluated as follows: if the value of numeric expression1 is true (i.e. non-zero), then the result is the expression2, otherwise it's expression3. Type of expression2 and expression3 may differ, i.e. the result type will be the type of the underlying expression but it can result in runtime errors.

Internally, it's implemented using the iif() function.

3.5.2. Boolean logic operators with two operands #

Logic OR can be written as || or or. Example: a || b

Logic AND can be written as && or and. Logic AND has precedence over OR. Example: a && b

Internally, they are implemented using the Boolean AND and Boolean OR functions.

3.5.3. Bitwise operators with two operands #

The bitwise operators in this precedence class and in their increasing order of precedence are: bitwise OR (|) and bitwise AND (&).

3.5.4. Equality and inequality comparison operators #

The equality comparison operator can be written as = or ==.

The inequality comparison operator can be written as <> or !=.

3.5.4.1. Equality and inequality comparison operators on vectors #

Vector equality and inequality comparisons have the same precedence as scalar comparisons. These are not vectors in the mathematical sense, but a comma separated list of scalars inside brackets ([ ... ]), with op being any of the equality or inequality comparison operators:

[ expa1, expa2, ... ] op [ expb1, expb2, ... ]

Such comparisons are expanded into a logic operator form:

(expa1 op expb1) and (expa2 op expb2) and ...

Please, note that because of the mechanical conversion from the vector form to the expanded logic operator form, the following two lines have different meaning:

not ([ expa1, expa2, ... ] = [ expb1, expb2, ... ])
[ expa1, expa2, ... ] != [ expb1, expb2, ... ]

3.5.5. Other comparison operators #

Less-than (<), less-or-equal (<=), greater-than (>) and greater-or-equal (>=).

3.5.5.1. Other comparison operators on vectors #

Vector comparisons using <, >, etc. operators have the same precedence as their scalar counterpart. These are also expanded into the logic form, see Section 3.5.4.1 above.

3.5.6. Bitwise shifts #

Bitwise shift left (a >> b) and bitwise shift right (a << b).

3.5.7. Addition and subtraction #

a + b and a - b.

3.5.8. Multiplication, division and modulo (remainder) #

a * b, a / b and a % b.

3.5.9. Power-of operator #

a ^ b works as a-to-the-power-of-b.

3.5.10. Factorial operator #

a!, the '!' sign used as postfix operator.

3.5.11. Unary plus and minus, logical and bitwise NOT, prefix increment and decrement #

Unary plus (+a), unary minus (-a), logical NOT (!a, '!' used as prefix operator), bitwise NOT (~a), prefix increment (++a) and prefix decrement (--a).

3.5.12. Postfix increment and decrement #

Postfix increment (a++) and decrement (a--).

3.5.13. Function calls and implicit multiplication #

Function calls execute a function on operands: function(operand[, ...]). A function name is a single word known by OpenCReports at the time of parsing, either as a built-in function, or a user-supplied one. The function name cannot have a leading dot or be a domain-qualified identifier.

Implicit multiplication is when two distinct operands are in juxtaposition, in other words they are written side by side without any whitespace. In this case, there is an implied multiplication between them that acts with higher precedence than regular multiplication or division. Implicit multiplication is applicable in these situations:

  • A numeric constant juxtaposed with an identifier, the numeric constant is the on the left side.

    2x

  • A numeric constant juxtaposed with an expression inside parentheses. The constant can be on either side of the expression.

    2(a+b)
    (a+b)2

  • An identifier juxtaposed with an expression inside parentheses, the identifier is on the left side of the expression.

    x(a+b)

    This is only treated as implicit multiplication if the following conditions are met:

    • the x identifier is not a function name at the time of parsing

    • there is a single expression inside the parentheses

    If any of the conditions below are true, the expression is treated as a function call:

    • x is a known function name

    • there is no expression inside the parentheses

    • a series of comma delimited expressions is inside the parentheses

    The function call validity is checked against the number of operands, with a potential parser error. If there's an ambiguity between function names and identifiers provided by data sources, it can be avoided by using dot-prefixed or dot-prefixed and quoted identifiers, or fully qualified identifiers in the form of query.identifier.

  • An expression inside parentheses juxtaposed with an identifier on the right side.

    (a+b)a

  • Two expressions inside parentheses juxtaposed with each other.

    (a+b)(c+d)

Implicit multiplication is NOT applicable in these situations, besides the exceptions already explained above:

  • An identifier juxtaposed with a numeric constant, the numeric constant is the on the right side.

    x2

    Since an identifier name may include digits as the second and subsequent characters, the numeric constant, or at least its integer part is simply recognized as part of the identifier name itself according to the token matching. This can also result in syntax errors when not handled with care.

  • An identifier juxtaposed with another identifier.

    ab

    The reason is the same as in the preceding case: there is only a single identifier according to token matching.

3.5.14. Parentheses #

Parenthesized expressions are always computed first.

3.5.15. A note on token matching, precendence and syntax errors #

Expression parsing works on two levels: token matching and applying grammar. Token matching breaks up the expression string into tokens in a greedy way: without whitepace delimiters, the longest possible token is chosen.

This may lead to slight confusion when coupled with implicit multiplication. For example, the expression 2e-1e is broken up into two tokens: 2e-1 juxtaposed with e. The first token is interpreted as a numeric constant using e-notation (so that it will mean 2 * 10^(-1)) and the second is the identifier e, leading to the meaning 0.2 * e. This is unambiguous for the computer, but can be somewhat confusing to the the user reading or writing expressions. To avoid any confusion, don't use implicit multiplication and use whitespace and parentheses gratituously.

Expression parsing handles precedence and whitespaces. For example, these below do not mean exactly the same:

a++ + ++b
a+++++b

The former is obvious, but the latter may be a little surprising: (a++)++ + b. This is how the lexer or token matching works, i.e. it matches the longest applicable token first.

If a and b are numbers, then the result of both expressions is a + b + 2, but the way it's arrived at is different.

However, the ++ (increment) and -- (decrement) operators may be interpreted differently for other types. For example, if both a and b are of the datetime type, then the result also depends on whether one of them is an interval datetime, and the other (regular) datetime value has valid time or not. To make the expression unambiguous, whitespace and/or parenthesis should be used.

Another ambiguous example:

a++b

The above may be interpreted as a + +b but since no whitespace is used, the tokenizer is free to interpret it as a++ b, because ++ is longer than +, so the former is matched first as an operator token. This is a syntax error and expression parsing throws an error for it.