Home > Software design >  Why is logical not after an addition syntactically invalid?
Why is logical not after an addition syntactically invalid?

Time:03-08

Today, I noticed that not True 5 is syntactically valid but 5 not True isn't. The syntax error points to the first character of not, as if the operator couldn't be used here. However, 5 (not True) works, which leads me to believe it is a parser issue.

Are there any reasons for this error?

  • Is it a design decision taken by the language?
  • Is it is a parser error, or a behaviour not defined by the language's rules?

I couldn't find any resources concerning this case.

CodePudding user response:

It's an operator precedence issue; see e.g. this table in the documentation. binds more tightly than not, so this:

not True   5

Is equivalent to:

not (True   5)

And True evaluates as 1 in this case (so you get True 5 == 6, and not 6 == False). On the other hand:

5   not True

Is:

(5   not) True

Which doesn't make any sense. You would need to explicitly write:

5   (not True)

(Which would equal 5, since not True is False and False evaluates to 0.)

CodePudding user response:

We need to look at the Python grammar to see why not True 5 is syntactically (though not semantically) valid while 5 not True is a parse error. Here are the most relevant portions:

Relevant grammar rules
inversion:
    | 'not' inversion 
    | comparison

comparison:
    | bitwise_or
bitwise_or:
    | bitwise_xor
bitwise_xor:
    | bitwise_and
bitwise_and:
    | shift_expr
shift_expr:
    | sum

sum:
    | sum ' ' term 
    | term

The order of these rules matches precedence of the various operators involved, from lowest precedence to highest. 'not' inversion is listed before sum ' ' term because the not operator has lower precedence than .

not <expr> is parsed as an inversion. Reading top-down following the chain of derivations, you can see that an inversion could be a sum. This means that not True 5 can be parsed as the not operator followed by an operand of True 5.

Parse tree for not True 5
inversion
│       │
'not'   inversion
        │
        comparison
        │
        bitwise_or
        │
        bitwise_xor
        │
        bitwise_and
        │
        shift_expr
        │
        sum
        │
        sum ' ' term
        │       │
        term    '5'
        │
        'True'

On the other hand if you have 5 not True there is no way to construct a parse tree. If you tried, 5 not True would be parsed as sum ' ' term. The left side is fine: 5 can be parsed as a sum. There's no valid parse for the right side, though: not True is not a term.

Failed parse tree for 5 not True
sum ─┬────┐
│    │    │
sum  ' '  term
│         │
term      <error: no derivation for 'not' 'True'>
│
'5'

NB: The only way to work back "up" the grammar is to add parentheses. not True is not a term, but (not True) with parentheses is. 5 (not True) is syntactically valid. Of course, it's still semantically invalid. But hey, if you don't care about semantics the parser won't stop you.

  • Related