boolean (boolean isTargetExpression) { // in sequence a.b.... .c all members but last are always references, and the last may be target. boolean recastFirstIdentifier = false; boolean recastQualifier = false; do { boolean firstIdentifierIsTarget = isTargetExpression && !recastFirstIdentifier; SyntaxTreeBuilder.Marker expr = myBuilder.mark(); if (!parsePrimaryExpression(firstIdentifierIsTarget)) { expr.drop(); return false; } while (true) { final IElementType tokenType = myBuilder.getTokenType(); if (tokenType == PyTokenTypes.DOT) { if (firstIdentifierIsTarget) { recastFirstIdentifier = true; expr.rollbackTo(); break; } myBuilder.advanceLexer(); checkMatches(PyTokenTypes.IDENTIFIER, message("PARSE.expected.name")); if (isTargetExpression && !recastQualifier && !atAnyOfTokens(PyTokenTypes.DOT, PyTokenTypes.LPAR, PyTokenTypes.LBRACKET)) { expr.done(PyElementTypes.TARGET_EXPRESSION); } else { expr.done(getReferenceType()); } expr = expr.precede(); } else if (tokenType == PyTokenTypes.LPAR) { parseArgumentList(); expr.done(PyElementTypes.CALL_EXPRESSION); expr = expr.precede(); } else if (tokenType == PyTokenTypes.LBRACKET) { myBuilder.advanceLexer(); parseSliceOrSubscriptionExpression(expr, false); if (isTargetExpression && !recastQualifier) { recastFirstIdentifier = true; // subscription is always a reference recastQualifier = true; // recast non-first qualifiers too expr.rollbackTo(); break; } expr = expr.precede(); } else { expr.drop(); break; } recastFirstIdentifier = false; // it is true only after a break; normal flow always unsets it. // recastQualifier is untouched, it remembers whether qualifiers were already recast } } while (recastFirstIdentifier); return true; }