Expressions

Grouping, Postifix, and Unaries.

primary-expr % primary
: "(" expressions-list ")" % paren
| identifier % ident
| constant % const
;
postfix-expr % postfix
: primary-expr % degenerate
| postfix-expr "=?" primary-expr % nullcoalesce
| postfix-expr "[" assign-expr "]" % indirect
| postfix-expr "." identifier % member
| postfix-expr "++" % inc
| postfix-expr "--" % dec
| function-call % funccall
| object-notation % objdef
;

function-call % funccall
: postfix-expr "(" ")" % noarg
| funccall-start-nocomma ")" % somearg
;

funccall-start-nocomma % funcinvokenocomma
: postfix-expr "(" assign-expr % base
| funccall-start-nocomma "," assign-expr % genrule
;

Note: Previously, the close-binding null-coalescing operator was ->, this was changed as it had been desired to reserve it for a 'trait' static call syntax where the first argument of a subroutine (i.e. non-method function) receives the value of or a reference to the left-hand of the operator. This is tentative and no commitment over this had been made yet. All in all, the close-binding null-coalescing operator is now =?. (Note dated 2025-09-26.)

unary-expr % unary
: postfix-expr % degenerate
| "++" unary-expr % inc
| "--" unary-expr % dec
| "+" unary-expr % positive
| "-" unary-expr % negative
| "~" unary-expr % bitcompl
| "!" unary-expr % logicnot
;

For inc and dec in unary and postfix, and positive and negative, operation are computed under arithmetic context. For bitcompl and logicnot, the operation are computed under integer context.

Arithmetic Binary Operations

mul-expr % mulexpr
: unary-expr % degenerate
| mul-expr "*" unary-expr % multiply
| mul-expr "/" unary-expr % divide
| mul-expr "%" unary-expr % remainder
;

The result of division on integers SHALL round towards 0.

The remainder computed SHALL be such that (a/b)*b + a%b == a is true.

If the divisor is 0, then the quotient of division becomes positive/negative infinity of type double if the sign of both operands are same/different, while the remainder becomes NaN, with the "invalid" floating point exception signalled.

For the purpose of determining the sign of operands, the integer 0 in ulong and two's complement signed long are considered to be positive.

Editorial Note: The first 3 of the above 4 paragraphs were together 1 paragraph in a previous version of the draft before 2025-08-25. This had the potential of causing the confusion that remainder is only applicable to integers. Because now remainder is also applicable to floating points, this is first separated into its own paragraph. The rule regarding type conversion on division by 0 is of separate interest, so it's also an individual paragraph now. The 4th paragraph is added on 2025-08-25.

Note: The condition for determining remainder is equivalent to:

remainder x % y shall be such x-ny such that for some integer n, if y is non-zero, the result has the same sign as x and magnitude less than that of y.

These are separate descriptions for integer modulo operator and floating point fmod function in the C language, as such, an implementation may utilize these facilities in C. Any inconsistency between these 2 definitions in C are supposedly unintentional from the standard developer's perspective.

All of mulexpr are computed under arithmetic context.

add-expr % addexpr
: mul-expr % degenerate
| add-expr "+" mul-expr % add
| add-expr "-" mul-expr % subtract
;

All of addexpr are computed under arithmetic context.

Bit Shifting Operations

bit-shift-expr % shiftexpr
: add-expr % degenerate
| bit-shift-expr "<<" add-expr % lshift
| bit-shift-expr ">>" add-expr % arshift
| bit-shift-expr ">>>" add-expr % rshift
;

All of shiftexpr are computed under integer context.

Side Note: There was left and right rotate operators. Since there's only a single 64-bit width in native integer types, bit rotation become meaningless. Therefore those functionalities will be offered in the standard library method functions.

Arithmetic Relations

rel-expr % relops
: bit-shift-expr % degenerate
| rel-expr "<" bit-shift-expr % lt
| rel-expr ">" bit-shift-expr % gt
| rel-expr "<=" bit-shift-expr % le
| rel-expr ">=" bit-shift-expr % ge
;

All of the ordering relations of relops are evaluated under arithmetic context. If either operand is NaN or null, then the value of the expression is false.

eq-expr % eqops
: rel-expr % degenerate
| eq-expr "==" rel-expr % eq
| eq-expr "!=" rel-expr % ne
| eq-expr "===" rel-expr % ideq
| eq-expr "!==" rel-expr % idne
;

Details of Loose and Strict Equality and Ordering Relation Comparison

To evaluate whether two operands are equal:

To evaluate the ordering relation of 2 operands:

Note: The equals() method is never used for ordering relations including the <= and the >= operators because an object that's missing cmpwith() has no reasonable definition of ordering relations. Conversely, the cmpwith() method is not used with the strict equality test, because the ordering of objects doesn't necessarily reflect their identity.

Bitwise Operations

bit-and % bitand
: eq-expr % degenerate
| bit-and "&" eq-expr % bitand
;

bit-xor % bitxor
: bit-and % degenerate
| bit-xor "^" bit-and % bitxor
;

bit-or % bitxor
: bit-xor % degenerate
| bit-or "|" bit-xor % bitor
;

All of the bitwise operations are computed under integer context.

Boolean Logics

logic-and % logicand
: bit-or % degenerate
| logic-and "&&" bit-or % logicand
;

logic-or % logicor
: logic-and % degenerate
| logic-or "||" logic-and % logicor
| logic-or "??" logic-and % nullcoalesce
;

Compounds

cond-expr % tenary
: logic-or % degenerate
| logic-or "?" expressions-list ":" cond-expr % tenary
;
assign-expr % assignment
: cond-expr % degenerate
| unary-expr "=" assign-expr % directassign
| unary-expr "*=" assign-expr % mulassign
| unary-expr "/=" assign-expr % divassign
| unary-expr "%=" assign-expr % remassign
| unary-expr "+=" assign-expr % addassign
| unary-expr "-=" assign-expr % subassign
| unary-expr "<<=" assign-expr % lshiftassign
| unary-expr ">>=" assign-expr % arshiftassign
| unary-expr ">>>=" assign-expr % rshiftassign
| unary-expr "&=" assign-expr % andassign
| unary-expr "^=" assign-expr % xorassign
| unary-expr "|=" assign-expr % orassign
;

See 9.2. Object/Value Key Access for further discussion.

expressions-list % exprlist
: assign-expr % degenerate
| expressions-list "," assign-expr % exprlist
;