closure args and array-style-access in new statement expressions

This commit is contained in:
Maxim Medvedev
2010-12-29 14:32:32 +03:00
parent 1f4042816c
commit 88942c7e74
10 changed files with 239 additions and 42 deletions
@@ -16,16 +16,18 @@
package org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions;
import com.intellij.lang.LighterASTNode;
import com.intellij.lang.PsiBuilder;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
import org.jetbrains.plugins.groovy.lang.parser.GroovyParser;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.blocks.OpenOrClosableBlock;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.arguments.CommandArguments;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.arithmetic.PathExpression;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.arithmetic.UnaryExpressionNotPlusMinus;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.primary.PrimaryExpression;
import org.jetbrains.plugins.groovy.lang.parser.parsing.util.ParserUtils;
/**
* Main classdef for any general expression parsing
@@ -36,17 +38,28 @@ public class ExpressionStatement implements GroovyElementTypes {
@Nullable
private static IElementType parseExpressionStatement(PsiBuilder builder, GroovyParser parser) {
final LighterASTNode firstDoneMarker = builder.getLatestDoneMarker();
if (checkForTypeCast(builder, parser)) return CAST_EXPRESSION;
PsiBuilder.Marker marker = builder.mark();
if (ConditionalExpression.parse(builder, parser) &&
final PathExpression.Result result = PathExpression.parseForExprStatement(builder, parser);
if (result != PathExpression.Result.WRONG_WAY &&
!TokenSets.SEPARATORS.contains(builder.getTokenType()) &&
CommandArguments.parse(builder, parser)) {
marker.done(CALL_EXPRESSION);
return CALL_EXPRESSION;
!TokenSets.BINARY_OP_SET.contains(builder.getTokenType()) &&
!TokenSets.UNARY_OP_SET.contains(builder.getTokenType())) {
if (result == PathExpression.Result.CALL_WITH_CLOSURE) {
marker.drop();
return PATH_METHOD_CALL;
}
else if (CommandArguments.parse(builder, parser)) {
marker.done(CALL_EXPRESSION);
return CALL_EXPRESSION;
}
}
marker.drop();
final LighterASTNode latestDoneMarker = builder.getLatestDoneMarker();
return latestDoneMarker != null && firstDoneMarker != latestDoneMarker ? latestDoneMarker.getTokenType() : WRONGWAY;
return WRONGWAY;
}
private static boolean checkForTypeCast(PsiBuilder builder, GroovyParser parser) {
return UnaryExpressionNotPlusMinus.parse(builder, parser, false);
}
/**
@@ -63,7 +76,7 @@ public class ExpressionStatement implements GroovyElementTypes {
PsiBuilder.Marker marker = builder.mark();
final IElementType result = parseExpressionStatement(builder, parser);
if (result != CALL_EXPRESSION) {
if (result != CALL_EXPRESSION && result != PATH_METHOD_CALL) {
marker.drop();
return result != WRONGWAY;
}
@@ -73,19 +86,36 @@ public class ExpressionStatement implements GroovyElementTypes {
marker.drop();
break;
}
final PsiBuilder.Marker exprStatement = marker.precede();
PsiBuilder.Marker exprStatement = marker.precede();
marker.done(REFERENCE_EXPRESSION);
if (builder.getTokenType() == mLPAREN) {
PrimaryExpression.methodCallArgsParse(builder, parser);
exprStatement.done(PATH_METHOD_CALL);
}
else if (mLBRACK.equals(builder.getTokenType()) &&
!ParserUtils.lookAhead(builder, mLBRACK, mCOLON) &&
!ParserUtils.lookAhead(builder, mLBRACK, mNLS, mCOLON)) {
PathExpression.indexPropertyArgsParse(builder, parser);
exprStatement.done(PATH_INDEX_PROPERTY);
if (mLPAREN.equals(builder.getTokenType())) {
PrimaryExpression.methodCallArgsParse(builder, parser);
}
else if (mLCURLY.equals(builder.getTokenType())) {
PsiBuilder.Marker argsMarker = builder.mark();
argsMarker.done(ARGUMENTS);
}
while (mLCURLY.equals(builder.getTokenType())) {
OpenOrClosableBlock.parseClosableBlock(builder, parser);
}
exprStatement = exprStatement.precede();
exprStatement.done(PATH_METHOD_CALL);
}
else if (CommandArguments.parse(builder, parser)) {
exprStatement.done(CALL_EXPRESSION);
}
else {
exprStatement.drop();
//builder.error(GroovyBundle.message("expression.expected"));
break;
}
@@ -31,33 +31,42 @@ import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.p
import org.jetbrains.plugins.groovy.lang.parser.parsing.types.TypeArguments;
import org.jetbrains.plugins.groovy.lang.parser.parsing.util.ParserUtils;
import static org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.arithmetic.PathExpression.Result.*;
/**
* @author ilyas
*/
public class PathExpression implements GroovyElementTypes {
public static boolean parse(PsiBuilder builder, GroovyParser parser) {
return parseForExprStatement(builder, parser) != PathExpression.Result.WRONG_WAY;
}
public enum Result{OK, WRONG_WAY, CALL_WITH_CLOSURE}
public static Result parseForExprStatement(PsiBuilder builder, GroovyParser parser) {
PsiBuilder.Marker marker = builder.mark();
final GroovyElementType qualifierType = PrimaryExpression.parse(builder, parser);
if (qualifierType != WRONGWAY) {
Result result = OK;
if (isPathElementStart(builder)) {
PsiBuilder.Marker newMarker = marker.precede();
marker.drop();
if (mLCURLY.equals(builder.getTokenType())) {
final boolean lCurly = mLCURLY.equals(builder.getTokenType());
if (lCurly) {
PsiBuilder.Marker argsMarker = builder.mark();
argsMarker.done(ARGUMENTS);
}
pathElementParse(builder, newMarker, parser, qualifierType);
result = pathElementParse(builder, newMarker, parser, qualifierType, lCurly?CALL_WITH_CLOSURE:OK);
}
else {
marker.drop();
}
return true;
return result;
}
else {
marker.drop();
return false;
return WRONG_WAY;
}
}
@@ -71,8 +80,11 @@ public class PathExpression implements GroovyElementTypes {
private static final TokenSet DOTS = TokenSet.create(mSPREAD_DOT, mOPTIONAL_DOT, mMEMBER_POINTER, mDOT);
private static GroovyElementType pathElementParse(PsiBuilder builder, PsiBuilder.Marker marker, GroovyParser parser,
GroovyElementType qualifierType) {
private static Result pathElementParse(PsiBuilder builder,
PsiBuilder.Marker marker,
GroovyParser parser,
GroovyElementType qualifierType,
Result result) {
GroovyElementType res;
@@ -100,7 +112,7 @@ public class PathExpression implements GroovyElementTypes {
PsiBuilder.Marker argsMarker = builder.mark();
argsMarker.done(ARGUMENTS);
}
pathElementParse(builder, newMarker, parser, res);
result = pathElementParse(builder, newMarker, parser, res, OK);
}
else {
builder.error(GroovyBundle.message("path.selector.expected"));
@@ -111,41 +123,46 @@ public class PathExpression implements GroovyElementTypes {
PrimaryExpression.methodCallArgsParse(builder, parser);
if (mLCURLY.equals(builder.getTokenType()) || ParserUtils.lookAhead(builder, mNLS, mLCURLY)) {
ParserUtils.getToken(builder, mNLS);
pathElementParse(builder, marker, parser, qualifierType);
result = pathElementParse(builder, marker, parser, qualifierType, OK);
}
else {
PsiBuilder.Marker newMarker = marker.precede();
marker.done(PATH_METHOD_CALL);
pathElementParse(builder, newMarker, parser, qualifierType);
result = pathElementParse(builder, newMarker, parser, qualifierType, OK);
}
}
else if (mLCURLY.equals(builder.getTokenType())) {
appendedBlockParse(builder, parser);
if (mLCURLY.equals(builder.getTokenType())) {
pathElementParse(builder, marker, parser, qualifierType);
result = pathElementParse(builder, marker, parser, qualifierType, result);
}
else {
PsiBuilder.Marker newMarker = marker.precede();
marker.done(PATH_METHOD_CALL);
pathElementParse(builder, newMarker, parser, PATH_METHOD_CALL);
result = pathElementParse(builder, newMarker, parser, PATH_METHOD_CALL, result);
}
}
else if (mLBRACK.equals(builder.getTokenType()) &&
!ParserUtils.lookAhead(builder, mLBRACK, mCOLON) &&
!ParserUtils.lookAhead(builder, mLBRACK, mNLS, mCOLON)) {
else if (checkForArrayAccess(builder)) {
indexPropertyArgsParse(builder, parser);
PsiBuilder.Marker newMarker = marker.precede();
marker.done(PATH_INDEX_PROPERTY);
if (mLCURLY.equals(builder.getTokenType())) {
final boolean lCurly = mLCURLY.equals(builder.getTokenType());
if (lCurly) {
PsiBuilder.Marker argsMarker = builder.mark();
argsMarker.done(ARGUMENTS);
}
pathElementParse(builder, newMarker, parser, PATH_INDEX_PROPERTY);
result = pathElementParse(builder, newMarker, parser, PATH_INDEX_PROPERTY, lCurly?CALL_WITH_CLOSURE : OK);
}
else {
marker.drop();
}
return PATH_EXPRESSION;
return result;
}
public static boolean checkForArrayAccess(PsiBuilder builder) {
return mLBRACK.equals(builder.getTokenType()) &&
!ParserUtils.lookAhead(builder, mLBRACK, mCOLON) &&
!ParserUtils.lookAhead(builder, mLBRACK, mNLS, mCOLON);
}
private static GroovyElementType parseThisSuperExpression(PsiBuilder builder, GroovyElementType qualifierType) {
@@ -221,7 +238,7 @@ public class PathExpression implements GroovyElementTypes {
* @param builder
* @return
*/
private static GroovyElementType indexPropertyArgsParse(PsiBuilder builder, GroovyParser parser) {
public static GroovyElementType indexPropertyArgsParse(PsiBuilder builder, GroovyParser parser) {
assert mLBRACK.equals(builder.getTokenType());
PsiBuilder.Marker marker = builder.mark();
@@ -33,6 +33,10 @@ import static org.jetbrains.plugins.groovy.lang.parser.parsing.statements.typeDe
public class UnaryExpressionNotPlusMinus implements GroovyElementTypes {
public static boolean parse(PsiBuilder builder, GroovyParser parser) {
return parse(builder, parser, true);
}
public static boolean parse(PsiBuilder builder, GroovyParser parser, boolean runPostfixIfFail) {
PsiBuilder.Marker marker = builder.mark();
if (builder.getTokenType() == mLPAREN) {
final ReferenceElement.ReferenceElementResult result = parseTypeCast(builder);
@@ -42,18 +46,22 @@ public class UnaryExpressionNotPlusMinus implements GroovyElementTypes {
return true;
} else {
marker.rollbackTo();
return PostfixExpression.parse(builder, parser);
return runPostfix(builder, parser, runPostfixIfFail);
}
} else {
marker.drop();
return PostfixExpression.parse(builder, parser);
return runPostfix(builder, parser, runPostfixIfFail);
}
} else {
marker.drop();
return PostfixExpression.parse(builder, parser);
return runPostfix(builder, parser, runPostfixIfFail);
}
}
private static boolean runPostfix(PsiBuilder builder, GroovyParser parser, boolean runPostfixIfFail) {
return runPostfixIfFail ? PostfixExpression.parse(builder, parser) : false;
}
private static ReferenceElement.ReferenceElementResult parseTypeCast(PsiBuilder builder) {
PsiBuilder.Marker marker = builder.mark();
if (!ParserUtils.getToken(builder, mLPAREN, GroovyBundle.message("lparen.expected"))) {
@@ -78,4 +86,4 @@ public class UnaryExpressionNotPlusMinus implements GroovyElementTypes {
return fail;
}
}
}
}
@@ -7,11 +7,12 @@ import com.intellij.util.containers.hash.HashMap;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.arithmetic.GrRangeExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
@@ -77,14 +78,15 @@ public class ClosureParameterEnhancer extends AbstractClosureParameterEnhancer {
@Override
@Nullable
protected PsiType getClosureParameterType(GrClosableBlock closure, int index) {
final PsiElement parent = closure.getParent();
if (!(parent instanceof GrMethodCallExpression)) {
PsiElement parent = closure.getParent();
if (parent instanceof GrArgumentList) parent = parent.getParent();
if (!(parent instanceof GrMethodCall)) {
return null;
}
PsiElementFactory factory = JavaPsiFacade.getInstance(closure.getProject()).getElementFactory();
String methodName = findMethodName((GrMethodCallExpression)parent);
String methodName = findMethodName((GrMethodCall)parent);
GrExpression expression = ((GrMethodCallExpression)parent).getInvokedExpression();
GrExpression expression = ((GrMethodCall)parent).getInvokedExpression();
if (!(expression instanceof GrReferenceExpression)) return null;
final PsiElement resolved = ((GrReferenceExpression)expression).resolve();
if (!(resolved instanceof GrGdkMethod)) return null;
@@ -174,7 +176,7 @@ public class ClosureParameterEnhancer extends AbstractClosureParameterEnhancer {
}
}
else if ("withStream".equals(methodName)) {
final PsiMethod method = ((GrMethodCallExpression)parent).resolveMethod();
final PsiMethod method = ((GrMethodCall)parent).resolveMethod();
if (method != null) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length > 0) {
@@ -266,7 +268,7 @@ public class ClosureParameterEnhancer extends AbstractClosureParameterEnhancer {
}
@Nullable
private static String findMethodName(@NotNull GrMethodCallExpression methodCall) {
private static String findMethodName(@NotNull GrMethodCall methodCall) {
GrExpression expression = methodCall.getInvokedExpression();
if (expression instanceof GrReferenceExpression) {
return ((GrReferenceExpression)expression).getReferenceName();
@@ -243,6 +243,10 @@ public class ExpressionsParsingTest extends GroovyParsingTestCase {
public void testcommandExpr$multiArgs() {doTest()}
public void testcommandExpr$RHS() {doTest()}
public void testcommandExpr$oddArgCount() {doTest()}
public void testcommandExpr$indexAccess1() {doTest()}
public void testcommandExpr$indexAccess2() {doTest()}
public void testcommandExpr$indexAccess3() {doTest()}
public void testcommandExpr$closureArg2() {doTest()}
/*def testCommandExpr$() {doTest()}
def testCommandExpr$() {doTest()}
def testCommandExpr$() {doTest()}
@@ -11,6 +11,6 @@ switch (e) {
int preved = 4
break;
case 2:
preved = 5
print preved
def preved = 5
print preved<caret>
}
@@ -0,0 +1,24 @@
foo {a} bar "x"
-----
Groovy script
Call expression
Reference expression
Method call
Reference expression
PsiElement(identifier)('foo')
PsiWhiteSpace(' ')
Arguments
<empty list>
Closable block
PsiElement({)('{')
Parameter list
<empty list>
Reference expression
PsiElement(identifier)('a')
PsiElement(})('}')
PsiWhiteSpace(' ')
PsiElement(identifier)('bar')
PsiWhiteSpace(' ')
Command arguments
Literal
PsiElement(Gstring)('"x"')
@@ -0,0 +1,36 @@
foo a1 a2[1](){} a3 a4
-----
Groovy script
Call expression
Reference expression
Method call
Property by index
Reference expression
Call expression
Reference expression
PsiElement(identifier)('foo')
PsiWhiteSpace(' ')
Command arguments
Reference expression
PsiElement(identifier)('a1')
PsiWhiteSpace(' ')
PsiElement(identifier)('a2')
Arguments
PsiElement([)('[')
Literal
PsiElement(Integer)('1')
PsiElement(])(']')
Arguments
PsiElement(()('(')
PsiElement())(')')
Closable block
PsiElement({)('{')
Parameter list
<empty list>
PsiElement(})('}')
PsiWhiteSpace(' ')
PsiElement(identifier)('a3')
PsiWhiteSpace(' ')
Command arguments
Reference expression
PsiElement(identifier)('a4')
@@ -0,0 +1,35 @@
foo a1 a2[1]{} a3 a4
-----
Groovy script
Call expression
Reference expression
Method call
Property by index
Reference expression
Call expression
Reference expression
PsiElement(identifier)('foo')
PsiWhiteSpace(' ')
Command arguments
Reference expression
PsiElement(identifier)('a1')
PsiWhiteSpace(' ')
PsiElement(identifier)('a2')
Arguments
PsiElement([)('[')
Literal
PsiElement(Integer)('1')
PsiElement(])(']')
Arguments
<empty list>
Closable block
PsiElement({)('{')
Parameter list
<empty list>
PsiElement(})('}')
PsiWhiteSpace(' ')
PsiElement(identifier)('a3')
PsiWhiteSpace(' ')
Command arguments
Reference expression
PsiElement(identifier)('a4')
@@ -0,0 +1,41 @@
foo a1 a2[1](){}{} a3 a4
------
Groovy script
Call expression
Reference expression
Method call
Property by index
Reference expression
Call expression
Reference expression
PsiElement(identifier)('foo')
PsiWhiteSpace(' ')
Command arguments
Reference expression
PsiElement(identifier)('a1')
PsiWhiteSpace(' ')
PsiElement(identifier)('a2')
Arguments
PsiElement([)('[')
Literal
PsiElement(Integer)('1')
PsiElement(])(']')
Arguments
PsiElement(()('(')
PsiElement())(')')
Closable block
PsiElement({)('{')
Parameter list
<empty list>
PsiElement(})('}')
Closable block
PsiElement({)('{')
Parameter list
<empty list>
PsiElement(})('}')
PsiWhiteSpace(' ')
PsiElement(identifier)('a3')
PsiWhiteSpace(' ')
Command arguments
Reference expression
PsiElement(identifier)('a4')