All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.plugins.groovy.lang.parser.GroovyParser Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition groovy-psi library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * Copyright 2000-2014 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.jetbrains.plugins.groovy.lang.parser;

import com.intellij.lang.ASTNode;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.PsiParser;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyBundle;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.parser.parsing.auxiliary.Separators;
import org.jetbrains.plugins.groovy.lang.parser.parsing.auxiliary.modifiers.Modifiers;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.*;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.blocks.OpenOrClosableBlock;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.constructor.ConstructorBody;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.declaration.Declaration;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.AssignmentExpression;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.ConditionalExpression;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.ExpressionStatement;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.imports.ImportStatement;
import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.typeDefinitions.TypeDefinition;
import org.jetbrains.plugins.groovy.lang.parser.parsing.toplevel.CompilationUnit;
import org.jetbrains.plugins.groovy.lang.parser.parsing.util.ParserUtils;

/**
 * Parser for Groovy script files
 *
 * @author ilyas, Dmitry.Krasilschikov
 */
public class GroovyParser implements PsiParser {

  public static final TokenSet RCURLY_ONLY = TokenSet.create(GroovyTokenTypes.mRCURLY);
  public static final TokenSet CASE_SECTION_END = TokenSet.create(GroovyTokenTypes.kCASE, GroovyTokenTypes.kDEFAULT,
                                                                  GroovyTokenTypes.mRCURLY);

  public boolean parseDeep() {
    return false;
  }

  public static void parseExpression(PsiBuilder builder) {
    ExpressionStatement.argParse(builder, new GroovyParser());
  }

  @Override
  @NotNull
  public ASTNode parse(IElementType root, PsiBuilder builder) {
    //builder.setDebugMode(true);
    if (root == GroovyElementTypes.OPEN_BLOCK) {
      OpenOrClosableBlock.parseOpenBlockDeep(builder, this);
    }
    else if (root == GroovyElementTypes.CLOSABLE_BLOCK) {
      OpenOrClosableBlock.parseClosableBlockDeep(builder, this);
    }
    else if (root == GroovyElementTypes.CONSTRUCTOR_BODY) {
      ConstructorBody.parseConstructorBodyDeep(builder, this);
    }
    else {
      assert root == GroovyParserDefinition.GROOVY_FILE : root;
      PsiBuilder.Marker rootMarker = builder.mark();
      CompilationUnit.parseFile(builder, this);
      rootMarker.done(root);
    }
    return builder.getTreeBuilt();
  }

  public boolean parseForStatement(PsiBuilder builder) {
    PsiBuilder.Marker marker = builder.mark();

    ParserUtils.getToken(builder, GroovyTokenTypes.kFOR);
    if (!ParserUtils.getToken(builder, GroovyTokenTypes.mLPAREN, GroovyBundle.message("lparen.expected"))) {
      marker.done(GroovyElementTypes.FOR_STATEMENT);
      return true;
    }
    if (!ForStatement.forClauseParse(builder, this)) {
      builder.error(GroovyBundle.message("for.clause.expected"));
      marker.done(GroovyElementTypes.FOR_STATEMENT);
      return true;
    }

    ParserUtils.getToken(builder, GroovyTokenTypes.mNLS);

    if (!ParserUtils.getToken(builder, GroovyTokenTypes.mRPAREN, GroovyBundle.message("rparen.expected"))) {
      ParserUtils.getToken(builder, GroovyTokenTypes.mNLS);
      marker.done(GroovyElementTypes.FOR_STATEMENT);
      return true;
    }

    PsiBuilder.Marker warn = builder.mark();
    ParserUtils.getToken(builder, GroovyTokenTypes.mNLS);

    if (parseExtendedStatement(builder)) {
      warn.rollbackTo();
      marker.done(GroovyElementTypes.FOR_STATEMENT);
      return true;
    }

    if (parseStatement(builder, true)) {
      warn.drop();
    }
    else {
      warn.rollbackTo();
      builder.error(GroovyBundle.message("statement.expected"));
    }
    marker.done(GroovyElementTypes.FOR_STATEMENT);
    return true;
  }

