Expressions

Operators and Precedence

The operators defined by OADL are as follows (in precedence order):

Operator Description
. [ ] #[ ] ( ) #( ) Public ref, array indexing, flattened array indexing, function call, array call
~ ! - @ @@ ?? Bitwise NOT, logical NOT, negate, copy, deep copy, typeof
=> Type conversion
** Exponentiation
* / % Multiplication, division, modulus
+ - Addition, subtraction
<< >> Left shift, right shift
< > <= >= Less, Greater, Less or Equal, Greater or Equal
== != #= ?= ~= Equal, Not-equal, array component-wise equality, is_a, pattern match
& Bitwise AND
^ Bitwise XOR
| Bitwise OR
&& Logical AND (pseudo-op)
|| Logical OR (pseudo-op)
? : Conditional expression
## Concatenation of two values

See the chapter on Arrays, Lists, and Strings and the chapter on Dictionaries for discussion of the array indexing operators. See the chapter on Objects for discussion of the public reference operator. See the chapters on Arrays, Lists, and Strings and Classes for a more detailed discussion of how those object types work with arithmetic, bitwise, and comparison operators.

Arithmetic Operators

The following operators are considered arithmetic operators:

** * / % + -

These operators automatically do arithmetic type promotion on their operands. This is done according to the following arithmetic type promotion table:

Char Byte Ubyte Short Ushort Half WideChar Int Uint Float Long Ulong Double Enclosure Array Object
Char I I I I I F I I ui F L U D E A O
Byte I I I I I F I I ui F L U D E A O
Ubyte I I I I I F I I ui F L U D E A O
Short I I I I I F I I ui F L U D E A O
Ushort I I I I I F I I ui F L U D E A O
Half F F F F F F F F F F D D D E A O
WideChar I I I I I F I I ui F L U D E A O
Int I I I I I F I I ui F L U D E A O
Uint ui ui ui ui ui F ui ui ui F L U D E A O
Float F F F F F F F F F F D D D E A O
Long L L L L L D L L L D L U D E A O
Ulong U U U U U D U U U D U U D E A O
Double D D D D D D D D D D D D D E A O
Enclosure E E E E E E E E E E E E E E A O
Array A A A A A A A A A A A A A A A O
Object O O O O O O O O O O O O O O O O

The entries in the table may be decoded thus:

Table Entry Meaning
I Int
F Float
ui Uint
L Long
U Ulong
D Double
E Enclosure
A Array
O Object

Bitwise Operators

The following operators are considered bitwise operators:

& | ^ ~ << >>

They operate on integral or boolean types:

Char Byte Ubyte Short
Ushort WideChar Int Uint
Long Ulong Bool *
* Bool values are not allowed with the shift operators << and >>

They operate bit-by-bit on their operands. Integral operands of the bitwise operators are promoted according to the promotion table above.

The shift operators << and >> treat their operands as unsigned values. This means that the sign bit does NOT propagate through as a result of a right shift. Additionally, the shift count is clamped to the range [0..64] This means that a shift by a negative number is the same as no shift at all, and a shift of greater than 64 bits in the left hand operand will result in all the bits being shifted off (giving a result of zero).

Comparison Operators

The following operators are considered comparison operators:

== != #= < > <= >= ~=

The comparison operators == or != always return a scalar Bool result (unless operator overloading is involved). If both arguments are arrays, the result is a lexographic comparison of the contents of the two arrays, after type promotion:

    "Hello" == L"Hello"
true

    [1,2,3] == [1.,2.,3.]
true

Otherwise, standard arithmetic promotion is performed on scalar operands. Non-arithmetic values may be compared with the == and != operators:

    public foo
    public::foo == 3
false

If the operands of comparison operators <, <=, >, or >= are both String or WideString, a lexographic comparison is also performed:

    "123" < "124"
true

    "1234" > L"123"
true

Otherwise, standard arithmetic promotion and array conformance checks are performed on the operands:

    "123" > '2'
false false true

    [1,2,3] > 2.5
false false true

If a lexographic equality comparison is not wanted, use the #= component-wise equality operator:

    [1,2,3] == [1,2,4]
