blob: 9c5a9d8280229659af34913072107fd5173f25a5 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.parsing;
import com.intellij.lang.ITokenTypeRemapper;
import com.intellij.lang.PsiBuilder;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.text.CharArrayUtil;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.PyElementType;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.util.EnumSet;
import java.util.Set;
/**
* @author yole
*/
public class StatementParsing extends Parsing implements ITokenTypeRemapper {
private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.parsing.StatementParsing");
@NonNls protected static final String TOK_FUTURE_IMPORT = "__future__";
@NonNls protected static final String TOK_WITH_STATEMENT = "with_statement";
@NonNls protected static final String TOK_NESTED_SCOPES = "nested_scopes";
@NonNls protected static final String TOK_PRINT_FUNCTION = "print_function";
@NonNls protected static final String TOK_WITH = "with";
@NonNls protected static final String TOK_AS = "as";
@NonNls protected static final String TOK_PRINT = "print";
@NonNls protected static final String TOK_NONE = "None";
@NonNls protected static final String TOK_TRUE = "True";
@NonNls protected static final String TOK_DEBUG = "__debug__";
@NonNls protected static final String TOK_FALSE = "False";
@NonNls protected static final String TOK_NONLOCAL = "nonlocal";
@NonNls protected static final String TOK_EXEC = "exec";
private static final String EXPRESSION_EXPECTED = "Expression expected";
public static final String IDENTIFIER_EXPECTED = "Identifier expected";
protected enum Phase {NONE, FROM, FUTURE, IMPORT} // 'from __future__ import' phase
private Phase myFutureImportPhase = Phase.NONE;
private boolean myExpectAsKeyword = false;
public enum FUTURE {ABSOLUTE_IMPORT, DIVISION, GENERATORS, NESTED_SCOPES, WITH_STATEMENT, PRINT_FUNCTION}
protected Set<FUTURE> myFutureFlags = EnumSet.noneOf(FUTURE.class);
public static class ImportTypes {
public final IElementType statement;
public final IElementType element;
public IElementType starElement;
public ImportTypes(IElementType statement, IElementType element, IElementType starElement) {
this.statement = statement;
this.element = element;
this.starElement = starElement;
}
}
public StatementParsing(ParsingContext context, @Nullable FUTURE futureFlag) {
super(context);
if (futureFlag != null) {
myFutureFlags.add(futureFlag);
}
}
private void setExpectAsKeyword(boolean expectAsKeyword) {
myExpectAsKeyword = expectAsKeyword;
myBuilder.setTokenTypeRemapper(this); // clear cached token type
}
public void parseStatement(ParsingScope scope) {
while (myBuilder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
myBuilder.advanceLexer();
}
final IElementType firstToken;
firstToken = myBuilder.getTokenType();
if (firstToken == null) return;
if (firstToken == PyTokenTypes.WHILE_KEYWORD) {
parseWhileStatement(scope);
return;
}
if (firstToken == PyTokenTypes.IF_KEYWORD) {
parseIfStatement(PyTokenTypes.IF_KEYWORD, PyTokenTypes.ELIF_KEYWORD, PyTokenTypes.ELSE_KEYWORD, PyElementTypes.IF_STATEMENT, scope);
return;
}
if (firstToken == PyTokenTypes.FOR_KEYWORD) {
parseForStatement(scope);
return;
}
if (firstToken == PyTokenTypes.TRY_KEYWORD) {
parseTryStatement(scope);
return;
}
if (firstToken == PyTokenTypes.DEF_KEYWORD) {
getFunctionParser().parseFunctionDeclaration();
return;
}
if (firstToken == PyTokenTypes.AT) {
getFunctionParser().parseDecoratedDeclaration(scope);
return;
}
if (firstToken == PyTokenTypes.CLASS_KEYWORD) {
parseClassDeclaration(scope);
return;
}
if (firstToken == PyTokenTypes.WITH_KEYWORD) {
parseWithStatement(scope);
return;
}
parseSimpleStatement(scope);
}
protected void parseSimpleStatement(ParsingScope scope) {
PsiBuilder builder = myContext.getBuilder();
final IElementType firstToken = builder.getTokenType();
if (firstToken == null) {
return;
}
if (firstToken == PyTokenTypes.PRINT_KEYWORD && hasPrintStatement()) {
parsePrintStatement(builder, scope);
return;
}
if (firstToken == PyTokenTypes.ASSERT_KEYWORD) {
parseAssertStatement(scope);
return;
}
if (firstToken == PyTokenTypes.BREAK_KEYWORD) {
parseKeywordStatement(builder, PyElementTypes.BREAK_STATEMENT, scope);
return;
}
if (firstToken == PyTokenTypes.CONTINUE_KEYWORD) {
parseKeywordStatement(builder, PyElementTypes.CONTINUE_STATEMENT, scope);
return;
}
if (firstToken == PyTokenTypes.DEL_KEYWORD) {
parseDelStatement(scope);
return;
}
if (firstToken == PyTokenTypes.EXEC_KEYWORD) {
parseExecStatement(scope);
return;
}
if (firstToken == PyTokenTypes.GLOBAL_KEYWORD) {
parseNameDefiningStatement(scope, PyElementTypes.GLOBAL_STATEMENT);
return;
}
if (firstToken == PyTokenTypes.NONLOCAL_KEYWORD) {
parseNameDefiningStatement(scope, PyElementTypes.NONLOCAL_STATEMENT);
return;
}
if (firstToken == PyTokenTypes.IMPORT_KEYWORD) {
parseImportStatement(scope, PyElementTypes.IMPORT_STATEMENT, PyElementTypes.IMPORT_ELEMENT);
return;
}
if (firstToken == PyTokenTypes.FROM_KEYWORD) {
parseFromImportStatement(scope);
return;
}
if (firstToken == PyTokenTypes.PASS_KEYWORD) {
parseKeywordStatement(builder, PyElementTypes.PASS_STATEMENT, scope);
return;
}
if (firstToken == PyTokenTypes.RETURN_KEYWORD) {
parseReturnStatement(builder, scope);
return;
}
if (firstToken == PyTokenTypes.RAISE_KEYWORD) {
parseRaiseStatement(scope);
return;
}
PsiBuilder.Marker exprStatement = builder.mark();
if (builder.getTokenType() == PyTokenTypes.YIELD_KEYWORD) {
getExpressionParser().parseYieldOrTupleExpression(false);
checkEndOfStatement(scope);
exprStatement.done(PyElementTypes.EXPRESSION_STATEMENT);
return;
}
else if (getExpressionParser().parseExpressionOptional()) {
IElementType statementType = PyElementTypes.EXPRESSION_STATEMENT;
if (PyTokenTypes.AUG_ASSIGN_OPERATIONS.contains(builder.getTokenType())) {
statementType = PyElementTypes.AUG_ASSIGNMENT_STATEMENT;
builder.advanceLexer();
if (!getExpressionParser().parseYieldOrTupleExpression(false)) {
builder.error(EXPRESSION_EXPECTED);
}
}
else if (builder.getTokenType() == PyTokenTypes.EQ) {
statementType = PyElementTypes.ASSIGNMENT_STATEMENT;
exprStatement.rollbackTo();
exprStatement = builder.mark();
getExpressionParser().parseExpression(false, true);
LOG.assertTrue(builder.getTokenType() == PyTokenTypes.EQ, builder.getTokenType());
builder.advanceLexer();
while (true) {
PsiBuilder.Marker maybeExprMarker = builder.mark();
final boolean isYieldExpr = builder.getTokenType() == PyTokenTypes.YIELD_KEYWORD;
if (!getExpressionParser().parseYieldOrTupleExpression(false)) {
maybeExprMarker.drop();
builder.error(EXPRESSION_EXPECTED);
break;
}
if (builder.getTokenType() == PyTokenTypes.EQ) {
if (isYieldExpr) {
maybeExprMarker.drop();
builder.error("Cannot assign to 'yield' expression");
}
else {
maybeExprMarker.rollbackTo();
getExpressionParser().parseExpression(false, true);
LOG.assertTrue(builder.getTokenType() == PyTokenTypes.EQ, builder.getTokenType());
}
builder.advanceLexer();
}
else {
maybeExprMarker.drop();
break;
}
}
}
checkEndOfStatement(scope);
exprStatement.done(statementType);
return;
}
else {
exprStatement.drop();
}
builder.advanceLexer();
reportParseStatementError(builder, firstToken);
}
protected void reportParseStatementError(PsiBuilder builder, IElementType firstToken) {
if (firstToken == PyTokenTypes.INCONSISTENT_DEDENT) {
builder.error("Unindent does not match any outer indentation level");
}
else if (firstToken == PyTokenTypes.INDENT) {
builder.error("Unexpected indent");
}
else {
builder.error("Statement expected, found " + firstToken.toString());
}
}
private boolean hasPrintStatement() {
return myContext.getLanguageLevel().hasPrintStatement() && !myFutureFlags.contains(FUTURE.PRINT_FUNCTION);
}
protected void checkEndOfStatement(ParsingScope scope) {
PsiBuilder builder = myContext.getBuilder();
if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
builder.advanceLexer();
scope.setAfterSemicolon(false);
}
else if (builder.getTokenType() == PyTokenTypes.SEMICOLON) {
if (!scope.isSuite()) {
builder.advanceLexer();
scope.setAfterSemicolon(true);
if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
builder.advanceLexer();
scope.setAfterSemicolon(false);
}
}
}
else if (!builder.eof()) {
builder.error("End of statement expected");
}
}
private void parsePrintStatement(final PsiBuilder builder, ParsingScope scope) {
LOG.assertTrue(builder.getTokenType() == PyTokenTypes.PRINT_KEYWORD);
final PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
if (builder.getTokenType() == PyTokenTypes.GTGT) {
final PsiBuilder.Marker target = builder.mark();
builder.advanceLexer();
getExpressionParser().parseSingleExpression(false);
target.done(PyElementTypes.PRINT_TARGET);
}
else {
getExpressionParser().parseSingleExpression(false);
}
while (builder.getTokenType() == PyTokenTypes.COMMA) {
builder.advanceLexer();
if (getEndOfStatementsTokens().contains(builder.getTokenType())) {
break;
}
getExpressionParser().parseSingleExpression(false);
}
checkEndOfStatement(scope);
statement.done(PyElementTypes.PRINT_STATEMENT);
}
protected void parseKeywordStatement(PsiBuilder builder, IElementType statementType, ParsingScope scope) {
final PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
checkEndOfStatement(scope);
statement.done(statementType);
}
private void parseReturnStatement(PsiBuilder builder, ParsingScope inSuite) {
LOG.assertTrue(builder.getTokenType() == PyTokenTypes.RETURN_KEYWORD);
final PsiBuilder.Marker returnStatement = builder.mark();
builder.advanceLexer();
if (builder.getTokenType() != null && !getEndOfStatementsTokens().contains(builder.getTokenType())) {
getExpressionParser().parseExpression();
}
checkEndOfStatement(inSuite);
returnStatement.done(PyElementTypes.RETURN_STATEMENT);
}
private void parseDelStatement(ParsingScope inSuite) {
assertCurrentToken(PyTokenTypes.DEL_KEYWORD);
final PsiBuilder.Marker delStatement = myBuilder.mark();
myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error("Expression expected");
}
while (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
if (!getEndOfStatementsTokens().contains(myBuilder.getTokenType())) {
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error("Expression expected");
}
}
}
checkEndOfStatement(inSuite);
delStatement.done(PyElementTypes.DEL_STATEMENT);
}
private void parseRaiseStatement(ParsingScope inSuite) {
assertCurrentToken(PyTokenTypes.RAISE_KEYWORD);
final PsiBuilder.Marker raiseStatement = myBuilder.mark();
myBuilder.advanceLexer();
if (!getEndOfStatementsTokens().contains(myBuilder.getTokenType())) {
getExpressionParser().parseSingleExpression(false);
if (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
getExpressionParser().parseSingleExpression(false);
if (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
getExpressionParser().parseSingleExpression(false);
}
}
else if (myBuilder.getTokenType() == PyTokenTypes.FROM_KEYWORD) {
myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error("Expression expected");
}
}
}
checkEndOfStatement(inSuite);
raiseStatement.done(PyElementTypes.RAISE_STATEMENT);
}
private void parseAssertStatement(ParsingScope scope) {
assertCurrentToken(PyTokenTypes.ASSERT_KEYWORD);
final PsiBuilder.Marker assertStatement = myBuilder.mark();
myBuilder.advanceLexer();
if (getExpressionParser().parseSingleExpression(false)) {
if (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(false)) {
myContext.getBuilder().error(EXPRESSION_EXPECTED);
}
}
checkEndOfStatement(scope);
}
else {
myContext.getBuilder().error(EXPRESSION_EXPECTED);
}
assertStatement.done(PyElementTypes.ASSERT_STATEMENT);
}
protected void parseImportStatement(ParsingScope scope, IElementType statementType, IElementType elementType) {
final PsiBuilder builder = myContext.getBuilder();
final PsiBuilder.Marker importStatement = builder.mark();
builder.advanceLexer();
parseImportElements(elementType, true, false, false);
checkEndOfStatement(scope);
importStatement.done(statementType);
}
/*
Really parses two forms:
from identifier import id, id... -- may be either relative or absolute
from . import identifier -- only relative
*/
private void parseFromImportStatement(ParsingScope inSuite) {
PsiBuilder builder = myContext.getBuilder();
assertCurrentToken(PyTokenTypes.FROM_KEYWORD);
myFutureImportPhase = Phase.FROM;
final PsiBuilder.Marker fromImportStatement = builder.mark();
builder.advanceLexer();
boolean from_future = false;
boolean had_dots = parseRelativeImportDots();
IElementType statementType = PyElementTypes.FROM_IMPORT_STATEMENT;
if (had_dots && parseOptionalDottedName() || parseDottedName()) {
final ImportTypes types = checkFromImportKeyword();
statementType = types.statement;
final IElementType elementType = types.element;
if (myFutureImportPhase == Phase.FUTURE) {
myFutureImportPhase = Phase.IMPORT;
from_future = true;
}
if (builder.getTokenType() == PyTokenTypes.MULT) {
final PsiBuilder.Marker star_import_mark = builder.mark();
builder.advanceLexer();
star_import_mark.done(types.starElement);
}
else if (builder.getTokenType() == PyTokenTypes.LPAR) {
builder.advanceLexer();
parseImportElements(elementType, false, true, from_future);
checkMatches(PyTokenTypes.RPAR, ") expected");
}
else {
parseImportElements(elementType, false, false, from_future);
}
}
else if (had_dots) { // from . import ...
final ImportTypes types = checkFromImportKeyword();
statementType = types.statement;
parseImportElements(types.element, false, false, from_future);
}
checkEndOfStatement(inSuite);
fromImportStatement.done(statementType);
myFutureImportPhase = Phase.NONE;
}
protected ImportTypes checkFromImportKeyword() {
checkMatches(PyTokenTypes.IMPORT_KEYWORD, "'import' expected");
return new ImportTypes(PyElementTypes.FROM_IMPORT_STATEMENT, PyElementTypes.IMPORT_ELEMENT, PyElementTypes.STAR_IMPORT_ELEMENT);
}
/**
* Parses option dots before imported name.
*
* @return true iff there were dots.
*/
private boolean parseRelativeImportDots() {
PsiBuilder builder = myContext.getBuilder();
boolean had_dots = false;
while (builder.getTokenType() == PyTokenTypes.DOT) {
had_dots = true;
builder.advanceLexer();
}
return had_dots;
}
private void parseImportElements(IElementType elementType, boolean is_module_import, boolean in_parens, final boolean from_future) {
PsiBuilder builder = myContext.getBuilder();
while (true) {
final PsiBuilder.Marker asMarker = builder.mark();
if (is_module_import) { // import _
if (!parseDottedNameAsAware(true, false)) {
asMarker.drop();
break;
}
}
else { // from X import _
String token_text = parseIdentifier(getReferenceType());
if (from_future) {
// TODO: mark all known future feature names
if (TOK_WITH_STATEMENT.equals(token_text)) {
myFutureFlags.add(FUTURE.WITH_STATEMENT);
}
else if (TOK_NESTED_SCOPES.equals(token_text)) {
myFutureFlags.add(FUTURE.NESTED_SCOPES);
}
else if (TOK_PRINT_FUNCTION.equals(token_text)) {
myFutureFlags.add(FUTURE.PRINT_FUNCTION);
}
}
}
setExpectAsKeyword(true); // possible 'as' comes as an ident; reparse it as keyword if found
if (builder.getTokenType() == PyTokenTypes.AS_KEYWORD) {
builder.advanceLexer();
setExpectAsKeyword(false);
parseIdentifier(PyElementTypes.TARGET_EXPRESSION);
}
asMarker.done(elementType);
setExpectAsKeyword(false);
if (builder.getTokenType() == PyTokenTypes.COMMA) {
builder.advanceLexer();
if (in_parens && builder.getTokenType() == PyTokenTypes.RPAR) {
break;
}
}
else {
break;
}
}
}
@Nullable
private String parseIdentifier(final IElementType elementType) {
final PsiBuilder.Marker idMarker = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.IDENTIFIER) {
String id_text = myBuilder.getTokenText();
myBuilder.advanceLexer();
idMarker.done(elementType);
return id_text;
}
else {
myBuilder.error(IDENTIFIER_EXPECTED);
idMarker.drop();
}
return null;
}
public boolean parseOptionalDottedName() {
return parseDottedNameAsAware(false, true);
}
public boolean parseDottedName() {
return parseDottedNameAsAware(false, false);
}
// true if identifier was parsed or skipped as optional, false on error
protected boolean parseDottedNameAsAware(boolean expect_as, boolean optional) {
if (myBuilder.getTokenType() != PyTokenTypes.IDENTIFIER) {
if (optional) return true;
myBuilder.error(IDENTIFIER_EXPECTED);
return false;
}
PsiBuilder.Marker marker = myBuilder.mark();
myBuilder.advanceLexer();
marker.done(getReferenceType());
boolean old_expect_AS_kwd = myExpectAsKeyword;
setExpectAsKeyword(expect_as);
while (myBuilder.getTokenType() == PyTokenTypes.DOT) {
marker = marker.precede();
myBuilder.advanceLexer();
checkMatches(PyTokenTypes.IDENTIFIER, IDENTIFIER_EXPECTED);
marker.done(getReferenceType());
}
setExpectAsKeyword(old_expect_AS_kwd);
return true;
}
private void parseNameDefiningStatement(ParsingScope scope, final PyElementType elementType) {
final PsiBuilder.Marker globalStatement = myBuilder.mark();
myBuilder.advanceLexer();
parseIdentifier(PyElementTypes.TARGET_EXPRESSION);
while (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
parseIdentifier(PyElementTypes.TARGET_EXPRESSION);
}
checkEndOfStatement(scope);
globalStatement.done(elementType);
}
private void parseExecStatement(ParsingScope inSuite) {
assertCurrentToken(PyTokenTypes.EXEC_KEYWORD);
final PsiBuilder.Marker execStatement = myBuilder.mark();
myBuilder.advanceLexer();
getExpressionParser().parseExpression(true, false);
if (myBuilder.getTokenType() == PyTokenTypes.IN_KEYWORD) {
myBuilder.advanceLexer();
getExpressionParser().parseSingleExpression(false);
if (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
getExpressionParser().parseSingleExpression(false);
}
}
checkEndOfStatement(inSuite);
execStatement.done(PyElementTypes.EXEC_STATEMENT);
}
protected void parseIfStatement(PyElementType ifKeyword, PyElementType elifKeyword, PyElementType elseKeyword, PyElementType elementType,
ParsingScope scope) {
assertCurrentToken(ifKeyword);
final PsiBuilder.Marker ifStatement = myBuilder.mark();
final PsiBuilder.Marker ifPart = myBuilder.mark();
myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error("expression expected");
}
parseColonAndSuite(scope);
ifPart.done(PyElementTypes.IF_PART_IF);
PsiBuilder.Marker elifPart = myBuilder.mark();
while (myBuilder.getTokenType() == elifKeyword) {
myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error("expression expected");
}
parseColonAndSuite(scope);
elifPart.done(PyElementTypes.IF_PART_ELIF);
elifPart = myBuilder.mark();
}
elifPart.drop(); // we always kept an open extra elif
final PsiBuilder.Marker elsePart = myBuilder.mark();
if (myBuilder.getTokenType() == elseKeyword) {
myBuilder.advanceLexer();
parseColonAndSuite(scope);
elsePart.done(PyElementTypes.ELSE_PART);
}
else {
elsePart.drop();
}
ifStatement.done(elementType);
}
private boolean expectColon() {
if (myBuilder.getTokenType() == PyTokenTypes.COLON) {
myBuilder.advanceLexer();
return true;
}
else if (myBuilder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
myBuilder.error("Colon expected");
return true;
}
final PsiBuilder.Marker marker = myBuilder.mark();
while (!atAnyOfTokens(null, PyTokenTypes.DEDENT, PyTokenTypes.STATEMENT_BREAK, PyTokenTypes.COLON)) {
myBuilder.advanceLexer();
}
boolean result = matchToken(PyTokenTypes.COLON);
if (!result && atToken(PyTokenTypes.STATEMENT_BREAK)) {
myBuilder.advanceLexer();
}
marker.error("Colon expected");
return result;
}
private void parseForStatement(ParsingScope scope) {
assertCurrentToken(PyTokenTypes.FOR_KEYWORD);
final PsiBuilder.Marker statement = myBuilder.mark();
parseForPart(scope);
final PsiBuilder.Marker elsePart = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
myBuilder.advanceLexer();
parseColonAndSuite(scope);
elsePart.done(PyElementTypes.ELSE_PART);
}
else {
elsePart.drop();
}
statement.done(PyElementTypes.FOR_STATEMENT);
}
protected void parseForPart(ParsingScope scope) {
final PsiBuilder.Marker forPart = myBuilder.mark();
myBuilder.advanceLexer();
getExpressionParser().parseExpression(true, true);
checkMatches(PyTokenTypes.IN_KEYWORD, "'in' expected");
getExpressionParser().parseExpression();
parseColonAndSuite(scope);
forPart.done(PyElementTypes.FOR_PART);
}
private void parseWhileStatement(ParsingScope scope) {
assertCurrentToken(PyTokenTypes.WHILE_KEYWORD);
final PsiBuilder.Marker statement = myBuilder.mark();
final PsiBuilder.Marker whilePart = myBuilder.mark();
myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error(EXPRESSION_EXPECTED);
}
parseColonAndSuite(scope);
whilePart.done(PyElementTypes.WHILE_PART);
final PsiBuilder.Marker elsePart = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
myBuilder.advanceLexer();
parseColonAndSuite(scope);
elsePart.done(PyElementTypes.ELSE_PART);
}
else {
elsePart.drop();
}
statement.done(PyElementTypes.WHILE_STATEMENT);
}
private void parseTryStatement(ParsingScope scope) {
assertCurrentToken(PyTokenTypes.TRY_KEYWORD);
final PsiBuilder.Marker statement = myBuilder.mark();
final PsiBuilder.Marker tryPart = myBuilder.mark();
myBuilder.advanceLexer();
parseColonAndSuite(scope);
tryPart.done(PyElementTypes.TRY_PART);
boolean haveExceptClause = false;
if (myBuilder.getTokenType() == PyTokenTypes.EXCEPT_KEYWORD) {
haveExceptClause = true;
while (myBuilder.getTokenType() == PyTokenTypes.EXCEPT_KEYWORD) {
final PsiBuilder.Marker exceptBlock = myBuilder.mark();
myBuilder.advanceLexer();
if (myBuilder.getTokenType() != PyTokenTypes.COLON) {
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error(EXPRESSION_EXPECTED);
}
setExpectAsKeyword(true);
if (myBuilder.getTokenType() == PyTokenTypes.COMMA || myBuilder.getTokenType() == PyTokenTypes.AS_KEYWORD) {
myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(true)) {
myBuilder.error(EXPRESSION_EXPECTED);
}
}
}
parseColonAndSuite(scope);
exceptBlock.done(PyElementTypes.EXCEPT_PART);
}
final PsiBuilder.Marker elsePart = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
myBuilder.advanceLexer();
parseColonAndSuite(scope);
elsePart.done(PyElementTypes.ELSE_PART);
}
else {
elsePart.drop();
}
}
final PsiBuilder.Marker finallyPart = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.FINALLY_KEYWORD) {
myBuilder.advanceLexer();
parseColonAndSuite(scope);
finallyPart.done(PyElementTypes.FINALLY_PART);
}
else {
finallyPart.drop();
if (!haveExceptClause) {
myBuilder.error("'except' or 'finally' expected");
// much better to have a statement of incorrectly determined type
// than "TRY" and "COLON" tokens attached to nothing
}
}
statement.done(PyElementTypes.TRY_EXCEPT_STATEMENT);
}
private void parseColonAndSuite(ParsingScope scope) {
if (expectColon()) {
parseSuite(scope);
}
else {
final PsiBuilder.Marker mark = myBuilder.mark();
mark.done(PyElementTypes.STATEMENT_LIST);
}
}
private void parseWithStatement(ParsingScope scope) {
assertCurrentToken(PyTokenTypes.WITH_KEYWORD);
final PsiBuilder.Marker statement = myBuilder.mark();
myBuilder.advanceLexer();
while (true) {
PsiBuilder.Marker withItem = myBuilder.mark();
getExpressionParser().parseExpression();
setExpectAsKeyword(true);
if (myBuilder.getTokenType() == PyTokenTypes.AS_KEYWORD) {
myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(true)) {
myBuilder.error("Identifier expected");
// 'as' is followed by a target
}
}
withItem.done(PyElementTypes.WITH_ITEM);
if (!matchToken(PyTokenTypes.COMMA)) {
break;
}
}
parseColonAndSuite(scope);
statement.done(PyElementTypes.WITH_STATEMENT);
}
private void parseClassDeclaration(ParsingScope scope) {
final PsiBuilder.Marker classMarker = myBuilder.mark();
parseClassDeclaration(classMarker, scope);
}
public void parseClassDeclaration(PsiBuilder.Marker classMarker, ParsingScope scope) {
assertCurrentToken(PyTokenTypes.CLASS_KEYWORD);
myBuilder.advanceLexer();
parseIdentifierOrSkip();
if (myBuilder.getTokenType() == PyTokenTypes.LPAR) {
getExpressionParser().parseArgumentList();
}
else {
final PsiBuilder.Marker inheritMarker = myBuilder.mark();
inheritMarker.done(PyElementTypes.ARGUMENT_LIST);
}
parseColonAndSuite(scope.withClass(true));
classMarker.done(PyElementTypes.CLASS_DECLARATION);
}
public void parseSuite(ParsingScope scope) {
parseSuite(null, null, scope);
}
public void parseSuite(@Nullable PsiBuilder.Marker endMarker, @Nullable IElementType elType, ParsingScope scope) {
if (myBuilder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
myBuilder.advanceLexer();
final PsiBuilder.Marker marker = myBuilder.mark();
final boolean indentFound = myBuilder.getTokenType() == PyTokenTypes.INDENT;
if (indentFound) {
myBuilder.advanceLexer();
if (myBuilder.eof()) {
myBuilder.error("Indented block expected");
}
else {
while (!myBuilder.eof() && myBuilder.getTokenType() != PyTokenTypes.DEDENT) {
parseStatement(scope);
}
}
}
else {
myBuilder.error("Indent expected");
}
marker.done(PyElementTypes.STATEMENT_LIST);
marker.setCustomEdgeTokenBinders(LeadingCommentsBinder.INSTANCE, FollowingCommentBinder.INSTANCE);
if (endMarker != null) {
endMarker.done(elType);
}
if (indentFound && !myBuilder.eof()) {
checkMatches(PyTokenTypes.DEDENT, "Dedent expected");
}
// NOTE: the following line advances the PsiBuilder lexer and thus
// ensures that the whitespace following the statement list is included
// in the block containing the statement list
myBuilder.getTokenType();
}
else {
final PsiBuilder.Marker marker = myBuilder.mark();
if (myBuilder.eof()) {
myBuilder.error("Statement expected");
}
else {
parseSimpleStatement(scope.withSuite(true));
while (matchToken(PyTokenTypes.SEMICOLON)) {
if (matchToken(PyTokenTypes.STATEMENT_BREAK)) {
break;
}
parseSimpleStatement(scope.withSuite(true));
}
}
marker.done(PyElementTypes.STATEMENT_LIST);
if (endMarker != null) {
endMarker.done(elType);
}
}
}
public IElementType filter(final IElementType source, final int start, final int end, final CharSequence text) {
if (
(myExpectAsKeyword || myContext.getLanguageLevel().hasWithStatement()) &&
source == PyTokenTypes.IDENTIFIER && isWordAtPosition(text, start, end, TOK_AS)
) {
return PyTokenTypes.AS_KEYWORD;
}
else if ( // filter
(myFutureImportPhase == Phase.FROM) &&
source == PyTokenTypes.IDENTIFIER &&
isWordAtPosition(text, start, end, TOK_FUTURE_IMPORT)
) {
myFutureImportPhase = Phase.FUTURE;
return source;
}
else if (
hasWithStatement() &&
source == PyTokenTypes.IDENTIFIER &&
isWordAtPosition(text, start, end, TOK_WITH)
) {
return PyTokenTypes.WITH_KEYWORD;
}
else if (hasPrintStatement() && source == PyTokenTypes.IDENTIFIER &&
isWordAtPosition(text, start, end, TOK_PRINT)) {
return PyTokenTypes.PRINT_KEYWORD;
}
else if (myContext.getLanguageLevel().isPy3K() && source == PyTokenTypes.IDENTIFIER) {
if (isWordAtPosition(text, start, end, TOK_NONE)) {
return PyTokenTypes.NONE_KEYWORD;
}
if (isWordAtPosition(text, start, end, TOK_TRUE)) {
return PyTokenTypes.TRUE_KEYWORD;
}
if (isWordAtPosition(text, start, end, TOK_FALSE)) {
return PyTokenTypes.FALSE_KEYWORD;
}
if (isWordAtPosition(text, start, end, TOK_DEBUG)) {
return PyTokenTypes.DEBUG_KEYWORD;
}
if (isWordAtPosition(text, start, end, TOK_NONLOCAL)) {
return PyTokenTypes.NONLOCAL_KEYWORD;
}
}
else if (!myContext.getLanguageLevel().isPy3K() && source == PyTokenTypes.IDENTIFIER) {
if (isWordAtPosition(text, start, end, TOK_EXEC)) {
return PyTokenTypes.EXEC_KEYWORD;
}
}
return source;
}
protected TokenSet getEndOfStatementsTokens() {
return PyTokenTypes.END_OF_STATEMENT;
}
private static boolean isWordAtPosition(CharSequence text, int start, int end, final String tokenText) {
return CharArrayUtil.regionMatches(text, start, end, tokenText) && end - start == tokenText.length();
}
private boolean hasWithStatement() {
return myContext.getLanguageLevel().hasWithStatement() || myFutureFlags.contains(FUTURE.WITH_STATEMENT);
}
}