← board

A method call or subscript on a parenthesised expression fails (or crashes)

("ab").upper()      # CPython AB;   pxx: error: expected newline after statement
(x).upper()         # same
("%d" % 5).zfill(3) # same
(x)[1]              # CPython b;    pxx: SIGSEGV
([1, 2])[0]         # same

Without the parentheses every one of these works, which is what makes it read as a mystery: adding a grouping — the most reflexive thing a programmer does to an expression — breaks it.

Cause

ParseFactorCore's grouped-postfix tail is Pascal-shaped: for [ it builds a raw AN_INDEX (Pascal array indexing) and for . it reads a record FIELD. Neither is what a Python receiver means, and it ran in Python mode too. The raw index over a TPyList/AnsiString handle is the SIGSEGV; the field read is the parse error.

Fix

In PyExprMode, stand down: leave . and [ to ParseFactor's own Python suffix loops, which run immediately afterwards and already handle str methods, subscript and slice, variant runtime dispatch and class methods. ^ stays — it has no Python spelling and no Python loop.

That is the same call the comment two lines below makes about not writing a fourth copy of member dispatch: this tail was a third copy, and the Python side already has the machinery.

Verified

test/test_nilpy_postfix_after_parens.npy — grouped str methods, arithmetic and % results, subscripts and slices over str/list/dict, chains that compose, a class instance's method and field, an immediately-invoked lambda (still callable), and a parenthesised TUPLE (still a tuple, not a grouping). Diffs clean against CPython's own output. make compiler/pascal26 fixedpoint + tools/gate.sh quick GREEN — the Pascal arm is unchanged by construction (the guard only removes the PyExprMode case) and the self-host exercises it heavily.

Log