What is EK9's operator precedence - does 'and' bind tighter than 'or', and where does 'not' sit?
← Operators and Expressions · Ref: Q2160
EK9 follows the convention of C, Java and Python. Tightest first: the postfix operators and the prefixes $ $$ #^ #< #> #? length abs sqrt ~ and 'is empty'; then '^' (right-associative, so 2 ^ 3 ^ 2 is 512); then unary minus (so -x ^ 2 is -(x ^ 2), and -2 ^ 2 is -(2 ^ 2) = -4.0 too - write (-2) ^ 2 to square the negative number); then the one coalescing level ?: ?? <? <=? >? >=?; then * / mod rem; + -; << >>; <=> <~>; then < <= > >= matches contains in and 'in a ... b'; then == <> !=; then word 'not'; then 'and'; then 'xor'; then 'or'; and the ternary last. So 'a or b and c' is 'a or (b and c)', 'name in names == expected' compares the membership, 'valid and age in 18 ... 65' is 'valid and (age in 18 ... 65)', and 'not age == limit' and 'not name in names' negate the whole test, as in Python. '~' applies to its operand alone, so '~name in names' asks a String for '~' (E07620). Word 'not' is never an operand of another operator unless parenthesised, except 'and', 'xor' and 'or' (E08273): read from the right it would NOT reach past an operator before it, so 'ready == not done' is a compile error, as in Python - write 'ready == (not done)', or 'not (ready == done)' - and every prefix operator binds tighter than it, so '$ not done' is an error too - write '$ (not done)'; word 'not', '~' and unary '-' are the same negation, so a negation applied to a negation cancels out and is E08274, in any mix of spellings and parenthesised or not ('not not done', 'not (not done)', '~~done', '~not done', '- -x', '-(-x)') - write 'done', or a single 'not done' (subtraction is not a negation: '-(a - b)' and 'a - -b' are fine); 'ready and not done' needs no parentheses because 'and', 'xor' and 'or' bind below 'not'. A coalescing operator ('??' included) mixed with any other binary operator needs parentheses (E08269): '(first ?? second) + third', never 'first ?? second + third'.
Example
defines module qa.operators.precedence defines constant low <- 18 high <- 65 defines function flagOf() -> answer as Boolean <- rtn as Boolean: answer numberOf() -> amount as Integer <- rtn as Integer: amount defines program OperatorPrecedenceDemo() stdout <- Stdout() yes <- flagOf(true) no <- flagOf(false) //'and' binds tighter than 'or': this is 'yes or (no and no)'. either <- yes or no and no stdout.println(`yes or no and no: ${either}`) //'^' associates right: 2 ^ (3 ^ 2). two <- numberOf(2) three <- numberOf(3) power <- two ^ three ^ two stdout.println(`2 ^ 3 ^ 2: ${power}`) //Unary minus binds below '^': -(x ^ 2). negated <- -three ^ two stdout.println(`-x ^ 2 with x = 3: ${negated}`) //The range test is a comparison: valid and (age in low ... high). age <- numberOf(30) inRange <- yes and age in low ... high stdout.println(`valid and age in low ... high: ${inRange}`) //'in' binds tighter than '==': (name in names) == expected. names <- ["Steve", "Stephen"] name <- "Steve" member <- name in names == yes stdout.println(`name in names == expected: ${member}`) //Word 'not' binds below the comparisons: not (age == low), not (name in names). notLow <- not age == low stdout.println(`not age == low: ${notLow}`) absent <- not name in names stdout.println(`not name in names: ${absent}`) //A negative literal is no exception to unary minus binding below '^': -(2 ^ 2). literal <- -2 ^ 2 stdout.println(`-2 ^ 2: ${literal}`) //A coalescing operator mixed with another operator is parenthesised (E08269). total <- (three ?? two) + two stdout.println(`(x ?? y) + y: ${total}`) //Word 'not' as an operand of '==' is parenthesised (E08273). same <- yes == (not no) stdout.println(`yes == (not no): ${same}`) //Word 'not' as the operand of a prefix operator is parenthesised too (E08273). text <- $ (not no) stdout.println(`string of (not no): ${text}`) //A negation of a negation, parenthesised or not, cancels out: write the value itself (E08274). plain <- no stdout.println(`no without a double negation: ${plain}`)
Common mistakes
E07620 — '~' applies to 'name' alone and a String has no '~' operator. Word 'not' binds below the comparisons, so 'not name in names' negates the membership test. See ek9 -h E07620 for details.
Incorrect:
absent <- ~name in names
Correct:
absent <- not name in names
E08269 — '??' is on the one coalescing level, which binds tighter than '+', so the unparenthesised form would be '(three ?? two) + two' - and other languages read it the other way. Mixing a coalescing operator with another binary operator needs parentheses. See ek9 -h E08269 for details.
Incorrect:
total <- three ?? two + two
Correct:
total <- (three ?? two) + two
E08273 — Word 'not' binds below the comparisons, so on the right of '==' it would negate only 'no' while on the left it negates the whole comparison. Parenthesise the negation, or write 'not (yes == no)'. See ek9 -h E08273 for details.
Incorrect:
same <- yes == not no
Correct:
same <- yes == (not no)
E08273 — Word 'not' is never an operand of another operator unless parenthesised, except 'and', 'xor' and 'or'. '$' binds tighter than word 'not', so '$ not no' would convert only the negation - the same trap as 'yes == not no' written the other way round. Parenthesise the negation: '$ (not no)'. See ek9 -h E08273 for details.
Incorrect:
text <- $ not no
Correct:
text <- $ (not no)
E08274 — Word 'not', '~' and unary '-' are the same negation, so two of them cancel out and 'not not no' is just 'no'. EK9 cannot know whether the value or a single negation was meant, so it does not choose: write 'no', or a single 'not no'. See ek9 -h E08274 for details.
Incorrect:
plain <- not not no
Correct:
plain <- no
E08274 — Parentheses group, they do not change what is negated: 'not (not no)' is still a negation of a negation, and still just 'no'. Write the value, or a single 'not no'. See ek9 -h E08274 for details.
Incorrect:
plain <- not (not no)
Correct:
plain <- no
Other ways to ask this
- Is 'a or b and c' read as 'a or (b and c)' in EK9?
- Does 'not x == y' negate the comparison or x?
- Is '^' right-associative in EK9?
- What does '-x ^ 2' evaluate to in EK9?
- Operator precedence table for EK9
- Why is 'a == not b' a compile error in EK9?
- Is '-2 ^ 2' equal to 4 or -4 in EK9?
- Why is '$ not b' a compile error in EK9?
Coming from another language?
C/Java: '&&' over '||', '!' binds to its operand - EK9's 'not' binds below the comparisons like Python's 'not' and SQL's NOT, and '~' plays the part of '!'. Python: identical for and/or/not and for '**' (right-associative, above unary minus). Java has no power operator; Math.pow(-x, 2) is what EK9 writes as (-x) ^ 2.
Keywords: exponent, logical and, operator order, associativity, xor, logical not, logical or, precedence