package org.fandev.lang.fan.parsing.statements; import com.intellij.lang.PsiBuilder; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import org.fandev.lang.fan.FanBundle; import static org.fandev.lang.fan.FanBundle.message; 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.types.SimpleTypeSpec; import org.fandev.lang.fan.parsing.types.TypeSpec; import org.fandev.lang.fan.parsing.util.ParserUtils; import static org.fandev.lang.fan.parsing.util.ParserUtils.*; /** * @author Dror Bereznitsky * @date Jan 6, 2009 11:19:40 PM */ public class Statement { public static final TokenSet FOR_STOPPERS = TokenSet.create(SEMICOLON, RPAR); public static final TokenSet RPAR_STOPPER = TokenSet.create(RPAR); public static final TokenSet SWITCH_CASE_STOPPER = TokenSet.create(COLON, RBRACE); public static final TokenSet CLOSURE_EOS = TokenSet.orSet(EOS, TokenSet.create(COMMA)); public static boolean parse(final PsiBuilder builder) { return parse(builder, false); } public static boolean parse(final PsiBuilder builder, final boolean inClosure) { IElementType statementType = null; final PsiBuilder.Marker statementMark = builder.mark(); final IElementType tokenType = builder.getTokenType(); if (BREAK_KEYWORD.equals(tokenType) || CONTINUE_KEYWORD.equals(tokenType)) { builder.advanceLexer(); // Should have EOS if (EOS.contains(builder.getTokenType())) { removeStoppers(builder, SEPARATOR, SEPARATOR); statementType = CONTROL_FLOW; } else { builder.error(message("separator.expected")); } } else if (FOR_KEYWORD.equals(tokenType)) { statementType = FOR_STATEMENT; parseFor(builder); } else if (IF_KEYWORD.equals(tokenType)) { statementType = IF_STATEMENT; parseIf(builder); } else if (RETURN_KEYWORD.equals(tokenType)) { statementType = RETURN_STATEMENT; parseReturnExpression(builder); } else if (SWITCH_KEYWORD.equals(tokenType)) { statementType = SWITCH_STATEMENT; parseSwitch(builder); } else if (THROW_KEYWORD.equals(tokenType)) { statementType = THROW_STATEMENT; parseThrowExpression(builder); } else if (WHILE_KEYWORD.equals(tokenType)) { statementType = WHILE_STATEMENT; parseWhile(builder); } else if (TRY_KEYWORD.equals(tokenType)) { statementType = TRY_STATEMENT; parseTry(builder); } else { final TokenSet stopper = inClosure ? CLOSURE_EOS : EOS; final boolean res = expressionOrLocalDef(builder, stopper, EXPRESSION, LOCAL_DEF_STATEMENT); if (inClosure && res && getToken(builder, COMMA)) { statementMark.done(IT_ADD_STATEMENT); } else { statementMark.drop(); } removeStoppers(builder, SEPARATOR, SEPARATOR); return res; } if (statementType != null) { statementMark.done(statementType); removeStoppers(builder, SEPARATOR, SEPARATOR); return true; } else { statementMark.drop(); return false; } } private static boolean parseFor(final PsiBuilder builder) { if (!getToken(builder, FOR_KEYWORD, message("keywords.expected", FOR_KEYWORD))) { return false; } removeNls(builder); getToken(builder, LPAR, message("lpar.expected")); expressionOrLocalDef(builder, FOR_STOPPERS, FOR_INIT_EXPR, FOR_INIT_LOCAL_DEF); removeNls(builder); getToken(builder, SEMICOLON, message("semicolon.expected")); removeNls(builder); Expression.parseExpr(builder, FOR_STOPPERS, FOR_CONDITION); removeNls(builder); getToken(builder, SEMICOLON, message("semicolon.expected")); removeNls(builder); Expression.parseExpr(builder, FOR_STOPPERS, FOR_REPEAT); removeNls(builder); getToken(builder, RPAR, message("rpar.expected")); removeNls(builder); Block.parse(builder, FOR_BLOCK); removeNls(builder); return true; } private static boolean expressionOrLocalDef(final PsiBuilder builder, final TokenSet stopper, final IElementType exprType, final IElementType localDefType) { removeNls(builder); // Initialization it can only be an Type IDENTIFIER follow by := So { is a stopper final TokenSet lookAheadStoppers = TokenSet.orSet(stopper, TokenSet.create(LBRACE)); if (ParserUtils.lookAheadForElement(builder, COLON_EQ, lookAheadStoppers)) { return parseLocalDef(builder, stopper, localDefType); } else { // Simple expression return Expression.parseExpr(builder, stopper, exprType); } } private static boolean parseLocalDef(final PsiBuilder builder, final TokenSet stopper, final IElementType localDefType) { boolean res = true; // Local def final PsiBuilder.Marker localDef = builder.mark(); // Type is optional, find if there is one by doing without it and if it fails fallback to type final PsiBuilder.Marker nameMark = builder.mark(); if (getToken(builder, IDENTIFIER_TOKENS_SET) && COLON_EQ == builder.getTokenType()) { nameMark.done(NAME_ELEMENT); } else { nameMark.rollbackTo(); res = TypeSpec.parse(builder); if (res) { removeNls(builder); res = ParserUtils.parseName(builder); } } if (res && getToken(builder, COLON_EQ, FanBundle.message("localDef.assign.expected"))) { removeNls(builder); res = Expression.parseExpr(builder, stopper, PARAM_DEFAULT); } if (res) { localDef.done(localDefType); return true; } else { // TODO: Check if I should drop it? localDef.done(localDefType); return false; } } private static boolean parseIf(final PsiBuilder builder) { if (!getToken(builder, IF_KEYWORD, message("keywords.expected", IF_KEYWORD))) { return false; } boolean res; parseIfCondition(builder); res = Block.parse(builder, COND_TRUE_BLOCK); removeNls(builder); while (res && !builder.eof() && getToken(builder, ELSE_KEYWORD)) { removeNls(builder); if (getToken(builder, IF_KEYWORD)) { parseIfCondition(builder); res = Block.parse(builder, COND_TRUE_BLOCK); } else { res = Block.parse(builder, COND_FALSE_BLOCK); } removeNls(builder); } return res; } private static void parseIfCondition(final PsiBuilder builder) { removeNls(builder); getToken(builder, LPAR, message("lpar.expected")); removeNls(builder); Expression.parseExpr(builder, RPAR_STOPPER, CONDITION_EXPR); getToken(builder, RPAR, message("rpar.expected")); removeNls(builder); } private static boolean parseReturnExpression(final PsiBuilder builder) { if (!getToken(builder, RETURN_KEYWORD, message("keywords.expected", RETURN_KEYWORD))) { return false; } boolean res = true; if (!EOS.contains(builder.getTokenType())) { res = Expression.parseExpr(builder, EOS, EXPRESSION); removeStoppers(builder, SEPARATOR, SEPARATOR); } return res; } private static boolean parseThrowExpression(final PsiBuilder builder) { if (!getToken(builder, THROW_KEYWORD, message("keywords.expected", THROW_KEYWORD))) { return false; } boolean res = Expression.parseExpr(builder, EOS, EXPRESSION); removeStoppers(builder, SEPARATOR, SEPARATOR); return res; } private static boolean parseSwitch(final PsiBuilder builder) { if (!getToken(builder, SWITCH_KEYWORD, message("keywords.expected", SWITCH_KEYWORD))) { return false; } removeNls(builder); getToken(builder, LPAR, message("lpar.expected")); removeNls(builder); Expression.parseExpr(builder, RPAR_STOPPER, SWITCH_VALUE); getToken(builder, RPAR, message("rpar.expected")); removeNls(builder); getToken(builder, LBRACE, message("lcurly.expected")); removeNls(builder); boolean hasDefault = false; while (!builder.eof() && !RBRACE.equals(builder.getTokenType())) { final PsiBuilder.Marker inSwitchMark = builder.mark(); if (getToken(builder, CASE_KEYWORD)) { if (hasDefault) { builder.error(message("case.after.default")); } Expression.parseExpr(builder, SWITCH_CASE_STOPPER, SWITCH_CASE_VALUE); } else if (getToken(builder, DEFAULT_KEYWORD)) { hasDefault = true; } else { inSwitchMark.error(message("case.default.expected")); advanceNoNls(builder); continue; } if (getToken(builder, COLON, message("colon.expected"))) { removeNls(builder); final PsiBuilder.Marker mark = builder.mark(); while (!builder.eof() && !SWITCH_BLOCK_TOKENS.contains(builder.getTokenType())) { Statement.parse(builder); } mark.done(SWITCH_CASE_STATEMENT); } inSwitchMark.done(SWITCH_CASE); removeNls(builder); } getToken(builder, RBRACE, message("rcurly.expected")); removeNls(builder); return true; } private static boolean parseWhile(final PsiBuilder builder) { if (!getToken(builder, WHILE_KEYWORD, message("keywords.expected", WHILE_KEYWORD))) { return false; } removeNls(builder); getToken(builder, LPAR, message("lpar.expected")); removeNls(builder); Expression.parseExpr(builder, RPAR_STOPPER, WHILE_CONDITION); getToken(builder, RPAR, message("rpar.expected")); removeNls(builder); Block.parse(builder, WHILE_BLOCK); removeNls(builder); return true; } private static boolean parseTry(final PsiBuilder builder) { if (!getToken(builder, TRY_KEYWORD, message("keywords.expected", TRY_KEYWORD))) { return false; } removeNls(builder); Block.parse(builder, TRY_BLOCK); removeNls(builder); if (!TRY_BLOCK_TOKENS.contains(builder.getTokenType())) { builder.error(message("catch.finally.expected")); } else { boolean hasFinally = false; while (!builder.eof() && TRY_BLOCK_TOKENS.contains(builder.getTokenType())) { final PsiBuilder.Marker catchMark = builder.mark(); if (getToken(builder, CATCH_KEYWORD)) { if (hasFinally) { builder.error(message("catch.after.finally")); } removeNls(builder); // Catch definition: All Exceptions no obj is empty if (getToken(builder, LPAR)) { removeNls(builder); SimpleTypeSpec.parseSimpleType(builder, false); removeNls(builder); final PsiBuilder.Marker nameMarker = builder.mark(); if (getToken(builder, IDENTIFIER_TOKENS_SET, message("identifier.expected"))) { nameMarker.done(NAME_ELEMENT); } else { nameMarker.drop(); } removeNls(builder); getToken(builder, RPAR, message("rpar.expected")); removeNls(builder); } Block.parse(builder, CATCH_BLOCK); catchMark.done(CATCH_STATEMENT); } else if (getToken(builder, FINALLY_KEYWORD)) { hasFinally = true; removeNls(builder); Block.parse(builder, FINALLY_BLOCK); catchMark.done(FINALLY_STATEMENT); } else { catchMark.drop(); break; } removeNls(builder); } } return true; } }