void () { final SyntaxTreeBuilder builder = myContext.getBuilder(); if (atToken(PyTokenTypes.FSTRING_START)) { final String prefixThenQuotes = builder.getTokenText(); assert prefixThenQuotes != null; final String openingQuotes = prefixThenQuotes.replaceFirst("^[UuBbCcRrFf]*", ""); final SyntaxTreeBuilder.Marker marker = builder.mark(); nextToken(); while (true) { if (atAnyOfTokens(PyTokenTypes.FSTRING_TEXT_TOKENS)) { nextToken(); } else if (atToken(PyTokenTypes.FSTRING_FRAGMENT_START)) { parseFStringFragment(); } else if (atToken(PyTokenTypes.FSTRING_END)) { if (builder.getTokenText().equals(openingQuotes)) { nextToken(); } // Can be the end of an enclosing f-string, so leave it in the stream else { builder.mark().error(message("PARSE.expected.fstring.quote", openingQuotes)); } break; } else if (atToken(PyTokenTypes.STATEMENT_BREAK)) { builder.mark().error(message("PARSE.expected.fstring.quote", openingQuotes)); break; } else { builder.error(message("unexpected.f.string.token")); break; } } marker.done(PyElementTypes.FSTRING_NODE); } }