[everything is this response is from 3.12.2]

AFAICT flat `x or y or z`

is **compiled** in 3.12 same `x or (y or z)`

.

```
Python 3.12.2 (main, Mar 12 2024, 11:02:14) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>> dis.dis('x or (y or z)')
0 0 RESUME 0
1 2 LOAD_NAME 0 (x)
4 COPY 1
6 POP_JUMP_IF_TRUE 8 (to 24)
8 POP_TOP
10 LOAD_NAME 1 (y)
12 COPY 1
14 POP_JUMP_IF_TRUE 3 (to 22)
16 POP_TOP
18 LOAD_NAME 2 (z)
20 RETURN_VALUE
>> 22 RETURN_VALUE
>> 24 RETURN_VALUE
```

That looks like a sensible optimization to me — ignoring “broken” objects, `or`

is **associative**:

```
>>> for x,y,z in itertools.product([0,1], repeat=3):
... print(x,y,z, '(x or y) or z =', (x or y) or z, 'x or (y or z) =', x or (y or z))
...
0 0 0 (x or y) or z = 0 x or (y or z) = 0
0 0 1 (x or y) or z = 1 x or (y or z) = 1
0 1 0 (x or y) or z = 1 x or (y or z) = 1
0 1 1 (x or y) or z = 1 x or (y or z) = 1
1 0 0 (x or y) or z = 1 x or (y or z) = 1
1 0 1 (x or y) or z = 1 x or (y or z) = 1
1 1 0 (x or y) or z = 1 x or (y or z) = 1
1 1 1 (x or y) or z = 1 x or (y or z) = 1
```

- Same for
`(x and y) and z`

vs. `x and (y and z)`

.
- However mixing is NOT associative e.g.
`(x or y) and z`

!= `x or (y and z)`

.

But it’s not enough to be “sensible optimization”; Python strives for predictable behavior, so does this match **parser**, and does this match official **grammar**?

### Well, AST surprised me

it’s neither this associativity nor that, but a single flat “BoolOp with 3 children” :

```
>>> print(ast.dump(ast.parse('(x or y) or z'), indent=2))
Module(
body=[
Expr(
value=BoolOp(
op=Or(),
values=[
BoolOp(
op=Or(),
values=[
Name(id='x', ctx=Load()),
Name(id='y', ctx=Load())]),
Name(id='z', ctx=Load())]))],
type_ignores=[])
>>> print(ast.dump(ast.parse('x or (y or z)'), indent=2))
Module(
body=[
Expr(
value=BoolOp(
op=Or(),
values=[
Name(id='x', ctx=Load()),
BoolOp(
op=Or(),
values=[
Name(id='y', ctx=Load()),
Name(id='z', ctx=Load())])]))],
type_ignores=[])
>>> print(ast.dump(ast.parse('x or y or z'), indent=2))
Module(
body=[
Expr(
value=BoolOp(
op=Or(),
values=[
Name(id='x', ctx=Load()),
Name(id='y', ctx=Load()),
Name(id='z', ctx=Load())]))],
type_ignores=[])
```

- Similarly
`x and y and z`

is a flat “And with 3 children”.
- (This is not new — python 3.11.8 also parses flat 3-children.)
- Mixes can not be flattened.
`x and y or z`

is parsed as `'(x and y) or z'`

BUT `x or y and z`

is parsed as `x or (y and z)`

— that is, `and`

has higher precendence.

### What about grammar?

- 6.17. Operator precedence confirms
`and`

has higher precedence.
- 6.11. Boolean operations explains that
`x or y`

, `x and y`

do short-circuiting, but not how exactly it behaves for longer chains.

It gives grammar:```
or_test ::= and_test | or_test "or" and_test
and_test ::= not_test | and_test "and" not_test
not_test ::= comparison | "not" not_test
```

- Grammar agrees “and” has higher precendence.
- Grammar gives no hint that “BoolOp with 3 children” is a possibility, but I’m not sure if precise AST structure
*must* match grammar, as long as it’s equivalent?
- Anyway, that grammar is left-recursive. IIUC it implies this parsing:
```
x "or" y "or" z
and_test(x) "or" and_test(y) "or" and_test(z)
or_test(and_test(x) "or" and_test(y)) "or" and_test(z)
or_test(or_test(and_test(x) "or" and_test(y)) "or" and_test(z))
```

So if I read that right, documented grammar requires `(x or y) or z`

associativity which contradicts `x or (y or z)`

execution.

If optimization is desired, it should be at least documented.