E08273: word 'not' is never an operand of another operator unless parenthesised, except 'and', 'xor' and 'or' - it binds below the comparisons, so write 'a == (not b)' or '$ (not b)', or 'not (a == b)' if that is meant; 'a and not b' needs none

Category: E08xxx Pre-IR checks (flow analysis) · Reported in phase: SYMBOL_DEFINITION · Classification: NOT_OPERAND_NEEDS_PARENTHESES · All error codes

What went wrong

Word 'not' is never an operand of another operator unless parenthesised, except 'and', 'xor' and 'or'. Here it is an operand of an operator that binds tighter than it, with no parentheses. Word 'not' binds below the comparisons, like Python and SQL, so on the LEFT it takes the whole comparison: 'not ready == done' is 'not (ready == done)'.

Read from the right, the same rule would make 'ready == not done' mean 'ready == (not done)' - a 'not' that does NOT reach past the '==' before it. The two places read differently, so EK9 (like Python) does not accept the second without parentheses: 'ready == not done' is this error.

The operators this applies to are every binary operator except 'and', 'xor' and 'or': '^', the coalescing operators '?:' '<?' '>?', '*' '/' 'mod' 'rem', '+' '-', '<<' '>>', '<=>' '<~>', '<' '<=' '>' '>=', 'matches', 'contains', 'in' (and their 'not' forms, and the start of 'x in a ... b'), and '==' '<>' '!='.

It applies to every PREFIX operator as well: '$$', '$', '#^', 'length', '#<', '#>', '#?', 'abs' and 'sqrt'. Each binds tighter than word 'not', so '$ not done' would be '$ (not done)' while 'not done == ready' is 'not (done == ready)' - the same trap written the other way round. Parenthesise the negation: '$ (not done)'. The exception is '~not done' and '-not done': '~' and unary '-' are themselves negations (the same operator as 'not'), so each is a negation of a negation (E08274) and parentheses would not mend it - '~(not done)' is E08274 too.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 2160 (What is EK9's operator precedence - does 'and' bind tighter than 'or', and where does 'not' sit?)

Each of these is E08273 ('~not done' and '-not done' are E08274 instead, a negation of a negation):

  • same <- ready == not done
  • under <- low < not high
  • chosen <- flag ?: not other
  • found <- ready in not wanted
  • text <- $not done //a prefix operator

Each of these is fine:

  • //Negate only the right operand
  • same <- ready == (not done)
  • //Negate the comparison (also written 'not ready == done')
  • different <- not (ready == done)
  • //'and', 'xor' and 'or' bind below 'not': one reading only
  • both <- ready and not done
  • either <- ready or not done
  • //A prefix operator over a parenthesised negation
  • text <- $ (not done)
  • //'~' is the tight negation: no parentheses needed
  • tight <- ready == ~done

How to fix it

  1. Parenthesise the negation to negate only that operand: 'ready == (not done)', '$ (not done)'.
  2. Or, if the whole comparison was meant to be negated, say so: 'not (ready == done)', or use the opposite operator: 'ready <> done'.
  3. '~' is the tight negation: 'ready == ~done' is 'ready == (~done)' with no parentheses needed.

Common causes

  • Writing 'a == not b' expecting 'a == (not b)', as C's '!' would give
  • Writing 'x < not y' while meaning 'not (x < y)'
  • Writing '$ not flag' or '#? not flag' to convert or hash the negation
  • Translating Python's 'a == (not b)' and dropping the parentheses

See also

  • E08273: word 'not' as an operand of another operator (this error)
  • E08269: a coalescing operator mixed with another operator without parentheses
  • E08274: a negation applied to a negation ('not not b', '~not b', '-not b', 'not (not b)')
  • E07620: an operator ('~' / 'not' included) that the operand's type does not define

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E08273: word 'not' is never an operand of another operator unless parenthesised, except 'and', 'xor' and 'or' - it binds below the comparisons, so write 'a == (not b)' or '$ (not b)', or 'not (a == b)' if that is meant; 'a and not b' needs none

#!ek9
defines module error.e08273.bad

  defines function

    sameAsNot()
      ->
        ready as Boolean
        done as Boolean
      <- rtn as Boolean?

      rtn: ready == not done

    otherForms()
      ->
        ready as Boolean
        done as Boolean
        low as Boolean
        high as Boolean
        flag as Boolean
        other as Boolean
        wanted as List of Boolean

      same <- ready == not done
      under <- low < not high
      chosen <- flag ?: not other
      found <- ready in not wanted
      text <- $not done //a prefix operator

Fixed: Parenthesise the not - or the comparison, if that is what is meant

#!ek9
defines module error.e08273.good

  defines function

    sameAsNot()
      ->
        ready as Boolean
        done as Boolean
      <- rtn as Boolean?

      rtn: ready == (not done)

    notSame()
      ->
        ready as Boolean
        done as Boolean
      <- rtn as Boolean?

      rtn: not (ready == done)

Fixed: Parenthesise the negation, negate the whole comparison, or use 'and'/'or'

#!ek9
defines module error.e08273.good.forms

  defines function

    readings()
      ->
        ready as Boolean
        done as Boolean
      <- rtn as String?
      //Negate only the right operand
      same <- ready == (not done)
      //Negate the comparison (also written 'not ready == done')
      different <- not (ready == done)
      //'and', 'xor' and 'or' bind below 'not': one reading only
      both <- ready and not done
      either <- ready or not done
      //A prefix operator over a parenthesised negation
      text <- $ (not done)
      //'~' is the tight negation: no parentheses needed
      tight <- ready == ~done
      rtn: `${same} ${different} ${both} ${either} ${text} ${tight}`

Questions and answers

These Q&A entries show E08273 and its fix: