Operators
Updated by Scott Waldron
All computer languages define operators. There are two basic kinds of operators: binary (very common), and unary (less common). A binary operator has a left-hand side (LHS) expression and a right-hand side (RHS) expression. A unary operator has only one "side", which could be on the left or right.
In addition to these two basic types, operators can be classed into categories, as shown in the following tables.
Comparison Operators
Operator | Purpose | Example |
== | Compare LHS and RHS for equality. | if a == b {...} |
!= | Compare LHS and RHS for inequality. | if a != b {...} |
> | Compare LHS as greater than RHS. | if a > b {...} |
< | Compare LHS as less than RHS. | if a < b {...} |
>= | Compare LHS as greater than or equal to RHS. | if a >= b {...} |
<= | Compare LHS as less than or equial to RHS. | if a <= b {...} |
Arithmetic Operators
Operator | Purpose | Example |
+ | Addition / concatenation | var b = a + 5; |
- | Subtraction | var b = a - 5; |
- | Unary minus (negation) | var b = -a; |
* | Multiplication | var b = a * 5; |
/ | Division | var b = a / 5; |
% | Modulo (remainder) | var b = a % 5; |
Member Access Operators
In object-oriented programming, member access is when we access some method, field, or property of an object and retrieve or set its value.
Operator | Purpose | Example |
. | Member access. The expression on the LHS represents the object that contains the member. The name on the RHS is the function, public method, public property, public field, public nested type, etc. that is defined in the LHS code. | import script "B.dino" as B; |
?. | Null-safe member access for the member to the left of the '?' | var x = null; |
[] | Indexing operator for accessing a member of an object by an index key | var a = array(1,2); |
?[] | Missing key index-safe indexing operator. This operator is safe to use for virtually any member access. It will even safely get the values of built-in properties on objects, or just get null if the key or property doesn’t exist. | var a = array(1,2); |
Special Member Access Circumstances
The ?. member access operator always means "if the left-hand side of the expression is null, just return null or ignore the operation." However, in certain circumstances it has an additional behavior: If the LHS is a dictionary with a string indexer or a dynamic object, it also means "if no member or index with key can be found, return null."
var d = dict[string, int]('One':1, 'Two':2);
var v1 = d?.One; // 1
var v2 = d?.Two // 2
var err = d.Three; // error, no such index (note missing '?')
var v3 = d?.Three; // null, same as d?['Three']
d?.Three = 3; // set a new value
v3 = d?.Three; // 3
var dyna = dynamic();
dyna?.One = 1;
dyna?.Two = 2;
var y1 = dyna?.One; // 1
var y2 = dyna?.Two // 2
var yerr = dyna.Three; // error, no such field (not missing '?')
var y3 = dyna?.Three; // null, same as dyna?['Three'];
dyna?.Three = 3; // set a new value
y3 = dyna.Three; // 3
Note that this special syntax meaning has no effect on non-dictionary or non-dynamic objects. If that were the case, missing properties of objects would simply be ignored, burying a host of useful error messages.
var str = 'a string';
var oops = str?.Whatever; //error, string has no member 'Whatever'
However, notice that this will not cause an error.
var nothing = null;
var no_op = nothing?.Whatever; // LHS is null, so Whatever is never evaluated
If you really want to just say "if this object can get or set a value of this name, then do it, otherwise ignore it", there is a way to almost ensure that the operation will not throw an error: use the ?[] indexing operator.
var str = 'a string';
var v1 = str?['Whatever']; // null
var len = str?['Length']; // 8 (length of 'a string'), same as str.Length
var c1 = str?[0]; // 'a', the first char of the string
Assignment Operators
Assignments in Dino are statements, not expressions. The only exception to this is the postfix increment/decrement operators, which return the original value. Forcing assignments to be statements rather than expressions takes away a bit of flexibility but has tremendous benefits in preventing a whole host of errors.
Possibly the most common logic error is show below.
var x = 10;
if x = 20 { // error: assignment
}
if x == 20 { // comparison (==): correct
}
The worst thing about this error is that it is potentially destructive because of side-effects. Imagine looping through a list of database records and typing the following.
each record in myList{
if record.Name = 'Test' { // You just changed the name of every record
}
}
By enforcing that an assignment must be a statement (on its own line, terminated by a semicolon), Dino prevents this error because the above syntax is wrong and will not build.
Operator | Purpose | Example |
= | Assign a value to a symbol or other LHS statement | var x = 10; |
+= | Add the RHS value from the LHS. Also used to add an event handler delegate. | x += 5; |
-= | Subract the RHS value form the LHS. Also used to remove an event handler delegate. | x -= 5; |
*= | Multiply the LHS value with the RHS | x *= 5; |
/= | Divide the LHS value with the RHS | x /= 5; |
++ | Postfix increment. Increase LHS by 1. | a++; |
-- | Postfix decrement. Decrease LHS by 1. | a--; |
Note that Dino does not define prefix increment/decrement operators.
Logical Operators
Operator | Purpose | Example |
and | Check LHS and RHS, return true if both LHS and RHS evaluate to truthy. And expressions are short-circuit evaluated: if the LHS is false, the RHS does not need to be evaluated. | var x = 0 and 1; // false |
or | Check LHS and RHS, return true if either LHS or RHS evaluates to truthy. Or expressions are short-circuit evaluated: if the LHS is true, the RHS does not need to be evaluated. | var x = 0 or 1; // true |
xor | Check LHS and RHS, return true if one and only one of LHS and RHS evaluates to truthy. Xor expressions cannot be short-circuit evaluated. | var x = 0 xor 1; // true |
not | Check RHS, returns true if RHS is falsy, false if RHS is truthy. Not is a unary operator. | var x = not 0; // true |
?: | Ternary operator, written as expr ? expr : expr. Return the middle expr if LHS is truthy, other if LHS is falsy, return the RHS expr. The ternary operator is a ternary (three-place) expression. | var x = true ? 'yes' : 'no'; // 'yes' |
?? | Null-coalescing operator. Return LHS if LHS is non-null, otherwise RHS. Note that this operator only works with null values, it does not follow the "truthiness" tests of the other logical operators. Short-circuit evaluated. | var a = 3 ?? 4; // 3 |
Bitwise Operators
Somewhere in C# land...
[Flags]
public enum TestEnum
{
None = 0,
Opt1 = 0x0001,
Opt2 = 0x0010,
Opt3 = 0x0100,
All = Opt1 | Opt2 | Opt3
}
And in the Dino code...
from MyC#Code import TestEnum as T;
Operator | Purpose | Example |
& | Bitwise AND | var t1 = T.All & T.Opt1; // T.Opt1 |
| | Bitwise OR | var t1 = T.Opt1 | T.Opt2; // Opt1, Opt2 |
~ | Bitwise NOT | var t1 = T.All & ~T.Opt1; // Opt2, Opt3 |
&= | Bitwise AND reassignment | var t1 = T.All; |
|= | Bitwise OR reassignment | var t1 = T.Opt1; |