Operators

Scott Waldron 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;
var name = 'John ' + 'Doe';

-

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;
from System import object;
from Nature import Animal;

B.foo(5);

var o = object();
var hash = o.GetHashCode();

var dino = Animal();
dino.Name = "T-Rex";
dino.IsCarnivore = true;
var n = dino.Name;

?.

Null-safe member access for the member to the left of the '?'

var x = null;
var a = x.Num; // oops
var b = x?.Num; // ok, null

[]

Indexing operator for accessing a member of an object by an index key

var a = array(1,2);
var x = a[0]; // 1
a[0] = 3;

var d = dict('a':1, 'b':2);
var v1 = d['a']; // 1
var v2 = d['b']; // 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);
var x = a[10]; // oops
var y = arr?[10]; // null
a?[0] = 3; // ok
a?[10] = 3; // ignored

var d = dict('a':1, 'b':2);
var v1 = d['c']; // oops
var v2 = d?['c']; // null
d?['a'] = 3; // replace value
d?['c'] = 3; // add value
v2 = d?['c']; // 3

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;
person.Name = 'Sam';
someList[0] = 4;

+=

Add the RHS value from the LHS. Also used to add an event handler delegate.

x += 5;
str += 'more';
def handler(sender, e) {...}
obj.Modified += handler;

-=

Subract the RHS value form the LHS. Also used to remove an event handler delegate.

x -= 5;
def handler(sender, e){...}
obj.Modified -= handler;

*=

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++;
var x = 10;
var y = x++; // y == 10, x == 11

--

Postfix decrement. Decrease LHS by 1.

a--;
var x = 10;
var y = x--; // y == 10, x == 9

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
var y = 1 and 3; // true

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
var y = 1 or 3; // true
var z = 0 or null; //false

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
var y = 1 xor 3; // false
var z = 0 xor null; // false

not

Check RHS, returns true if RHS is falsy, false if RHS is truthy. Not is a unary operator.

var x = not 0; // true
var y = not true; // false
var z = not null; // 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'
var y = 0 ? 5 : 10; // 10

??

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
var b = null ?? 4? // 4
var c = null ?? null; // null

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
var t2 = T.Opt1 & T.Opt2; // 0
var t3 = T.All & ~T.Opt2; // Opt1, Opt3

|

Bitwise OR

var t1 = T.Opt1 | T.Opt2; // Opt1, Opt2
var t2 = T.All | T.Opt1 // T.All

~

Bitwise NOT

var t1 = T.All & ~T.Opt1; // Opt2, Opt3

&=

Bitwise AND reassignment

var t1 = T.All;
t1 &= ~T.Opt2; // Opt1, Opt3
|

|=

Bitwise OR reassignment

var t1 = T.Opt1;
t1 |= T.Opt3; // Opt1, Opt3

How did we do?

Dino Script™ Table of Contents

Concepts

Contact