diff --git a/python/src/com/jetbrains/python/PyElementTypes.java b/python/src/com/jetbrains/python/PyElementTypes.java index 5c28567f5287..80357cd0b649 100644 --- a/python/src/com/jetbrains/python/PyElementTypes.java +++ b/python/src/com/jetbrains/python/PyElementTypes.java @@ -97,6 +97,7 @@ public interface PyElementTypes { PyElementType YIELD_EXPRESSION = new PyElementType("YIELD_EXPRESSION", PyYieldExpressionImpl.class); PyElementType SET_LITERAL_EXPRESSION = new PyElementType("SET_LITERAL_EXPRESSION", PySetLiteralExpressionImpl.class); + PyElementType SET_COMP_EXPRESSION = new PyElementType("SET_COMP_EXPRESSION", PySetCompExpressionImpl.class); TokenSet EXPRESSIONS = TokenSet.create(EMPTY_EXPRESSION, REFERENCE_EXPRESSION, INTEGER_LITERAL_EXPRESSION, FLOAT_LITERAL_EXPRESSION, IMAGINARY_LITERAL_EXPRESSION, STRING_LITERAL_EXPRESSION, PARENTHESIZED_EXPRESSION, @@ -105,7 +106,7 @@ public interface PyElementTypes { LAMBDA_EXPRESSION, LIST_COMP_EXPRESSION, DICT_LITERAL_EXPRESSION, KEY_VALUE_EXPRESSION, REPR_EXPRESSION, GENERATOR_EXPRESSION, CONDITIONAL_EXPRESSION, YIELD_EXPRESSION, TARGET_EXPRESSION, NONE_LITERAL_EXPRESSION, BOOL_LITERAL_EXPRESSION, - SET_LITERAL_EXPRESSION); + SET_LITERAL_EXPRESSION, SET_COMP_EXPRESSION); TokenSet LIST_LIKE_EXPRESSIONS = TokenSet.create(LIST_LITERAL_EXPRESSION, LIST_COMP_EXPRESSION, TUPLE_EXPRESSION); diff --git a/python/src/com/jetbrains/python/parsing/ExpressionParsing.java b/python/src/com/jetbrains/python/parsing/ExpressionParsing.java index ba0ea1c7aaf5..018993b23e10 100644 --- a/python/src/com/jetbrains/python/parsing/ExpressionParsing.java +++ b/python/src/com/jetbrains/python/parsing/ExpressionParsing.java @@ -89,7 +89,7 @@ public class ExpressionParsing extends Parsing { builder.error(message("PARSE.expected.expression")); } if (builder.getTokenType() == PyTokenTypes.FOR_KEYWORD) { - parseListCompExpression(expr, PyTokenTypes.RBRACKET, PyElementTypes.LIST_COMP_EXPRESSION); + parseComprehension(expr, PyTokenTypes.RBRACKET, PyElementTypes.LIST_COMP_EXPRESSION); } else { while (builder.getTokenType() != PyTokenTypes.RBRACKET) { @@ -106,7 +106,7 @@ public class ExpressionParsing extends Parsing { } } - private void parseListCompExpression(PsiBuilder.Marker expr, + private void parseComprehension(PsiBuilder.Marker expr, final IElementType endToken, final IElementType exprType) { assertCurrentToken(PyTokenTypes.FOR_KEYWORD); @@ -163,6 +163,10 @@ public class ExpressionParsing extends Parsing { firstExprMarker.drop(); parseSetLiteralTail(expr); } + else if (myBuilder.getTokenType() == PyTokenTypes.FOR_KEYWORD) { + firstExprMarker.drop(); + parseComprehension(expr, PyTokenTypes.RBRACE, PyElementTypes.SET_COMP_EXPRESSION); + } else { myBuilder.error("expression expected"); firstExprMarker.drop(); @@ -230,7 +234,7 @@ public class ExpressionParsing extends Parsing { else { parseYieldOrTupleExpression(builder, isTargetExpression); if (builder.getTokenType() == PyTokenTypes.FOR_KEYWORD) { - parseListCompExpression(expr, PyTokenTypes.RPAR, PyElementTypes.GENERATOR_EXPRESSION); + parseComprehension(expr, PyTokenTypes.RPAR, PyElementTypes.GENERATOR_EXPRESSION); } else { checkMatches(PyTokenTypes.RPAR, message("PARSE.expected.rpar")); @@ -360,7 +364,7 @@ public class ExpressionParsing extends Parsing { argNumber++; if (argNumber > 1) { if (argNumber == 2 && myBuilder.getTokenType() == PyTokenTypes.FOR_KEYWORD && genexpr != null) { - parseListCompExpression(genexpr, PyTokenTypes.RPAR, PyElementTypes.GENERATOR_EXPRESSION); + parseComprehension(genexpr, PyTokenTypes.RPAR, PyElementTypes.GENERATOR_EXPRESSION); needBracket = false; break; } diff --git a/python/src/com/jetbrains/python/psi/PySetCompExpression.java b/python/src/com/jetbrains/python/psi/PySetCompExpression.java new file mode 100644 index 000000000000..a8d27731d784 --- /dev/null +++ b/python/src/com/jetbrains/python/psi/PySetCompExpression.java @@ -0,0 +1,9 @@ +package com.jetbrains.python.psi; + +/** + * Set comprehension: {x for x in range(10)} + * + * @author yole + */ +public interface PySetCompExpression extends PyExpression, NameDefiner { +} diff --git a/python/src/com/jetbrains/python/psi/impl/PySetCompExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PySetCompExpressionImpl.java new file mode 100644 index 000000000000..11d27a7e8eba --- /dev/null +++ b/python/src/com/jetbrains/python/psi/impl/PySetCompExpressionImpl.java @@ -0,0 +1,19 @@ +package com.jetbrains.python.psi.impl; + +import com.intellij.lang.ASTNode; +import com.jetbrains.python.psi.PySetCompExpression; +import com.jetbrains.python.psi.types.PyType; + +/** + * @author yole + */ +public class PySetCompExpressionImpl extends PyComprehensionElementImpl implements PySetCompExpression { + public PySetCompExpressionImpl(ASTNode astNode) { + super(astNode); + } + + public PyType getType() { + // TODO + return null; + } +} diff --git a/python/testData/psi/SetComprehension.py b/python/testData/psi/SetComprehension.py new file mode 100644 index 000000000000..d98f80b505d9 --- /dev/null +++ b/python/testData/psi/SetComprehension.py @@ -0,0 +1 @@ +{x for x in range(10)} \ No newline at end of file diff --git a/python/testData/psi/SetComprehension.txt b/python/testData/psi/SetComprehension.txt new file mode 100644 index 000000000000..4feb8122550e --- /dev/null +++ b/python/testData/psi/SetComprehension.txt @@ -0,0 +1,23 @@ +PyFile:SetComprehension.py + PyExpressionStatement + PySetCompExpression + PsiElement(Py:LBRACE)('{') + PyReferenceExpression: x + PsiElement(Py:IDENTIFIER)('x') + PsiWhiteSpace(' ') + PsiElement(Py:FOR_KEYWORD)('for') + PsiWhiteSpace(' ') + PyTargetExpression: x + PsiElement(Py:IDENTIFIER)('x') + PsiWhiteSpace(' ') + PsiElement(Py:IN_KEYWORD)('in') + PsiWhiteSpace(' ') + PyCallExpression: range + PyReferenceExpression: range + PsiElement(Py:IDENTIFIER)('range') + PyArgumentList + PsiElement(Py:LPAR)('(') + PyNumericLiteralExpression + PsiElement(Py:INTEGER_LITERAL)('10') + PsiElement(Py:RPAR)(')') + PsiElement(Py:RBRACE)('}') \ No newline at end of file diff --git a/python/testData/psi/SetLiteral.py b/python/testData/psi/SetLiteral.py index 47848d0a3b83..d8168cf9ab6c 100644 --- a/python/testData/psi/SetLiteral.py +++ b/python/testData/psi/SetLiteral.py @@ -1 +1,2 @@ {1, 2} +{1} diff --git a/python/testData/psi/SetLiteral.txt b/python/testData/psi/SetLiteral.txt index f4ea1d26df2a..9168cb3da5c5 100644 --- a/python/testData/psi/SetLiteral.txt +++ b/python/testData/psi/SetLiteral.txt @@ -8,4 +8,11 @@ PyFile:SetLiteral.py PsiWhiteSpace(' ') PyNumericLiteralExpression PsiElement(Py:INTEGER_LITERAL)('2') - PsiElement(Py:RBRACE)('}') \ No newline at end of file + PsiElement(Py:RBRACE)('}') + PsiWhiteSpace('\n') + PyExpressionStatement + PySetLiteralExpression + PsiElement(Py:LBRACE)('{') + PyNumericLiteralExpression + PsiElement(Py:INTEGER_LITERAL)('1') + PsiElement(Py:RBRACE)('}') \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonParsingTest.java b/python/testSrc/com/jetbrains/python/PythonParsingTest.java index e18a9e844253..d8aa0b7b5caf 100644 --- a/python/testSrc/com/jetbrains/python/PythonParsingTest.java +++ b/python/testSrc/com/jetbrains/python/PythonParsingTest.java @@ -146,6 +146,10 @@ public class PythonParsingTest extends ParsingTestCase { doTest(LanguageLevel.PYTHON30); } + public void testSetComprehension() throws Exception { + doTest(LanguageLevel.PYTHON30); + } + public void doTest() throws Exception { doTest(LanguageLevel.PYTHON25); }