  public boolean parseIfStatement(PsiBuilder builder) {
    //allow error messages
    PsiBuilder.Marker ifStmtMarker = builder.mark();
    if (!ParserUtils.getToken(builder, GroovyTokenTypes.kIF)) {
      ifStmtMarker.rollbackTo();
      builder.error(GroovyBundle.message("if.expected"));
      return false;
    }

    if (!ParserUtils.getToken(builder, GroovyTokenTypes.mLPAREN, GroovyBundle.message("lparen.expected"))) {
      ifStmtMarker.done(GroovyElementTypes.IF_STATEMENT);
      return true;
    }

    if (!ConditionalExpression.parse(builder, this)) {
      builder.error(GroovyBundle.message("expression.expected"));
    }

    ParserUtils.getToken(builder, GroovyTokenTypes.mNLS);
    ParserUtils.getToken(builder, GroovyTokenTypes.mRPAREN, GroovyBundle.message("rparen.expected"));

    if (!parseBranch(builder)) {
      ifStmtMarker.done(GroovyElementTypes.IF_STATEMENT);
      return true;
    }

    PsiBuilder.Marker rb = builder.mark();
    if (GroovyTokenTypes.kELSE.equals(builder.getTokenType()) ||
        (Separators.parse(builder) && builder.getTokenType() == GroovyTokenTypes.kELSE)) {
      rb.drop();
      ParserUtils.getToken(builder, GroovyTokenTypes.kELSE);

      parseBranch(builder);
    }
    else {
      rb.rollbackTo();
    }

    ifStmtMarker.done(GroovyElementTypes.IF_STATEMENT);
    return true;
  }

  public void parseSwitchCaseList(PsiBuilder builder) {
    if (parseGenericStatement(builder, CASE_SECTION_END)) {
      parseCodeBlock(builder, CASE_SECTION_END);
    }
  }

  //gsp directives, scriptlets and such
  protected boolean isExtendedSeparator(@Nullable final IElementType tokenType) {
    return false;
  }

  //gsp template statement, for example
  protected boolean parseExtendedStatement(PsiBuilder builder) {
    return false;
  }

  public boolean parseWhileStatement(PsiBuilder builder) {

    PsiBuilder.Marker marker = builder.mark();

    ParserUtils.getToken(builder, GroovyTokenTypes.kWHILE);

    if (!ParserUtils.getToken(builder, GroovyTokenTypes.mLPAREN, GroovyBundle.message("lparen.expected"))) {
      marker.done(GroovyElementTypes.WHILE_STATEMENT);
      return true;
    }

    if (!ExpressionStatement.argParse(builder, this)) {
      builder.error(GroovyBundle.message("expression.expected"));
    }

    ParserUtils.getToken(builder, GroovyTokenTypes.mNLS);

    if (!ParserUtils.getToken(builder, GroovyTokenTypes.mRPAREN, GroovyBundle.message("rparen.expected"))) {
      marker.done(GroovyElementTypes.WHILE_STATEMENT);
      return true;
    }

    parseBranch(builder);
    marker.done(GroovyElementTypes.WHILE_STATEMENT);
    return true;
  }

  private boolean parseBranch(@NotNull PsiBuilder builder) {
    PsiBuilder.Marker warn = builder.mark();
    ParserUtils.getToken(builder, GroovyTokenTypes.mNLS);

    if (!parseStatement(builder, true) && !parseExtendedStatement(builder)) {
      warn.rollbackTo();
      builder.error(GroovyBundle.message("statement.expected"));
      return false;
    }
    else {
      warn.drop();
      return true;
    }
  }

  public void parseBlockBody(PsiBuilder builder) {
    skipSeparators(builder);
    parseBlockBodyWithoutSkippingSeparators(builder);
  }

  public void parseBlockBodyWithoutSkippingSeparators(PsiBuilder builder) {
    parseCodeBlock(builder, RCURLY_ONLY);
    ParserUtils.getToken(builder, GroovyTokenTypes.mNLS);
  }

  private void parseCodeBlock(PsiBuilder builder, TokenSet until) {
    while (true) {
      if (builder.eof() || until.contains(builder.getTokenType())) break;
      if (!parseGenericStatement(builder, until)) break;
    }
  }

  private boolean parseGenericStatement(PsiBuilder builder, TokenSet until) {
    boolean plainStatement = parseStatement(builder, false);

    if (plainStatement || parseExtendedStatement(builder)) {
      if (parseSeparatorsWithoutLastNls(builder, plainStatement, until)) {
        return false;
      }
    }
    else {
      builder.error(GroovyBundle.message("wrong.statement"));
      assert builder.getTokenType() != GroovyTokenTypes.mLCURLY && builder.getTokenType() != GroovyTokenTypes.mRCURLY;
      builder.advanceLexer();
    }
    return true;
  }

