support keyword arguments in superclass list (http://www.python.org/dev/peps/pep-3115/)

This commit is contained in:
Dmitry Jemerov
2010-01-22 18:43:08 +03:00
parent 562ef6b819
commit 6cd30dbd64
11 changed files with 99 additions and 46 deletions
@@ -228,7 +228,7 @@ public class ExpressionParsing extends Parsing {
expr = expr.precede();
}
else if (tokenType == PyTokenTypes.LPAR) {
parseArgumentList(builder);
parseArgumentList();
expr.done(PyElementTypes.CALL_EXPRESSION);
expr = expr.precede();
}
@@ -297,48 +297,48 @@ public class ExpressionParsing extends Parsing {
expr.done(PyElementTypes.SLICE_EXPRESSION);
}
public void parseArgumentList(final PsiBuilder builder) {
LOG.assertTrue(builder.getTokenType() == PyTokenTypes.LPAR);
final PsiBuilder.Marker arglist = builder.mark();
final PsiBuilder.Marker genexpr = builder.mark();
builder.advanceLexer();
public void parseArgumentList() {
LOG.assertTrue(myBuilder.getTokenType() == PyTokenTypes.LPAR);
final PsiBuilder.Marker arglist = myBuilder.mark();
final PsiBuilder.Marker genexpr = myBuilder.mark();
myBuilder.advanceLexer();
int argNumber = 0;
boolean needBracket = true;
while (builder.getTokenType() != PyTokenTypes.RPAR) {
while (myBuilder.getTokenType() != PyTokenTypes.RPAR) {
argNumber++;
if (argNumber > 1) {
if (argNumber == 2 && builder.getTokenType() == PyTokenTypes.FOR_KEYWORD) {
if (argNumber == 2 && myBuilder.getTokenType() == PyTokenTypes.FOR_KEYWORD && genexpr != null) {
parseListCompExpression(genexpr, PyTokenTypes.RPAR, PyElementTypes.GENERATOR_EXPRESSION);
needBracket = false;
break;
}
else if (builder.getTokenType() == PyTokenTypes.COMMA) {
builder.advanceLexer();
if (builder.getTokenType() == PyTokenTypes.RPAR) {
else if (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
if (myBuilder.getTokenType() == PyTokenTypes.RPAR) {
break;
}
}
else {
builder.error(message("PARSE.expected.comma.or.rpar"));
myBuilder.error(message("PARSE.expected.comma.or.rpar"));
break;
}
}
if (builder.getTokenType() == PyTokenTypes.MULT || builder.getTokenType() == PyTokenTypes.EXP) {
final PsiBuilder.Marker starArgMarker = builder.mark();
builder.advanceLexer();
if (myBuilder.getTokenType() == PyTokenTypes.MULT || myBuilder.getTokenType() == PyTokenTypes.EXP) {
final PsiBuilder.Marker starArgMarker = myBuilder.mark();
myBuilder.advanceLexer();
if (!parseSingleExpression(false)) {
builder.error(message("PARSE.expected.expression"));
myBuilder.error(message("PARSE.expected.expression"));
}
starArgMarker.done(PyElementTypes.STAR_ARGUMENT_EXPRESSION);
}
else {
if (builder.getTokenType() == PyTokenTypes.IDENTIFIER) {
final PsiBuilder.Marker keywordArgMarker = builder.mark();
builder.advanceLexer();
if (builder.getTokenType() == PyTokenTypes.EQ) {
builder.advanceLexer();
if (myBuilder.getTokenType() == PyTokenTypes.IDENTIFIER) {
final PsiBuilder.Marker keywordArgMarker = myBuilder.mark();
myBuilder.advanceLexer();
if (myBuilder.getTokenType() == PyTokenTypes.EQ) {
myBuilder.advanceLexer();
if (!parseSingleExpression(false)) {
builder.error(message("PARSE.expected.expression"));
myBuilder.error(message("PARSE.expected.expression"));
}
keywordArgMarker.done(PyElementTypes.KEYWORD_ARGUMENT_EXPRESSION);
continue;
@@ -346,13 +346,15 @@ public class ExpressionParsing extends Parsing {
keywordArgMarker.rollbackTo();
}
if (!parseSingleExpression(false)) {
builder.error(message("PARSE.expected.expression"));
myBuilder.error(message("PARSE.expected.expression"));
}
}
}
if (needBracket) {
genexpr.drop();
if (genexpr != null) {
genexpr.drop();
}
checkMatches(PyTokenTypes.RPAR, message("PARSE.expected.rpar"));
}
arglist.done(PyElementTypes.ARGUMENT_LIST);
@@ -48,7 +48,7 @@ public class FunctionParsing extends Parsing {
myBuilder.advanceLexer();
getStatementParser().parseDottedName();
if (myBuilder.getTokenType() == PyTokenTypes.LPAR) {
getExpressionParser().parseArgumentList(myBuilder);
getExpressionParser().parseArgumentList();
}
else { // empty arglist node, so we always have it
myBuilder.mark().done(PyElementTypes.ARGUMENT_LIST);
@@ -665,15 +665,13 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
assertCurrentToken(PyTokenTypes.CLASS_KEYWORD);
myBuilder.advanceLexer();
checkMatches(PyTokenTypes.IDENTIFIER, "identifier expected");
final PsiBuilder.Marker inheritMarker = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.LPAR) {
myBuilder.advanceLexer();
if (myBuilder.getTokenType() != PyTokenTypes.RPAR) {
getExpressionParser().parseExpression();
}
checkMatches(PyTokenTypes.RPAR, ") expected");
getExpressionParser().parseArgumentList();
}
else {
final PsiBuilder.Marker inheritMarker = myBuilder.mark();
inheritMarker.done(PyElementTypes.ARGUMENT_LIST);
}
inheritMarker.done(PyElementTypes.PARENTHESIZED_EXPRESSION);
checkMatches(PyTokenTypes.COLON, "colon expected");
parseSuite();
classMarker.done(PyElementTypes.CLASS_DECLARATION);
@@ -81,15 +81,9 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
@NotNull
public PyExpression[] getSuperClassExpressions() {
final PyParenthesizedExpression superExpression = PsiTreeUtil.getChildOfType(this, PyParenthesizedExpression.class);
if (superExpression != null) {
PyExpression expr = superExpression.getContainedExpression();
if (expr instanceof PyTupleExpression) {
return ((PyTupleExpression) expr).getElements();
}
if (expr != null) {
return new PyExpression[] { expr };
}
final PyArgumentList argList = PsiTreeUtil.getChildOfType(this, PyArgumentList.class);
if (argList != null) {
return argList.getArguments();
}
return PyExpression.EMPTY_ARRAY;
}
@@ -181,7 +175,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
List<PyClass> result = new ArrayList<PyClass>();
// maybe a bare old-style class?
// TODO: depend on language version: py3k does not do old style classes
PsiElement paren = PsiTreeUtil.getChildOfType(this, PyParenthesizedExpression.class).getFirstChild(); // no NPE, we always have the par expr
PsiElement paren = PsiTreeUtil.getChildOfType(this, PyArgumentList.class).getFirstChild(); // no NPE, we always have the par expr
if (paren != null && "(".equals(paren.getText())) { // "()" after class name, it's new style
for(PsiElement element: superClassElements) {
if (element instanceof PyClass) {
@@ -44,7 +44,7 @@ public class PythonLanguageLevelPusher implements FilePropertyPusher<LanguageLev
for (Module module : modules) {
final Sdk sdk = PythonSdkType.findPythonSdk(module);
myModuleSdks.put(module, sdk);
if (!usedSdks.contains(sdk)) {
if (sdk != null && !usedSdks.contains(sdk)) {
usedSdks.add(sdk);
updateSdkLanguageLevel(project, sdk);
}
+1 -1
View File
@@ -18,7 +18,7 @@ PyFile:ClassDecorators.py
PsiElement(Py:CLASS_KEYWORD)('class')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('A')
PyParenthesizedExpression
PyArgumentList
<empty list>
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
+1 -1
View File
@@ -3,7 +3,7 @@ PyFile:EmptySuperclassList.py
PsiElement(Py:CLASS_KEYWORD)('class')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('A')
PyParenthesizedExpression
PyArgumentList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
@@ -0,0 +1,3 @@
class Foo(base1, base2, metaclass=mymeta): pass
class Bar(*bases, **kwds): pass
@@ -0,0 +1,49 @@
PyFile:SuperclassKeywordArguments.py
PyClass: Foo
PsiElement(Py:CLASS_KEYWORD)('class')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('Foo')
PyArgumentList
PsiElement(Py:LPAR)('(')
PyReferenceExpression: base1
PsiElement(Py:IDENTIFIER)('base1')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyReferenceExpression: base2
PsiElement(Py:IDENTIFIER)('base2')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyKeywordArgumentImpl: metaclass
PsiElement(Py:IDENTIFIER)('metaclass')
PsiElement(Py:EQ)('=')
PyReferenceExpression: mymeta
PsiElement(Py:IDENTIFIER)('mymeta')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n')
PyClass: Bar
PsiElement(Py:CLASS_KEYWORD)('class')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('Bar')
PyArgumentList
PsiElement(Py:LPAR)('(')
PyStarArgument
PsiElement(Py:MULT)('*')
PyReferenceExpression: bases
PsiElement(Py:IDENTIFIER)('bases')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyStarArgument
PsiElement(Py:EXP)('**')
PyReferenceExpression: kwds
PsiElement(Py:IDENTIFIER)('kwds')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
@@ -1,5 +1,6 @@
package com.jetbrains.python;
import com.jetbrains.python.refactoring.classes.PyPullUpTest;
import junit.framework.TestSuite;
/**
@@ -12,6 +13,7 @@ public class PythonAllTestsSuite {
public static final Class[] tests = {
PythonLexerTest.class,
PythonParsingTest.class,
PyStringLiteralTest.class,
PyIndentTest.class,
PyStatementPartsTest.class,
PythonHighlightingTest.class,
@@ -29,7 +31,8 @@ public class PythonAllTestsSuite {
PythonDemorganLawIntentionTest.class,
PyQuickFixTest.class,
PySelectWordTest.class,
PySurroundWithTest.class
PySurroundWithTest.class,
PyPullUpTest.class
};
public static TestSuite suite() {
@@ -134,6 +134,10 @@ public class PythonParsingTest extends ParsingTestCase {
doTest(LanguageLevel.PYTHON30);
}
public void testSuperclassKeywordArguments() throws Exception {
doTest(LanguageLevel.PYTHON30);
}
public void doTest() throws Exception {
doTest(LanguageLevel.PYTHON25);
}