false

    [1,2,3] #= [1,2,4]
true true false

The ~= operator is the OADL string pattern matching operator. The left-hand side is a string-valued expression. The right hand side is a Perl-compatible regular expression. The operator returns true if the string matches the regular expression, and false otherwise. See also the match statement in the Procedures chapter.

Copy Operators

Since elements of constant arrays and strings may not be reassigned, and since it is cumbersome to type "str.copy()" to enable modification, the @ operator is implemented. It is shorthand syntax for the copy() intrinsic method. For example:

    a = "hello"
    a[0] = 'H'
Access failure

    a = @ a
    a[0] = 'H' // This will now succeed
    a
Hello

The @@ operator is similar to the @ operator, but it represents the deepcopy intrinsic method. For example:

    a = {"hello","world"}
    a[0][0] = 'H'
Access failure

    a = @@ a
    a[0][0] = 'H'
    a
Hello world

The @ and @@ operators are not allowed in constant initializers

Type Conversion Operator

The => operator is the OADL type conversion operator. It can be overloaded - which is a convenient way for classes to implement a class-to-string method. The left-hand side of the => operator is the value to be converted; the right-hand side of the => operator is the type (or class!) to be converted to. See the following example:

    a = 1.4;
    a => Int
1

    a => complex; // Assuming class complex properly overloads =>
(1.4,0)

    a => String
1.4

Type conversion may also be done via the call syntax; for example:

    Int(1.4)
1

    3 + Float(".1415927")
3.14159

Type Query Operators

The ?= operator is the same as the is_a() intrinsic method. It is used to evaluate the class hierarchy transitively. It returns true if the right hand side is one of the parent classes of the left hand side, and false otherwise.

The ?? operator is shorthand for the typeof intrinsic.

Logical (Short-circuit) Operators

The test ? expr1 : expr2 conditional operator evaluates the leftmost argument. If the test expression is logically TRUE then the result of the expression is the middle argument. Otherwise, the result of the expression is the right argument. For example, the following implements the signum function:

proc sgn(val)
{
    return (val < 0) ? -1 : ((val > 0) ? 1 : 0);
}

If the leftmost argument is logically TRUE, then the rightmost argument is not evaluated. If the leftmost argument is logically FALSE, then the middle argument is not evaluated. This is useful to prevent exceptions from being thrown; for example, this procedure will not index the array outside its bounds:

proc safeIdx(a, i)
{
    return (i < 0)
                ? nil
                : (i >= a.length())
                        ? nil
                        : a[i];
}

The complete list of logically FALSE values is:

false nil '\x0' '\x0'L 0B 0UB
0S 0US 0 0U 0L 0UL
0.H -0.H 0. -0. 0.D -0.D

Any other value is considered logically TRUE.

Logical AND and Logical OR are pseudo-ops; that is, they are not overloadable and both implement "short-circuit" semantics. Specifically:

This can be useful if an expression to be evaluated or a function to be called has an error condition that would be avoided by placing it in a short-circuit evaluator. The following code will not dereference the array a outside its bounds:

if ((i < 0) || (i >= length(a)) || (a[i] == 10)) {
    "a[i] == 10 (or i is out of range)\n";
}

Finally, the logical operators use the same list of logically FALSE values that the ? : operator uses. This can lead to different arithmetic results than bitwise AND and OR; for example,

    2 && 3
3

    2 & 3
2

A logical AND expression of op1 && op2 is equivalent to the conditional expression op1 ? op2 : op1, with the exception that each operand is evaluated only once. A logical OR expression of op1 || op2 is equivalent to the conditional expression op1 ? op1 : op2 with the same exception.

The ! operator performs the logical NOT operation according to the logically FALSE table above.

Expression Syntax

The following describes the syntax of expressions. The precedence of operators is encoded by the ordering of the productions.

expr            : cond_expr
                | expr '##' cond_expr
                ;

exprs           : /* NOTHING */
                | exprlist
                ;

exprlist        : expr
                | exprlist ',' expr
                ;

cond_expr       : logor_expr
                | logor_expr '?' expr ':' cond_expr
                ;

