# Operators & Operations

To keep this concise, only exceptions from other C-like languages are listed. Operator precedence is different from C in some places, but will not be included here at the moment.

Note that many of these operators also support the `<op>=`

form, such as `~=`

for appending.

*Note that in all cases, each operand is only evaluated once, regardless of how many times it appears within “equivalent to”.*

```
// ternary shorthands
a ?: b // boolify(a) ? a : b
a ?? b // a !is null ? a! : b
// null handling
a! // `if(a is null) error()`; type is `remove_nullable(typeof(a))`
a?.b // `a !is null ? a.b : null`; type is `typeof(a.b)?`
// arithmetic
a ** b // a to the power of b (TBD: semantics when both are *signed* integers?)
a %% b // feature TBD; modulo operation (`a % b` is remainder), which is to say: `a%b`, but with the sign of `b`. Example use case: `arr[(a - 1) %% length]`
a .* b // feature TBD; a different type of product (e.g. dot product for vectors?)
// shifts (most languages with separate shifts do this)
a >> b // arithemetic shift (when `a` is signed)
a >>> b // logical shift
// bit-rotations were considered, but dropped
// miscellaneous
a is b // identity test
a !is b
a in b // membership test
a !in b
a ~ b // concatenation
// comparisons (work mathematically, to allow "chaining")
min <= x < max // does the mathematically-expected: `(min <= x) && (x < max)`
a == b != c // also works, equivalent to `(a == b) && (b != c)`
a == b <= c != d > e // a more complex example that mixes both of above
// the "spaceship operator" is supported
a <=> b // returns `a<b ? -something : a>b ? +something : 0`; *may* be forced to be -1/+1/0 by compiler (to allow for things like `arr[(a<=>b)+1]`), this is TBD
a <=> b <=> c // feature TBD, as it looks confusing. Equivalent to `a<=>b ?: b<=>c`, except `b` is evaluated once. See also: `?:` operator above.
// feature TBD (Is there a good way to check this automatically, but without huge performance drops? Even if we end up only allowing this on explicitly `discardable` items [which might not be a bad idea anyways])
discard x; // Invokes opDiscard operator overload (if present), which may abort it; calls destructor if not aborted. Object is then considered dead. Doubles as a hint to the garbage collector that it can free some related memory (if applicable). GC may ignore the hint, depending on implementation.
```

## Array Operators

There is also a number of array operators available. Out of bounds indexing, including negative indices or out-of-bounds slices are an error.
Slices use half-open ranges, meaning the index end of the slice is *not* included in the input. Perhaps the best way to represent this mentally is to view the slice indices are numbers that exist *between* array members (starting with 0 at the very start of array).

*Note that array slices do not copy the data: They are merely views. (this is TBD, but I’d really like to preserve it)*

```
int[] arr;
int[][] arrJagged;
int[,] arr2D;
// indexing
arr[3]
arrJagged[3][4]
arr2D[3,4]
// in simple arrays, both `length` and `$` are a shorthands for `currentArray.length`; the following are equivalent:
arr[arr.length-1]
arr[length-1]
arr[$-1]
// in multidimensional arrays, `length` is an array (per dimension); `$` contains the length of the *current dimension*; `$n` contains the length of `n-th dimension`; the following are equivalent:
arr2D[0..length[1],0..length[1]]
arr2D[0..$1,0..$1]
arr2D[0..$1,0..$]
// slicing (uses half-open ranges)
arr[3..5] // a view of [arr[3], arr[4]]
arr[5..$] // arr[5] to the end
arrJagged[3][4..6] // TBD: arrJagged[3..5][4..6] may or may not be supported
arr2D[3..5,4..6]
// blank slices represent the full slice; the following two lines are equivalent:
arr[0..$]
arr[]
// empty slices
arr[0..0] // always works, always empty
arr[5..5] // may be an error if `arr.length < 5`
// errors
arr[-1..0] // ERROR: start index out of bounds
arr[0..$+1] // ERROR: end index out of bounds
arr[5..3] // ERROR: end index must be smaller or equal to start (TBD: Should we allow this? If we do, should this be an empty slice, or should it be a reversed result?)
arr[0..-1] // ERROR: (either end index out of bounds, or end must be <= start; TBD, and it doesn't really matter)
```

### Array Casts

*Feature TBD: It does encode a requirement for a specific memory arrangement and it disables a bunch of aliasing optimizations, but it is a powerful feature. Plus there’s the general language complexity concerns.*

Because arrays are views, multidimensional arrays can be cast to single-dimensional ones, and vice-versa.

```
int[,] arr2D;
// view the entire array; this is just like `int arr[2][3]; int* a = (int[])arr;` in C, but without the undefined behavior
var a = bitcast(int[])arr2D;
assert(a.length == arr2D.length[0] * arr2D.length[1]);
if(a.length > 0)
{
assert(a[0] == arr2D[0,0]);
assert(a[1] == arr2D[0,1]); // TBD: this depends on storage order of arr2D, but I think this order makes sense (as it matches C int[N][M])
}
```

## Bitcasts

Note the use of `bitcast`

above. This is a special operator that allows casting some POD types to others. It is a safe equivalent of C++'s `reinterpret_cast`

. Note that syntax is not yet 100% determined (will either be this or generics-based).

```
// scalars
float f = 3.2f;
int a = bitcast(int)f; // okay (but value of `a` is implementation-defined)
long a = bitcast(long)f; // error: types are of different size
// arrays
int[] arr = [1, 2, 3];
byte[] barr = bitcast(byte[])arr; // okay (but endianness is implementation-defined)
assert(barr.length == arr.length * 4);
assert(barr[0] == arr[0] || barr[3] == arr[0]); // which one is true depends on endianness
SomeClass x = new SomeClass;
var xID = bitcast(uintword)x; // TBD. Either an error ("unsafe bitcast") *or* okay, but only in this direction, and typically yielding a pointer (in which case, it's equivalent to Python's `id(x)`)
```