Language6 min read

Operators

Arithmetic, comparison, logical, bitwise, ternary, unary, and assignment operators in BWSL.

Reading Time
6 min
Word Count
903
Sections
12
Try It Live

Pressure-test the syntax

Take the concept from this page into the playground and deliberately break a pass, binding, or type signature to see how the compiler responds.

Try a Live Edit

BWSL provides the standard arithmetic and control operators used across the current compiler and regression suite.

Arithmetic Operators

Arithmetic operators perform mathematical operations on numeric values:

OperatorNameExampleDescription
+Additiona + bAdds two values
-Subtractiona - bSubtracts right from left
*Multiplicationa * bMultiplies two values
/Divisiona / bDivides left by right
%Moduloa % bRemainder after division
bwsl
float x = 10.0 + 5.0;
float y = 10.0 - 3.0;
float z = 4.0 * 2.5;
float w = 20.0 / 4.0;
int remainder = 17 % 5;

Integer Division and Remainder

Signed integer / truncates toward zero, and % gives the matching remainder a - b * (a / b). A non-zero remainder therefore has the sign of the dividend:

bwsl
int q = -7 / 2; // -3
int r = -4 % 3; // -1
int s = 4 % -3; // 1

This applies to integer vectors component-wise and to constants folded at compile time. For floats, use mod (floored, sign of the divisor) or fmod (truncated, sign of the dividend).

Vector Arithmetic

Arithmetic operators work component-wise on vectors:

bwsl
float3 a = float3(1.0, 2.0, 3.0);
float3 b = float3(4.0, 5.0, 6.0);
float3 sum = a + b;
float3 diff = b - a;
float3 prod = a * b;
float3 quot = b / a;

Scalar-vector operations broadcast the scalar to all components:

bwsl
float3 v = float3(2.0, 4.0, 6.0);
float3 scaled = v * 0.5;
float3 offset = v + 1.0;

Comparison Operators

Comparison operators return booleans:

OperatorNameExampleDescription
<Less thana < bTrue if left is less than right
>Greater thana > bTrue if left is greater than right
<=Less or equala <= bTrue if left is less than or equal to right
>=Greater or equala >= bTrue if left is greater than or equal to right
==Equala == bTrue if values are equal
!=Not equala != bTrue if values are not equal

Logical Operators

Logical operators combine boolean expressions:

OperatorNameExampleDescription
&&Logical ANDa && bTrue if both operands are true
||Logical ORa || bTrue if either operand is true
bwsl
bool inRange = (x >= 0.0) && (x <= 1.0);
bool outOfBounds = (x < 0.0) || (x > 1.0);

Short-circuit evaluation

For scalar operands, && evaluates its right operand only when the left is true, and || only when the left is false. Side effects in the skipped operand do not happen, so guards like i < n && values[i] > 0.0 are safe. && and || on bool vectors work component-wise and do not short-circuit.

When the right operand is short and has no side effects (comparisons, arithmetic, locals, swizzles), the compiler emits a plain logical operation instead of a branch. Operands that call your functions, index arrays, read through pointers, divide integers or shift keep the branch.

Bitwise Operators

Bitwise operators work on integer and integer-backed enum values. Using them, or the shift operators, on a float is a compile error; convert explicitly or use asuint first:

OperatorNameExampleDescription
&Bitwise ANDa & bKeeps bits set in both operands
|Bitwise ORa | bKeeps bits set in either operand
^Bitwise XORa ^ bKeeps bits set in exactly one operand
~Bitwise NOT~aInverts all bits
<<Left shift1u << 4uShifts bits left
>>Right shiftvalue >> 8uShifts bits right
bwsl
uint flags = 0u;
flags = flags | (1u << 0u);
flags = flags | (1u << 2u);
uint mask = 0xFFu;
uint value = 0x12345678u;
uint lowByte = value & mask;
uint nextByte = (value >> 8u) & mask;

Unary Operators

Unary operators take a single operand:

OperatorNameExampleDescription
!Logical NOT!aInverts a boolean value
-Negation-aNegates a numeric value
+Unary plus+aReturns the value unchanged
++Increment++a or a++Increases value by 1
--Decrement--a or a--Decreases value by 1
bwsl
bool visible = true;
bool hidden = !visible;
float value = 5.0;
float negated = -value;
int counter = 0;
counter++;
++counter;
counter--;

Prefix vs Postfix

The prefix form (++a) increments before returning the value, while the postfix form (a++) returns the original value before incrementing:

bwsl
int a = 5;
int b = ++a;
int c = 5;
int d = c++;

Ternary Operator

Use ?: for conditional selection:

bwsl
float brightness = isLit ? litValue : unlitValue;
float3 color = enabled ? baseColor : float3(0.0);

Only the selected arm is evaluated, so side effects in the other arm don't happen. Both arms must have the same type: c ? float3(1.0) : float2(1.0) is a compile error. Arms can be scalars, vectors, matrices, structs or arrays, and the condition can be a bool vector for component-wise selection. Pointer operands are not supported.

When both arms are short and free of side effects, the compiler emits a branch-free select. Calls to your own functions, array indexing, derivatives and texture reads keep the branch.

The same form is also used in compile-time stage selection:

bwsl
vertex = usePrimary ? primaryVertex() : fallbackVertex()

Assignment Operators

Assignment operators store values in variables:

OperatorNameExampleEquivalent
=Assignmenta = bAssigns value of b to a
+=Add assigna += ba = a + b
-=Subtract assigna -= ba = a - b
*=Multiply assigna *= ba = a * b
/=Divide assigna /= ba = a / b
%=Remainder assigna %= ba = a % b
&= |= ^=Bitwise assignflags |= bita = a | b
<<= >>=Shift assignv <<= 2ua = a << b

The target of a compound assignment, including any index expression, is evaluated once: a[next()] += 1 calls next() a single time.

Assignment targets must be writable: a variable, field, swizzle, array element or pointer dereference. Constants and the results of calls or other expressions cannot be assigned. Assigning a float to an int or uint variable truncates, the same as an explicit int(x) conversion.

bwsl
float total = 0.0;
total += 10.0;
total -= 3.0;
total *= 2.0;
total /= 7.0;

Compound assignment also works with vectors:

bwsl
float3 position = float3(0.0, 0.0, 0.0);
float3 velocity = float3(1.0, 0.5, 0.0);
position += velocity;
position *= 2.0;

Operator Precedence

Operators are evaluated in the following order, from highest to lowest precedence:

High
Low
1
++--
Postfix
L→R
2
!-+++--
Prefix/Unary
R→L
3
*/%
Multiplicative
L→R
4
+-
Additive
L→R
5
<<=>>=
Relational
L→R
6
==!=
Equality
L→R
7
&&
Logical AND
L→R
8
||
Logical OR
L→R
9
=+=-=*=/=
Assignment
R→L
bwsl
float result = 2.0 + 3.0 * 4.0;
bool check = x > 0.0 && y > 0.0;

When in doubt, use parentheses to make precedence explicit.

See Also