/*
* Copyright 2000-2007 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 org.fandev.lang.fan.parsing.expression.arithmetic;
import com.intellij.lang.PsiBuilder;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.fandev.lang.fan.FanBundle;
import org.fandev.lang.fan.FanElementTypes;
import static org.fandev.lang.fan.FanElementTypes.*;
import static org.fandev.lang.fan.FanTokenTypes.*;
import org.fandev.lang.fan.parsing.expression.Expression;
import org.fandev.lang.fan.parsing.expression.argument.LiteralExpression;
import org.fandev.lang.fan.parsing.statements.Block;
import org.fandev.lang.fan.parsing.statements.expressions.arguments.Arguments;
import org.fandev.lang.fan.parsing.statements.typeDefinitions.members.FieldDefinition;
import org.fandev.lang.fan.parsing.statements.typeDefinitions.members.PropertyBlock;
import org.fandev.lang.fan.parsing.util.ParserUtils;
import static org.fandev.lang.fan.parsing.util.ParserUtils.*;
/**
* @author ilyas
*/
public class TermExpression {
private static final TokenSet DOTS = TokenSet.create(
DOT,
DYN_CALL,
SAFE_DOT,
SAFE_DYN_CALL
);
public static boolean parse(final PsiBuilder builder, final TokenSet stopper) {
final PsiBuilder.Marker marker = builder.mark();
final TokenSet newStopper = TokenSet.orSet(stopper, DOTS);
boolean res = parseBase(builder, newStopper);
if (res && (hasDot(builder) || !stopper.contains(builder.getTokenType()))) {
res = parseTermChainLoop(builder, stopper);
}
if (res) {
// Check for with block with look ahead
if (parseWithBlock(builder) == null) {
res = false;
}
}
marker.done(TERM_EXPR);
return res;
}
public static boolean parseTermChainLoop(final PsiBuilder builder, final TokenSet stopper) {
boolean res = true;
if (!stopper.contains(NLS)) {
removeNls(builder);
}
while (res && !builder.eof() && (hasDot(builder) || !stopper.contains(builder.getTokenType()))) {
res = parseTermChain(builder, stopper);
if (!stopper.contains(NLS)) {
// Remove NLS if not part of the stoppers (Meaning NLS has no meaning in this expression)
removeNls(builder);
}
}
return res;
}
public static boolean parseBase(final PsiBuilder builder, final TokenSet stopper) {
if (LiteralExpression.parse(builder, stopper)) {
return true;
}
if (IdExpression.parse(builder)) {
return true;
}
return ClosureExpression.parse(builder);
}
private static boolean parseTermChain(final PsiBuilder builder, final TokenSet stopper) {
if (hasDot(builder)) {
// Dot call
removeNls(builder);
advanceNoNls(builder);
// Can be .super for named super
if (SUPER_KEYWORD == builder.getTokenType()) {
builder.advanceLexer();
} else {
IdExpression.parse(builder);
}
} else if (LBRACKET.equals(builder.getTokenType())) {
// Index expression
advanceNoNls(builder);
boolean res = true;
while (res && !builder.eof() && RBRACKET != builder.getTokenType()) {
res = Expression.parseExpr(builder, TokenSet.create(RBRACKET), FanElementTypes.INDEX_EXPR);
removeNls(builder);
}
getToken(builder, RBRACKET, FanBundle.message("rbrack.expected"));
} else if (LPAR.equals(builder.getTokenType())) {
// Arguments
removeNls(builder);
Arguments.parse(builder);
removeNls(builder);
// May have closure after
ClosureExpression.parse(builder);
} else {
return parseWithBlock(builder) == WITH_BLOCK_EXPR;
}
return true;
}
private static boolean hasDot(final PsiBuilder builder) {
// TODO: Mege dots and with block lookahead without stopper test
return DOTS.contains(ParserUtils.firstAfter(builder,NLS));
}
private static IElementType parseWithBlock(final PsiBuilder builder) {
if (ParserUtils.firstAfter(builder, NLS) == LBRACE) {
if (builder.getUserData(FieldDefinition.FIELD_NAME) != null) {
// May be get and set block
final PropertyBlock block = FieldDefinition.findPropertyBlockType(builder);
if (block != PropertyBlock.NONE) {
// Should be parsed in FieldDefinition
return WRONGWAY;
}
}
removeNls(builder);
if (Block.parse(builder, WITH_BLOCK_EXPR)) {
return WITH_BLOCK_EXPR;
} else {
return null;
}
}
return WRONGWAY;
}
}