  private boolean parseSeparatorsWithoutLastNls(PsiBuilder builder, boolean requireSeparator, TokenSet until) {
    boolean hasSeparator = false;
    while (true) {
      while (builder.getTokenType() == GroovyTokenTypes.mSEMI || isExtendedSeparator(builder.getTokenType())) {
        hasSeparator = true;
        builder.advanceLexer();
      }

      if (builder.getTokenType() == GroovyTokenTypes.mNLS) {
        PsiBuilder.Marker beforeNls = builder.mark();
        hasSeparator = true;
        builder.advanceLexer();
        if (builder.eof() || until.contains(builder.getTokenType())) {
          beforeNls.rollbackTo();
          return true;
        }
        beforeNls.drop();
      }
      else {
        break;
      }
    }
    if (builder.eof() || until.contains(builder.getTokenType())) {
      return true;
    }
    if (requireSeparator && !hasSeparator) {
      builder.error(GroovyBundle.message("separator.or.rcurly.expected"));
    }
    return false;
  }

  private boolean skipSeparators(PsiBuilder builder) {
    boolean hasSeparators = false;
    while (builder.getTokenType() == GroovyTokenTypes.mSEMI || isExtendedSeparator(builder.getTokenType()) || builder.getTokenType() ==
                                                                                                              GroovyTokenTypes.mNLS) {
      hasSeparators = true;
      builder.advanceLexer();
    }
    return hasSeparators;
  }

  public boolean parseStatement(PsiBuilder builder, boolean isBlockStatementNeeded) {
    if (isBlockStatementNeeded && GroovyTokenTypes.mLCURLY.equals(builder.getTokenType())) {
      final PsiBuilder.Marker marker = builder.mark();
      OpenOrClosableBlock.parseOpenBlockDeep(builder, this);
      marker.done(GroovyElementTypes.BLOCK_STATEMENT);
      return true;
    }

    if (isBlockStatementNeeded && GroovyTokenTypes.mSEMI == builder.getTokenType()) {
      return true;
    }

    if (GroovyTokenTypes.kIMPORT.equals(builder.getTokenType())) {
      PsiBuilder.Marker marker = builder.mark();
      ImportStatement.parse(builder, this);
      marker.error(GroovyBundle.message("import.not.allowed"));
      return true;
    }

    if (GroovyTokenTypes.kIF.equals(builder.getTokenType())) {
      return parseIfStatement(builder);
    }
    if (GroovyTokenTypes.kSWITCH.equals(builder.getTokenType())) {
      SwitchStatement.parseSwitch(builder, this);
      return true;
    }
    if (GroovyTokenTypes.kTRY.equals(builder.getTokenType())) {
      return TryCatchStatement.parse(builder, this);
    }
    if (GroovyTokenTypes.kWHILE.equals(builder.getTokenType())) {
      return parseWhileStatement(builder);
    }
    if (GroovyTokenTypes.kFOR.equals(builder.getTokenType())) {
      return parseForStatement(builder);
    }
    if (ParserUtils.lookAhead(builder, GroovyTokenTypes.kSYNCHRONIZED, GroovyTokenTypes.mLPAREN)) {
      PsiBuilder.Marker synMarker = builder.mark();
      if (SynchronizedStatement.parse(builder, this)) {
        synMarker.drop();
        return true;
      }
      else {
        synMarker.rollbackTo();
      }
    }

    // Possible errors
    if (GroovyTokenTypes.kELSE.equals(builder.getTokenType())) {
      ParserUtils.wrapError(builder, GroovyBundle.message("else.without.if"));
      parseStatement(builder, true);
      return true;
    }
    if (GroovyTokenTypes.kCATCH.equals(builder.getTokenType())) {
      ParserUtils.wrapError(builder, GroovyBundle.message("catch.without.try"));
      parseStatement(builder, false);
      return true;
    }
    if (GroovyTokenTypes.kFINALLY.equals(builder.getTokenType())) {
      ParserUtils.wrapError(builder, GroovyBundle.message("finally.without.try"));
      parseStatement(builder, false);
      return true;
    }
    if (GroovyTokenTypes.kCASE.equals(builder.getTokenType())) {
      PsiBuilder.Marker marker = builder.mark();
      SwitchStatement.parseCaseLabel(builder, this);
      marker.error(GroovyBundle.message("case.without.switch"));
      parseStatement(builder, false);
      return true;
    }
    if (GroovyTokenTypes.kDEFAULT.equals(builder.getTokenType())) {
      PsiBuilder.Marker marker = builder.mark();
      SwitchStatement.parseCaseLabel(builder, this);
      marker.error(GroovyBundle.message("default.without.switch"));
      parseStatement(builder, false);
      return true;
    }

    if (BranchStatement.BRANCH_KEYWORDS.contains(builder.getTokenType())) {
      return BranchStatement.parse(builder, this);
    }
    if (parseLabeledStatement(builder)) {
      return true;
    }

    if (parseDeclaration(builder, false, false, null)) return true;

    return AssignmentExpression.parse(builder, this, true);
  }

