Operators
Operators control the evaluation order of an expression and determine how the operands are combined to yield a final result. Each operator has a precedence level within the entire group of available operators. When an expression is evaluated, the operators act in order of their precedence.
An operator is either unary, binary, or ternary. (See the Precedence of Numeric Operators table for a list of operators and their type.) Unary operators act on a single operand and must appear on the left side of the expression. Binary operators act on two operands and are placed between the operands. Ternary operators take three operands.
Synergy DBL supports the following types of operations that occur outside of a class:
- Assignment operations
- Alpha operations (only)
- Numeric operations (only)
- Relational operations
- Boolean operations
- Bitwise operations
- Conditional operations
See Object operators for information about operators that can be used on a class.
Assignment operations
The assignment operation is equivalent to a single-line assignment statement, which is described in detail in Assignment statements. The equal sign is the most common assignment operator. Additional assignment operators are specific to numeric and bitwise operations. (See Additional numeric assignment operators and Additional integer assignment operators.)
Assignment operator
We refer to the equal sign as an assignment operator, because it assigns one operand to another. When you use the equal sign as an operator, the operand on the left must be a variable specification, and the operand on the right is treated as an expression and stored in the variable before the variable is used in the rest of the expression.
For example:
xcall sub(a=1, b=2, c="ABC")
is equivalent to the following:
a=1 b=2 c="ABC" xcall sub(a, b, c)
In the following example, Y is set to 3 before it is added to X:
X + Y = 3
This characteristic enables you to create operations like the following:
if ((len=^size(arg)).eq.4)
The assignment operator causes parts of an expression to be evaluated from right to left instead of left to right. See How assignment operators affect order of evaluation for more information. |
For a description of the += addition assignment operator, see Concatenation and reduction.
Alpha operations
Conversion to and from System.String
Alpha operators allow both alpha and System.String operands in the same expression. However, prior to calling the appropriate operator, one will implicitly be converted to the other according to the following rules:
- Relational operators (.EQ., .NE., etc.) convert System.String operands to alpha when the other operand is alpha. In Synergy .NET, literals for a string and a literal are String, not alpha, which differs from traditional Synergy where literals are alpha.
- String relational operators (.EQS., .NES., etc.) convert alpha operands to System.String when one of the operands is a System.String.
- When using symbolic string relational operators (==, !=, etc.) with System.String operands, see the String operators table.
- The concatenation operator ( + ) converts an alpha operand on the right to System.String if the operand on the left is a System.String. In traditional Synergy, if the operand on the left is alpha and the operand on the right is System.String, the System.String is converted to alpha.
When an alpha operand is converted to System.String, it is not blank trimmed.
In alpha expressions, the addition (+), subtraction (–), and addition assignment (+=) operators perform string operations on alpha operands. The addition operator performs string concatenation, in which the operand on the right is appended to the operand on the left. The subtraction operator performs string reduction, in which the first occurrence of the operand on the right is removed from the operand on the left. If the operand on the right does not occur in the operand on the left, the result is the content of the operand on the left.
For example, the following expression yields a result of “abcdef”:
"abc" + "def"
The expression below yields a result of “xydefabc”:
"xyabcdefabc" - "abc"
The addition assignment operator modifies the value of a variable by concatenating the value currently stored in that variable with the value that follows the operator. For example,
record svar, string proc svar = "hello" svar += ", there!" ;svar now contains "hello, there!" end
A System.String can be concatenated with another string or, in Synergy .NET, with a number or an object. A non-string operand is converted into a string and appended to either the beginning or the end of the original string, depending on the order of the operands. (Objects are converted to strings by calling their ToString method.) The result of the expression is a string. For example,
main proc data fred, string, "fred" data ivar, int, 4 console.writeline(fred+ivar) ;Results in "fred4" end
Also see the Concat method in System.String.
String relational operators
String relational operations compare two operands. Either operand can be an alpha or System.String, and they are compared slightly differently than relational operators. The string relational operators available in Synergy DBL are as follows:
.EQS. |
Equal to |
.NES. |
Not equal to |
.GTS. |
Greater than |
.LTS. |
Less than |
.GES. |
Greater than or equal to |
.LES. |
Less than or equal to |
Alpha operands are compared according to the order of characters within the ASCII character set. The complete ASCII character set is listed in Appendix B: ASCII Character Set. If the alpha operands in an expression are the same size, the characters in corresponding character positions are compared. If the alpha operands differ in size, the shorter operand is logically extended with blanks on the right until it is the same size as the larger operand. For example, comparing “A” to “ABC” is logically equivalent to comparing “A ” to “ABC”.
The result of a string relational operation is either true or false. If the relation is true, the result is an integer value of 1. If the relation is false, the result is an integer value of 0.
- The result of the .EQS. operation is true if the operand on the left is equal to the operand on the right.
- The result of the .NES. operation is true if the operand on the left is not equal to the operand on the right.
- The result of the .GTS. operation is true if the operand on the left is greater than the operand on the right.
- The result of the .LTS. operation is true if the operand on the left is less than the operand on the right.
- The result of the .GES. operation is true if the operand on the left is greater than or equal to the operand on the right.
- The result of the .LES. operation is true if the operand on the left is less than or equal to the operand on the right.
Here are some examples:
Expression |
Result |
---|---|
“ABCDEF” .eqs. “ABCDEF” |
true |
“ABCDEF” .eqs. “ABC” |
false |
The following expression yields a result of 5:
5 + 11 * ("AB" .eqs. "ABCD")
First, the statement that “AB” is equal to “ABCD” is evaluated to be false. Because false has a value of 0, the equation becomes 5 + 11 * 0, which equals 5.
If either operand is System.String, the corresponding System.String operator is called. Strings are equal if the length of both strings are equal as well as the contents of the both strings (according to the order of characters in the ASCII character set). A System.String object that is equal to ^NULL is less than a System.String object that is equal to “”.
Numeric operations
A numeric data hierarchy consists of the following data types, listed from highest to lowest:
- Implied-packed
- Packed
- Implied-decimal
- Decimal
- Integer
If an operator has operands with different numeric types, the operand that is lower in the hierarchy is promoted to the data type of the higher one. For example, if an integer value and an implied-decimal value are operands in an expression, the integer is promoted to implied-decimal before the operation occurs, and the result is implied-decimal.
For performance reasons, Synergy uses the platform’s native integer size for expression intermediates. On 32-bit platforms, if an i8 is explicitly used in an integer expression, an i8 intermediate is used. Otherwise, an integer expression not using i8s that results in a value exceeding the native i4 will wrap (not be promoted to an i8), and no error will be generated. If necessary, you can avoid this by using i8s instead but at a slight cost to integer performance. On 64-bit platforms, all integer expressions use native i8 intermediates. |
Unary plus and minus operators
Unary plus and minus operators determine whether the numeric operand is positive (+) or negative (–). Synergy DBL ignores the unary plus operator, because it assumes unsigned values are positive. The unary minus operator changes the sign of the operand to its right. Successive minuses are combined algebraically. For example, two minuses become a plus, three minuses become one minus, and so forth.
The data type of the operation’s result is the data type of the operand.
Arithmetic operators
Arithmetic operators perform addition (+), subtraction (–), multiplication (*), division (/ and //), or (in Synergy .NET only) modulo (.mod.).
If neither operand contains an implied decimal point, the addition, subtraction, multiplication, and division operators perform standard, signed, whole number arithmetic. If either operand contains an implied decimal point, the intermediate will contain a fractional portion.
If you add a 1 to a d28 variable that contains all 9s, the number rolls over to zero.
If a division operation using “/” involves two whole numbers (decimal, integer, or packed), any fractional part of the result is truncated without rounding. For example, the following expression yields a result of 2, not 2.5 or 3:
5/2
Division by zero is illegal. If you try to divide by zero, you’ll get an “Attempt to divide by zero” error ($ERR_DIVIDE).
When you multiply or divide implied-decimal or implied-packed operands, the intermediate result of the operation is calculated to a fractional precision of 29 digits. It is then truncated or rounded to 28 digits. For example, the expression below yields a result of 0.6666666666666666666666666666 if truncation is the default or 0.6666666666666666666666666667 if rounding is the default:
2.0/3.0
Rounding is the default unless (in traditional Synergy) you set system option #11 or specify the TRUNCATE option in the SUBROUTINE statement, which indicates the start of the routine. See system option #11 and SUBROUTINE-ENDSUBROUTINE for more information. (Neither TRUNCATE nor system option #11 are supported in Synergy .NET.)
Using the “//” division operator always results in an intermediate result that contains a fractional portion.
In Synergy .NET, the .mod. operator performs a modulo operation, which finds the remainder of a division operation between two numbers. For example, 9 .mod. 2 is 1, because 9 / 2 is 4 with a remainder of 1.
Synergy DBL checks a numeric data field for invalid characters or length when that field is used in an arithmetic expression. If you use a decimal field that contains more than 28 digits, a packed field that contains more than 18 digits, an implied-decimal field that has a whole number part or fractional precision of more than 28 digits, or an implied-packed field that has a whole number part of more than 18 digits or a fractional precision of more than 10 digits, an “Arithmetic operand exceeds maximum size” ($ERR_BIGNUM) error is generated. If any nonnumeric characters other than a blank, a decimal point, or a sign (+ or –) are present in the operands, a “Bad digit encountered” error ($ERR_DIGIT) is generated.
Additional numeric assignment operators
Additional assignment operators for numeric operands include +=, –=, *=, and /=. These operators are more efficient than their equivalent expressions (shown in the following table).
Operation |
Equivalent expression |
---|---|
A += B |
A = A + B |
A –= B |
A = A – B |
A *= B |
A = A * B |
A /= B |
A = A / B |
Rounding operators
There are two kinds of rounding operators: # and ##.
The # rounding operator is a binary operator that rounds the left operand (the value being rounded) by the number of digits specified by the right operand (the round value). For example, if the right operand has a value of 3, the rounding operation discards the rightmost three digits of the left operand and then adds 1 to the result if the leftmost discarded digit is greater than or equal to 5.
The following rules apply to rounding with the # operator:
- The round value must be in the range 1 through 28. (Anything outside of this range generates an “Invalid round value” error [$ERR_RNDVAL].)
- Both operands must be decimal or integer.
- The data type of the result is the same as the operand.
- The sign of the result is the sign of the value being rounded.
- If the number of significant digits in the value being rounded is less than the round value, the result is zero.
Here are some examples:
Expression |
Result |
---|---|
345671 # -1 |
compiler error |
345671 # 0 |
345671 |
345678 # 3 |
346 |
345678 # 4 |
35 |
-345678 # 4 |
-35 |
345678 # 7 |
0 |
6789.456 # 1 |
compiler error |
The ## rounding operator specifies true rounding (rounding without discarding any digits in the value to be rounded). It rounds the left operand (the value being rounded) by the number of digits specified by the right operand (the round value).
The following rules apply to rounding with the ## operator:
- The round value must be in the range -28 through 28.
- If the round value is positive, rounding begins “round value” places to the left of the decimal point. For example, if the round value is 3, rounding begins three places to the left of the decimal point.
- If the round value is negative, rounding begins “round value + 1” places to the right of the decimal point. For example, if the round value is -1, rounding begins two places to the right of the decimal point.
- Both operands must be decimal or integer.
- The data type of the result is decimal.
- The sign of the result is the sign of the value being rounded.
- If the number of significant digits in the value being rounded is less than the round value, the result is zero.
Here are some examples:
Expression |
Result |
---|---|
123.456 ## -2 |
123.46 |
12345 ## 2 |
12300 |
345671 ## -1 |
345671.0 |
345671 ## 0 |
345671 |
345678 ## 3 |
346000 |
345678 ## 4 |
350000 |
-345678 ## 4 |
-350000 |
Relational operations
Relational operators
Relational operators (and their corresponding symbolic relational operators) compare two operands. The relational operators available in Synergy DBL are as follows:
.EQ. (or ==) |
Equal to |
.NE. (or !=) |
Not equal to |
.GT. (or >) |
Greater than |
.LT. (or <) |
Less than |
.GE. (or >=) |
Greater than or equal to |
.LE. (or <=) |
Less than or equal to |
When you use a relational operator, both operands must be either alpha or System.String, or both must be numeric. If only one of the operands is a string, the == (or !=, etc.) operator maps to .EQS. (or .NES., etc.); otherwise, it maps to .EQ. (or .NE., etc.). When using System.String operands, see Conversion to and from System.String.
See the String operators table for a list of string operators and their equivalents. |
The result of a relational operation is either true or false. If the relation is true, the result is an integer value of 1. If the relation is false, the result is an integer value of 0.
- The result of the .EQ. (or ==) operation is true if the operand on the left is equal to the operand on the right.
- The result of the .NE. (or !=) operation is true if the operand on the left is not equal to the operand on the right.
- The result of the .GT. (or >) operation is true if the operand on the left is greater than the operand on the right.
- The result of the .LT. (or <) operation is true if the operand on the left is less than the operand on the right.
- The result of the .GE. (or >=) operation is true if the operand on the left is greater than or equal to the operand on the right.
- The result of the .LE. (or <=) operation is true if the operand on the left is less than or equal to the operand on the right.
Numeric operands result in standard, signed arithmetic comparisons.
Alpha operands are compared according to the order of characters within the ASCII character set for the length of the shortest operand. See Appendix B: ASCII Character Set for the complete ASCII character set. If the alpha operands in an expression differ in size, only the number of characters in the shortest operand are compared.
Here are some examples:
Expression |
Result |
---|---|
“ABCDEF” .eq. “ABC” |
true |
“ABCDEF” .eq. “ABD” |
false |
The following expression yields a result of 20:
17 + 3 * ("ABCD".eq."AB")
First, the statement that “ABCD” is equal to “AB” is evaluated to be true. Because true has a value of 1, the equation becomes 17 + 3 * 1, which equals 20.
Unsigned integer operators
Synergy DBL also supports the following unsigned integer operators:
.EQU. |
Unsigned integer equal to |
.NEU. |
Unsigned integer not equal to |
.GTU. |
Unsigned integer greater than |
.LTU. |
Unsigned integer less than |
.GEU. |
Unsigned integer greater than or equal to |
.LEU. |
Unsigned integer less than or equal to |
Boolean operations
Boolean operators
Boolean operators compare the truth value of operands. The Boolean operators are as follows:
.OR. (or ||) |
OR |
.XOR. |
Exclusive OR |
.AND. (or &&) |
AND |
.NOT. (or !) |
NOT |
The result of a Boolean operation is either true or false. If the operation is true, the result is an integer value of 1. If the operation is false, the result is an integer value of 0.
- The result of the .OR. (or ||) operation is true if either operand has a truth value of true.
- The result of the .XOR. operation is true if only one of the operands has a truth value of true.
- The result of the .AND. (or &&) operation is true if both operands have a truth value of true.
- The result of the .NOT. (or !) operation is true if the operand on the right has a truth value of false.
Boolean operators are evaluated from left to right. If the result of an .AND. or .OR. Boolean operation can be determined by the evaluation of the operand on the left, Synergy DBL won’t process the operand on the right. For example, in the following Boolean expression:
ndx .and. array(ndx)
the right operand, array(ndx), won’t be evaluated if the left operand, ndx, has a false truth value, because if ndx is false, the entire expression is false.
Bitwise operations
Bitwise operators perform Boolean operations on the bits within integer operands. (A bitwise operator can also be used within an enumeration.) If an operand is decimal, it is converted to integer before the operation. The bitwise operators are as follows:
.BOR. (or |) |
Bitwise OR |
.BXOR. |
Bitwise exclusive OR |
.BAND. (or &) |
Bitwise AND |
.BNAND. |
Bitwise NOT AND |
.BNOT. (or ~) |
Bitwise NOT |
<< |
Bitwise left shift (Synergy .NET only) |
>> |
Bitwise right shift (Synergy .NET only) |
If the size of the operands are not the same, the length of the smaller operand is logically extended to the length of the larger by adding null (0) high-order bytes. The size of the result is the size of the largest operand.
- Each result bit in a .BOR. (or |) operation is one if either of the corresponding bits of the operands is one, and zero if both bits are zero.
- Each result bit in a .BXOR. operation is one if only one of the corresponding bits of the operands is one, and zero if both bits are the same value.
- Each result bit in a .BAND. (or &) operation is one if both of the corresponding bits of the operands are one, and zero if either bit is zero.
- Each result bit in a .BNAND. operation is one if either of the corresponding bits of the operands is zero, and zero if both bits are one.
- Each result bit in a .BNOT. (or ~) operation is one if the corresponding bit of the operand is zero, and zero if it is one.
For example, in the bitwise expression:
i2var .band. i1var
where the variables are declared as follows:
i2var i2, 256 i1var i1, -1
i1var is logically extended to an i2 field by adding a null high-order byte. The result of this operation is an i2 value of zero.
Bit shifting operators
Bitwise left shift and right shift operators give you more control over individual bits within integer types. When used in combination with other bitwise operators, the left and right shift operators (<< and >>) can access parts of fields that store flag data or combine and extract small pieces of data. They can be applied to Synergy decimal and integer types; decimal types are converted into integers before the shift is performed.
The << and >> operators shift, in bucket brigade fashion, the bits in the value on the left of the operator to either the left or the right by the number of bits specified by the value on the right of the operator. Here’s an example:
data x, i1, 3 data y, i1 y = x << 2
The binary value of x is 00000011. Shifting that left by two bits changes the binary value to 00001100, which is 12 decimal. In effect, x was multiplied by 22, or 4. If a 1 bit is shifted into the high bit of a signed field, the value will be negative; if a 0 is shifted into the high bit, the value will be positive. Any bits shifted out of the high end of the field will be lost, and 0 bits are shifted into the low end of the field.
If, on the other hand, x is shifted 1 bit to the right, as shown below:
y = x >> 1
the value becomes 1, so 00000011 becomes 00000001, in effect, dividing x by 21, or 2. Any bits shifted out of the low end of the field will be lost, and bits that match the sign bit (the high bit) will be shifted in from the high end, so a negative value remains negative and a positive value remains positive. This is called a sign extending right shift.
The following example shows a class that implements the right shift operator:
namespace ns1 class fred public method fred arg, int proc val = arg end public property val, int method get endmethod method set endmethod endproperty public static method op_RightShift, @fred arg1, @fred arg2, int proc arg1.val = arg1.val >> arg2 ;Right shifts the val property mreturn arg1 end private myval, int endclass endnamespace proc data h, @fred, new fred(125) h = h >> 3 ;Calls the op_RightShift method console.writeline(h.val) end
Additional integer assignment operators
Additional assignment operators for integer operands are |= and &=. Equivalent expressions are shown in the following table:
Operation |
Equivalent expression |
---|---|
A |= B |
A = (A .BOR. B) |
A &= B |
A = (A .BAND. B) |
These additional assignment operators can make bit manipulation much clearer. For example, the following statement sets all of the bits within A that are also set within B:
A |= B
The statement below sets A to all bits set in A and B:
A &= B
Conditional operations
A conditional operation is the equivalent of an IF statement condensed into a single line of code. Full support is provided for the conditional, null coalescing, and null conditional operators when compiling with a -qrntcompat value of 110101 or greater. In runtime versions prior 11.1.1, some expressions involving temporary handles are restricted within ternary operations.
Conditional operator
The conditional operator (?:) defines a conditional expression that takes three operands: a condition, a result for true, and a result for false. This allows you to assign one value to the variable if the condition is true and another value if the condition is false.
It has the syntax
condition ? expression1 : expression2
where
condition
A Boolean expression that evaluates to true or false, Condition must be suitable for use in an IF statement.
expression1
The result of the operation if condition evaluates to true.
expression2
The result of the operation if condition evaluates to false,
Expression1 and expression2 can be any type, object, or value type, but they must be compatible types with one other or a TERNMSMCH error will occur.
For example,
main proc begin data x, int, 5 data y, int y = x > 4? 4: x ;Results in 4 being assigned to y open(15,o,'tt:') writes(15,%string(y)) end endmain
Null coalescing operator
In Synergy .NET, the null coalescing operator (??) provides an easy, compact way to check if an operand is null, and if it is, to return an alternate value. The ?? operator returns the operand on the left if that operand is not null; otherwise it returns the operand on the right.
For example, for the expression
x = y ?? z
y is assigned to x unless y is null. If y is null, z is assigned to x. The types of x, y, and z must be objects or nullable.
You can also use the ?? operator to return a default value when a nullable type is assigned to a non-nullable type, as shown below:
data int_val, int data null_int, int? . . . int_val = null_int ?? 10
If you attempt to assign a nullable value type to a non-nullable value type without using the null coalescing operator, a compile-time error will occur.
Null conditional operator
In Synergy .NET, the null conditional operator (?. in a path) enables you to conditionally test for null before performing a member-access operation, which allows you to handle a null check with a single line of code. The null conditional operator evaluates whether or not the object to its left is null. If it’s null, null is returned and the access chain is stopped, thereby avoiding a null reference exception. If it’s not null, the object to its right is fetched.
For example, the following expression is null if value is null:
value?.substring(0, length)
Here’s another example:
fred = parent?.a?.b?.c
The first ?. operator checks to see if parent is null. If not, it fetches what’s to the right; if so, it returns null and halts the access chain. The same process is repeated with a and b.
Note that using this feature affects inferred type determination. For example, if you have a class1 that has an integer field, intfld, and the following code (where cvar is of type class1):
data myfld = cvar?.intfld
the expression could return a null (if cvar was null) or it could return an int (the type of intfld), so the resulting inferred type would be nullable<int>.
However, if the statement were
data myfld = cvar.intfld
the resulting inferred type could only be int.
In Synergy .NET, the null suppression (or null forgiving) operator (!) can be appended to the end of a variable or expression to suppress nullability warnings involving that variable or expression. For example,
data cvar = cvar2
will give a warning if the DATA statement is in a nullable region and cvar2 is “potentially null.” This can be suppressed by writing
data cvar = cvar2!
which ignores the fact that cvar2 could be null. This can also be used to force a null value to be assigned to a non-null variable, such as
data cvar = ^null!
This operator can also be used in a path for method access (!.). For example, academy.student.Study() will give a null warning if the student variable is possibly null. You can suppress this warning by instead writing academy.student!.Study().
The null suppression operator has no effect when used outside of a nullable region. (See .NULLABLE for more information about nullable regions.)
Object operators
Object operators can be implemented within a class so they can be used on instances of the class.
To find out if the runtime class of an instance variable is from a particular class, use the .IS. operator. If the class provided is in the class hierarchy of the instantiated class of the instance variable, the .IS. operator returns true. If not, it returns ^NULL. For example,
MyOther myobj = new MyClass if (myobj .is. MyClass)
Unary class operators
Synergy DBL supports the following unary operator methods for use on a class:
Unary operator methods |
||
---|---|---|
Unary operator method name |
Unary operator |
Example syntax |
op_Increment |
incr |
incr obj |
op_Decrement |
decr |
decr obj |
op_UnaryNegation |
- |
-obj |
op_UnaryPlus |
+ |
+obj |
op_LogicalNot |
! (.not.) |
!obj |
op_True |
truth condition |
if (myclass) |
A unary operator method is invoked with the appropriate syntax from the table above. For example, the following invokes the op_Increment() operator:
c1, @myclass proc incr c1 end
A unary operator method must have one argument, and it must be the enclosing class type. The method must be declared as PUBLIC and STATIC, and it cannot return a VOID.
The unary operator method in the example below increments a myclass object:
public static method op_Increment, @myclass p1, @myclass proc p1.m_value = p1.m_value + 1 ;m_value is an i4 field in myclass return p1 end
Synergy DBL supports the following binary operator methods for use on a class:
Binary operator methods |
||
---|---|---|
Binary operator method name |
Binary operator |
Example syntax |
op_Addition |
+ |
obj + obj2 |
op_Subtraction |
- |
obj - obj2 |
op_Multiply |
* |
obj * obj2 |
op_Division |
/ |
obj / obj2 |
op_ExclusiveOr |
.xor. |
obj .xor. obj2 |
op_BitwiseAnd |
& (.band.) |
obj & obj2 |
op_BitwiseOr |
| (.bor.) |
obj | obj2 |
op_BXOR | .bxor. | obj .bxor. obj2 |
op_BNAND | .bnand. | obj .bnand. obj2 |
op_LogicalAnd |
&& (.and.) |
if (obj && obj2) |
op_LogicalOr |
|| (.or.) |
if (obj || obj2) |
op_OnesComplement |
~ |
if (~obj) |
op_Equality |
== (.eq.) |
if (obj == obj2) |
op_GreaterThan |
> (.gt.) |
if (obj > obj2) |
op_LessThan |
< (.lt.) |
if (obj < obj2) |
op_Inequality |
!= (.ne.) |
if (obj != obj2) |
op_GreaterThanOrEqual |
>= (.ge.) |
if (obj >= obj2) |
op_LessThanOrEqual |
<= (.le.) |
if (obj <= obj2) |
op_LeftShift | << | obj = obj << obj2 |
op_RightShift | >> | obj = obj >> obj2 |
A binary operator method is invoked with the appropriate syntax from the table above.
A binary operator method must have two arguments, and at least one must be the enclosing class type. The method must be declared as PUBLIC and STATIC, and it cannot return a VOID. You can declare multiple binary operator methods of the same name within a class as long as their method signatures differ.
The binary operator method in the example below adds two myclass objects:
public static method op_Addition, @myclass p1, @myclass p2, @myclass proc mreturn new myclass(p1.m_value+p2.m_value) end
If either operand to a symbolic string relational operator is of type System.String, the operator will operate the same as its corresponding string relational operator:
== |
.EQS. |
!= |
.NES. |
> |
.GTS. |
< |
.LTS. |
>= |
.GES. |
<= |
.LES. |
If a string relational operator is used in an expression and one of the operands is System.String, the compiler assumes that both operands are System.String or can be converted to System.String, and the corresponding string operator is run. (See System.String for a list of string operators.) For == or .EQS. where an operand is System.String, the equality operator (op_Equality) is run, which ensures the lengths are equal and then performs a character-by-character comparison.
In Synergy .NET, or when compiling with the -qnoargnopt option in traditional Synergy, the USING statement also operates using string relational operators.
If the nonstring relational operator is used, the compiler assumes that both operands are of type alpha or can be implicitly converted to alpha.
To compare two boxed objects, you must first unbox them using a type cast.
When comparing two object references, the equality operator (.EQ. or ==) returns true only if the objects reference the exact same instance of an object. Otherwise, it returns false. If you declare the op_Equality operator for the class, the result of the op_Equality method is returned.
For example, assume ChildClass extends ParentClass.
var1 ,@ChildClass var2 ,@ChildClass var3 ,@ParentClass var4 ,@ChildClass var1 = new ChildClass() var2 = var1 if (var2 .eq. var1) ;True if (var1 .eq. var2) ;True var3 = (ParentClass)var1 if (var3 .eq. var1) ;True if (var3 .eq. var2) ;True var4 = new ChildClass() if (var4 .eq. var1) ;False if (var4 .eq. var2) ;False if (var4 .eq. var3) ;False clear var1 clear var2 if (var1 .eq. var2) ;True
In a Where expression (see Synergex.SynergyDE.Select.Where), avar==avar is equivalent to avar.eqs.avar |
The inequality operator (.NE. or !=) returns true if the objects do not reference the exact same instance of an object. Otherwise, it returns false. If you declare the op_Inequality operator for the class, the result of the op_Inequality method is returned.
The example below determines that myobj is uninstantiated (i.e., it has never been created with NEW):
if (myobj==^NULL)
Synergy DBL also supports the op_Explicit conversion operator method, as shown below:
Conversion operator method name |
Description |
Example syntax |
---|---|---|
op_Explicit |
Explicit type conversion |
obj1 = (class1)obj2 |
op_Explicit must have one argument that designates the source of a conversion and a return type to denote the destination for the conversion. Either the argument type or the return type must be the enclosing class type, but they cannot be within the same class hierarchy.
The example below converts from i4 to myclass:
public static method op_Explicit, @myclass p1, i4 proc mreturn new myclass(p1); end
The conversion operator method must be declared as PUBLIC and STATIC. You can declare multiple conversion operator methods within a class as long as their return types and argument types differ.
If you do an explicit conversion cast, the compiler performs the following steps in order.
- See if the class is in the class hierarchy. If so, cast it.
- If the class is not in the class hierarchy, and an op_Explicit() method whose argument is the source type and whose return type is the destination type exists in either the source or destination class, execute the op_Explicit method.
For example, if the above op_Explicit() were defined, it would be legal to invoke the explicit conversion operator using this syntax:
c1, @myclass x, i4, 5 proc c1 = (myclass)x end
Op_Explicit has the following restrictions:
- You cannot call op_Explicit explicitly. For example, the following will generate an error:
myclass.op_Explicit(5)
- You cannot use op_Explicit with both d and d., because the compiler interprets them as being the same and generates a “Class classname already defines a method op_Explicit with the same parameter types” error.
- In traditional Synergy, if you specify op_Explicit for d., you can’t cast an object as (d.); you must use (decimal) instead.
Resolving object operators
When resolving object operators, the compiler looks for the operators only in the direct classes involved in the operation; it does not search up the hierarchy for an appropriate operator that could work. For example, using the code below, the compiler will attempt to run the op_Explicit operation from class2. If class2 or class3 doesn’t have that operator, the compiler will not go up the class hierarchy looking for appropriate operators, but will instead generate an “Invalid cast operation” error, because the operator cannot resolve.
namespace ns1 class class1 public static method op_Explicit, @class3 parm1, @class1 proc . . . end endclass class class2 extends class1 parm1, @class2 proc . . . end endclass class class3 endclass
and
c2, @class2 c3, @class3 proc c2 = new class2() c3 = (class3)c2
Order of evaluation
The following table lists the numeric operators available in Synergy DBL. It is organized by left-to-right precedence, with the operators that have the highest precedence at the top.
Precedence |
Operator |
Description |
|
---|---|---|---|
1 |
(class), ^A, ^D, ^F, ^I |
Type conversion |
Cast |
2 |
array[dim] |
|
Array access |
function(arg) |
|
Function or method call |
|
3 |
+ |
Unary |
Positive |
– |
Unary |
Negative |
|
4 |
# and ## |
Binary |
Rounding |
5 |
<< and >> |
Binary |
Bitwise left shift and bitwise right shift (Synergy .NET only) |
6 |
* |
Binary |
Multiplication |
/ |
Binary |
Division |
|
// |
Binary |
Division implied |
|
.mod. |
Binary |
Modulo (Synergy .NET only) |
|
7 |
+ |
Binary |
Addition |
– |
Binary |
Subtraction |
|
8 |
.EQ. or == |
Binary |
Equal to |
.NE. or != |
Binary |
Not equal to |
|
.GT. or > |
Binary |
Greater than |
|
.LT. or < |
Binary |
Less than |
|
.GE. or >= |
Binary |
Greater than or equal to |
|
.LE. or <= |
Binary |
Less than or equal to |
|
.EQS. |
Binary |
String alpha equal to |
|
.NES. |
Binary |
String alpha not equal to |
|
9 |
.GTS. |
Binary |
String alpha greater than |
.LTS. |
Binary |
String alpha less than |
|
.GES. |
Binary |
String alpha greater than or equal to |
|
.LES. |
Binary |
String alpha less than or equal to |
|
.EQU. |
Binary |
Unsigned integer equal to |
|
.NEU. |
Binary |
Unsigned integer not equal to |
|
.GTU. |
Binary |
Unsigned integer greater than |
|
.LTU. |
Binary |
Unsigned integer less than |
|
.GEU. |
Binary |
Unsigned integer greater than or equal to |
|
.LEU. |
Binary |
Unsigned integer less than or equal to |
|
10 |
.NOT. or ! |
Unary |
Boolean NOT |
.BNOT. or ~ |
Unary |
Bitwise NOT |
|
11 |
.AND. or && |
Binary |
Boolean AND |
.BAND. or & |
Binary |
Bitwise AND |
|
.BNAND. |
Binary |
Bitwise NOT AND |
|
12 |
.OR. or || |
Binary |
Boolean OR |
.XOR. |
Binary |
Boolean exclusive OR |
|
.BOR. or | |
Binary |
Bitwise OR |
|
.BXOR. |
Binary |
Bitwise exclusive OR |
|
13 |
?? |
Binary |
Null coalescing (Synergy .NET only) |
14 |
?: |
Ternary |
Conditional |
15 |
= |
Binary |
Assignment |
+= |
Binary |
Assignment |
|
–= |
Binary |
Assignment |
|
*= |
Binary |
Assignment |
|
/= |
Binary |
Assignment |
|
|= |
Binary |
Assignment |
|
&= |
Binary |
Assignment |
When two or more operators of the same precedence (except for the assignment operator) are adjacent, they are evaluated from left to right. For example, A * B / C is evaluated by multiplying A by B and dividing the result by C.
How assignment operators affect order of evaluation
The presence of an assignment operator (=) causes an expression to be evaluated from right to left, which often mixes a left-to-right precedence order with a right-to-left precedence order and makes the order of evaluation quite confusing. In a right-to-left evaluation, the right side always has precedence over the left side, and the assignment operation occurs before the left side is evaluated.
For example, the expression
X + Y = 3 + Z
is evaluated as follows:
X + (Y = 3 + Z)
The expression 3 + Z is evaluated first, then Y is assigned to the result of 3 + Z, and finally X + Y is evaluated.
Likewise, the expression
X + Y = Z * 3 / I = 15
is evaluated as follows:
X + (Y = Z * 3 / (I = 15))
First it sets I to 15, then it divides 3 by I, multiplies the result of 3 / I by Z, sets Y to (Z * (3 / I)), and adds Y to X, in that order.
A balanced set of parentheses can clarify or alter the expression’s order of evaluation and enable you to make your expressions as complex as necessary. Use parentheses if you need to override the precedence order of the operations in your expression. Parentheses can also help make your code easier to read, because they clearly indicate which operations are evaluated first.
Here’s an example of when you’d want to override the normal precedence order. Let’s assume you want to use the following equation with variables that have the same data type:
If you wrote the expression as A + B / C – D, precedence rules would dictate that the division operation be performed first. Thus, the value would be calculated as if the function looked like this:
The only way to get the correct order of evaluation is to override the precedence rules with parentheses and write the expression as (A + B) / (C + D).
The expressions in the most deeply nested sets of parentheses are evaluated first. For example, in the following expression the G – H operation is evaluated first because it’s in the most deeply nested set of parentheses. D + E is evaluated next, followed by (F * (G – H)):
A + B * (C - (D + E) / (F * (G - H)) + I)
As another example, let’s assume the following variable values:
A = 10
B = 30
C = 5
D = 2
The following expressions will have the specified values:
Expression |
Value |
---|---|
A + B / C * D |
22 |
A + B / (C * D) |
13 |
(A + B) / (C * D) |
4 |
More examples of expressions
The examples in the table below assume that the data division contains the following information:
record d5 ,d5 ,12345 d53 ,d5.3 ,12.345 a6 ,a6 money ,d6 ,127654 y ,d3 ,-326 a ,p1 ,4 b ,d2 ,10 c ,d2 ,20 d ,i1 ,5 e ,d5.3 ,12.300
Expression |
Value |
---|---|
5/3 |
“ 1” |
5//3 |
“1.6666” |
5//3##–4 |
“1.6666” |
(5//3)#0 |
runtime error |
d5#0 |
“ 12345” |
d5#1 |
“ 1235” |
d5##1 |
“ 12350” |
(5//3)##–4 |
“1.6667” |
a+b– c |
– 6 |
a*d |
20 |
b/a |
2 |
b//a |
2.5 |
e/b |
1.23 |
b+c/d*a |
26 |
b+c/(d*a) |
11 |
(b+c)/(d*a) |
1 |
((b+c)/d)*a |
24 |
money#a |
13 |
y#2 |
-3 |
y#a |
0 |
y#1 |
-33 |
a .eq. 4 |
1 |
a .ne. 4 |
0 |
‘abc’ == ‘def’ |
0 |
a .eq. 4 .and. b .eq. 10 |
1 |
a .and. b |
1 |
a .and. 0 |
0 |