logor_expr      : logand_expr
                | logor_expr '||' logand_expr
                ;

logand_expr     : or_expr
                | logand_expr '&&' or_expr
                ;

or_expr         : excl_or_expr
                | or_expr '|' excl_or_expr
                ;

excl_or_expr    : and_expr
                | excl_or_expr '^' and_expr
                ;

and_expr        : equal_expr
                | and_expr '&' equal_expr
                ;

equal_expr      : rel_expr
                | equal_expr '==' rel_expr
                | equal_expr '!=' rel_expr
                | equal_expr '?=' rel_expr
                | equal_expr '~=' rel_expr
                | equal_expr '#=' rel_expr
                ;

rel_expr        : shift_expr
                | rel_expr '<' shift_expr
                | rel_expr '>' shift_expr
                | rel_expr '<=' shift_expr
                | rel_expr '>=' shift_expr
                ;

shift_expr      : add_expr
                | shift_expr '<<' add_expr
                | shift_expr '>>' add_expr
                ;

add_expr        : mult_expr
                | add_expr '+' mult_expr
                | add_expr '-' mult_expr
                ;

mult_expr       : pow_expr
                | mult_expr '*' pow_expr
                | mult_expr '/' pow_expr
                | mult_expr '%' pow_expr
                ;

// Note that '**' is right-associative
pow_expr        : cvt_expr
                | cvt_expr '**' pow_expr
                ;

cvt_expr        : unary_expr
                | cvt_expr '=>' unary_expr
                ;

unary_expr      : '~' unary_expr
                | '!' unary_expr
                | '-' unary_expr
                | '@' unary_expr
                | '@@' unary_expr
                | '??' unary_expr
                | '&' qual_name                 // Only for internal OADL use
                | term
                ;

indices         : index
                | indices ',' index
                ;

index           : expr
                | opt_expr ':' opt_expr
                ;

qual_name       : IDENTIFIER
                | IDENTIFIER '::' IDENTIFIER
                | '::' IDENTIFIER
                ;

term            : qual_name
                | 'public' '::' IDENTIFIER
                | '(' expr ')'
                | '(' 'proc' ')'                // ID of current proc
                | '{' exprs '}'                 // Inline List decl
                | '<<<' exprs '>>>'             // Inline Dict decl
                | '[' exprs ']'                 // Inline packed array decl
                | '[' iterator ']'
                | STRING
                | LSTRING
                | INTCON
                | FLOATCON
                | CHARCON
                | LCHARCON
                | MATCH_ARG
                | MATCH_COUNT
                | LOOPBASE
                | 'new' qual_name               // The create args are parsed...
                | 'new' '(' expr ')'            // ... by the call syntax below
                | 'proc' no_name_proc
                | 'proc' STRING no_name_proc
                | 'operator' operator
                | '`' operator
                | '`' IDENTIFIER                // Same as public::IDENTIFIER
                | term '(' exprs ')'            // The call syntax
                | term '[' '*' ']'              // For "Array[*]"
                | term '#( exprs ')'
                | term '.' public
                | term '[' indices ']'
                | term '#[' expr ']'
                | term '{' obj_props '}'        // For new and static objects
                | 'foreach' '(' expr ')' '{' expr '}'
                ;

iterator        : opt_expr ':' opt_expr
                | opt_expr ':' opt_expr ':' opt_expr
                ;

opt_expr        : /* NOTHING */
                | expr
                ;

public          : IDENTIFIER
                | '(' expr ')'
                | 'operator' operator
                | '`' operator
                | '`' IDENTIFIER
                ;

The no_name_proc and operator productions may be found in the OADL Program Syntax chapter.

Since procedures, classes, and objects are first-class types in OADL, they may also exist in expressions. For example, a program can put an unnamed procedure into an array:

a[3] = proc () {say("a[3]!\n");}

There is a subtle but significant difference between the static_obj and new_obj expression types. A static_obj creates a single, static object whose value persists over the entire duration of program execution. A new_obj creates a new dynamic object which will be deleted once all references to it are removed.

For more discussions of classes and objects, please see the Classes chapter.

Back to Objects (Instances)

Continue to Procedures

Return to Introduction