  /**
   * parses imports (marks them as not allowed), type definitions, methods, variables or fields (if isInClass), initializers (if isInClass), constructors
   * with corresponding typeDefinitionName
   * 

* If non of preceding elements was found rolls back and return false */ public boolean parseDeclaration(@NotNull PsiBuilder builder, boolean isInClass, boolean isInAnnotation, @Nullable String typeDefinitionName) { PsiBuilder.Marker declMarker = builder.mark(); boolean modifiersParsed = Modifiers.parse(builder, this); if (GroovyTokenTypes.kIMPORT == builder.getTokenType()) { final PsiBuilder.Marker impMarker = declMarker.precede(); ImportStatement.parseAfterModifiers(builder); declMarker.done(GroovyElementTypes.IMPORT_STATEMENT); impMarker.error(GroovyBundle.message("import.not.allowed")); return true; } if (isTypeDefinitionStart(builder)) { final IElementType tdType = TypeDefinition.parseAfterModifiers(builder, this); if (tdType != GroovyElementTypes.WRONGWAY) { declMarker.done(tdType); } else { builder.error(GroovyBundle.message("identifier.expected")); declMarker.drop(); } return true; } if (isInClass && parseInitializer(builder)) { declMarker.done(GroovyElementTypes.CLASS_INITIALIZER); return true; } final IElementType declType = Declaration.parseAfterModifiers(builder, isInClass, isInAnnotation, typeDefinitionName, this, modifiersParsed); if (declType != GroovyElementTypes.WRONGWAY) { if (declType != null) { declMarker.done(declType); } else { declMarker.drop(); } return true; } if (modifiersParsed) { declMarker.drop(); builder.error(GroovyBundle.message("identifier.expected")); return true; } declMarker.rollbackTo(); return false; } private boolean parseInitializer(PsiBuilder builder) { ParserUtils.getToken(builder, GroovyTokenTypes.mNLS); return GroovyTokenTypes.mLCURLY == builder.getTokenType() && OpenOrClosableBlock.parseOpenBlock(builder, this); } private static boolean isTypeDefinitionStart(PsiBuilder builder) { return GroovyTokenTypes.kCLASS == builder.getTokenType() || //class GroovyTokenTypes.kINTERFACE == builder.getTokenType() || //interface GroovyTokenTypes.kENUM == builder.getTokenType() || //enum GroovyTokenTypes.kTRAIT == builder.getTokenType() || //trait ParserUtils.lookAhead(builder, GroovyTokenTypes.mAT, GroovyTokenTypes.kINTERFACE); //@interface } public boolean parseStatementWithImports(PsiBuilder builder) { if (ImportStatement.parse(builder, this)) { return true; } else { return parseStatement(builder, false); } } private boolean parseLabeledStatement(PsiBuilder builder) { PsiBuilder.Marker marker = builder.mark(); if (!ParserUtils.getToken(builder, GroovyTokenTypes.mIDENT) || !ParserUtils.getToken(builder, GroovyTokenTypes.mCOLON)) { marker.rollbackTo(); return false; } final PsiBuilder.Marker nlsMarker = builder.mark(); ParserUtils.getToken(builder, GroovyTokenTypes.mNLS); if (parseStatement(builder, true)) { nlsMarker.drop(); } else { nlsMarker.rollbackTo(); builder.error(GroovyBundle.message("statement.expected")); } marker.done(GroovyElementTypes.LABELED_STATEMENT); return true; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy