/*
* Copyright 2013-2017 consulo.io
*
* 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 consulo.csharp.lang.parser.decl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import consulo.csharp.lang.parser.CSharpBuilderWrapper;
import consulo.csharp.lang.parser.ModifierSet;
import consulo.csharp.lang.parser.SharedParsingHelpers;
import consulo.csharp.lang.parser.UsingStatementParsing;
import consulo.csharp.lang.parser.exp.ExpressionParsing;
import consulo.csharp.lang.psi.CSharpStubElements;
import consulo.csharp.lang.psi.CSharpTokenSets;
import consulo.csharp.lang.psi.CSharpTokens;
import com.intellij.lang.PsiBuilder;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.NotNullFunction;
/**
* @author VISTALL
* @since 28.11.13.
*/
public class DeclarationParsing extends SharedParsingHelpers
{
// { (
private static final TokenSet NAME_STOPPERS = TokenSet.create(LBRACE, LPAR, THIS_KEYWORD);
public static void parseAll(@NotNull CSharpBuilderWrapper builder, boolean root, boolean isEnum)
{
if(isEnum)
{
IElementType prevToken;
while(!builder.eof())
{
prevToken = builder.getTokenType();
parseEnumConstant(builder, ModifierSet.EMPTY);
if(builder.getTokenType() == COMMA)
{
if(prevToken == COMMA)
{
builder.error("Name expected");
}
builder.advanceLexer();
}
else if(builder.getTokenType() == RBRACE)
{
break;
}
else
{
PsiBuilder.Marker errorMarker = builder.mark();
builder.advanceLexer();
errorMarker.error("Expected comma");
}
}
}
else
{
while(!builder.eof())
{
if(!root && builder.getTokenType() == RBRACE)
{
return;
}
builder.skipNonInterestItems();
parse(builder, root);
}
}
}
private static void parse(@NotNull CSharpBuilderWrapper builder, boolean root)
{
PsiBuilder.Marker marker = builder.mark();
Pair<PsiBuilder.Marker, ModifierSet> modifierListPair = parseWithSoftElements(new NotNullFunction<CSharpBuilderWrapper, Pair<PsiBuilder.Marker, ModifierSet>>()
{
@NotNull
@Override
public Pair<PsiBuilder.Marker, ModifierSet> fun(CSharpBuilderWrapper builderWrapper)
{
return parseModifierListWithAttributes(builderWrapper, STUB_SUPPORT);
}
}, builder, PARTIAL_KEYWORD, ASYNC_KEYWORD);
PsiBuilder.Marker modifierListMarker = modifierListPair.getFirst();
ModifierSet modifierSet = modifierListPair.getSecond();
IElementType tokenType = builder.getTokenType();
if(tokenType == null)
{
if(modifierListPair.getSecond().isEmpty())
{
marker.drop();
}
else
{
if(root)
{
marker.done(DUMMY_DECLARATION);
}
else
{
marker.error("Expected identifier");
}
}
return;
}
if(tokenType == NAMESPACE_KEYWORD)
{
NamespaceDeclarationParsing.parse(builder, marker);
}
else if(CSharpTokenSets.TYPE_DECLARATION_START.contains(tokenType))
{
TypeDeclarationParsing.parse(builder, marker);
}
else if(tokenType == EVENT_KEYWORD)
{
builder.advanceLexer();
EventParsing.parse(builder, marker, modifierSet);
}
else if(tokenType == DELEGATE_KEYWORD)
{
builder.advanceLexer();
MethodParsing.parseMethodStartAtType(builder, marker, modifierSet);
}
else if(tokenType == USING_KEYWORD)
{
UsingStatementParsing.parseUsing(builder, marker);
}
else if(tokenType == CONST_KEYWORD)
{
builder.advanceLexer();
FieldOrPropertyParsing.parseFieldOrLocalVariableAtTypeWithDone(builder, marker, FIELD_DECLARATION, STUB_SUPPORT, true, modifierSet);
}
else
{
// MODIFIER_LIST IDENTIFIER LPAR -> CONSTRUCTOR
if(tokenType == CSharpTokens.IDENTIFIER && builder.lookAhead(1) == LPAR)
{
MethodParsing.parseMethodStartAfterType(builder, marker, null, MethodParsing.Target.CONSTRUCTOR, modifierSet);
}
else if(tokenType == TILDE)
{
builder.advanceLexer();
MethodParsing.parseMethodStartAfterType(builder, marker, null, MethodParsing.Target.DECONSTRUCTOR, modifierSet);
}
else
{
TypeInfo typeInfo = parseType(builder, STUB_SUPPORT);
if(typeInfo == null)
{
if(!modifierSet.isEmpty())
{
if(root)
{
marker.done(DUMMY_DECLARATION);
return;
}
builder.error("Type expected");
marker.done(FIELD_DECLARATION);
return;
}
else
{
modifierListMarker.drop();
}
marker.drop();
advanceUnexpectedToken(builder);
}
else if(builder.getTokenType() == OPERATOR_KEYWORD)
{
MethodParsing.parseMethodStartAfterType(builder, marker, typeInfo, MethodParsing.Target.METHOD, modifierSet);
}
else
{
TypeInfo implementType = parseImplementType(builder);
if(implementType == null)
{
builder.error("Name is expected");
// if we dont have name but we have lbracket - parse as index method
parseAfterName(builder, marker, builder.getTokenType() == LBRACKET ? THIS_KEYWORD : null, modifierSet);
return;
}
IElementType prevToken = null;
if(builder.getTokenType() == DOT)
{
builder.advanceLexer();
prevToken = builder.getTokenType();
doneThisOrIdentifier(builder);
}
else
{
if(implementType.marker != null)
{
implementType.marker.rollbackTo();
}
prevToken = builder.getTokenType();
doneThisOrIdentifier(builder);
}
parseAfterName(builder, marker, prevToken, modifierSet);
}
}
}
}
public static void doneThisOrIdentifier(CSharpBuilderWrapper builder)
{
if(builder.getTokenType() == THIS_KEYWORD)
{
builder.advanceLexer();
}
else
{
expectOrReportIdentifier(builder, STUB_SUPPORT);
}
}
private static boolean parseEnumConstant(CSharpBuilderWrapper builder, ModifierSet set)
{
if(builder.getTokenType() == RBRACE)
{
return true;
}
PsiBuilder.Marker mark = builder.mark();
boolean nameExpected = false;
if(builder.getTokenType() == LBRACKET)
{
PsiBuilder.Marker modMark = builder.mark();
parseAttributeList(builder, set, STUB_SUPPORT);
modMark.done(CSharpStubElements.MODIFIER_LIST);
nameExpected = true;
}
if(builder.getTokenType() == CSharpTokens.IDENTIFIER)
{
if(!nameExpected)
{
emptyElement(builder, CSharpStubElements.MODIFIER_LIST);
}
doneIdentifier(builder, STUB_SUPPORT);
if(builder.getTokenType() == EQ)
{
builder.advanceLexer();
if(ExpressionParsing.parse(builder, set) == null)
{
builder.error("Expression expected");
}
}
}
else
{
if(builder.getTokenType() == COMMA || builder.getTokenType() == RBRACE)
{
if(nameExpected)
{
PsiBuilder.Marker identifierMarker = builder.mark();
builder.error("Name expected");
identifierMarker.done(CSharpStubElements.IDENTIFIER);
}
done(mark, ENUM_CONSTANT_DECLARATION);
return false;
}
}
done(mark, ENUM_CONSTANT_DECLARATION);
return true;
}
private static void parseAfterName(CSharpBuilderWrapper builder, PsiBuilder.Marker marker, @Nullable IElementType prevToken, ModifierSet set)
{
if(prevToken == THIS_KEYWORD)
{
FieldOrPropertyParsing.parseArrayAfterThis(builder, marker, set);
}
else if(builder.getTokenType() == LPAR || builder.getTokenType() == LT) // MODIFIER_LIST TYPE IDENTIFIER LPAR -> METHOD
{
MethodParsing.parseMethodStartAfterName(builder, marker, MethodParsing.Target.METHOD, set);
}
else
{
FieldOrPropertyParsing.parseFieldOrPropertyAfterName(builder, marker, set);
}
}
@Nullable
public static TypeInfo parseImplementType(CSharpBuilderWrapper builder)
{
IElementType tokenType = builder.getTokenType();
if(tokenType == THIS_KEYWORD)
{
return new TypeInfo();
}
return parseType(builder, STUB_SUPPORT, NAME_STOPPERS);
}
}