/**
   * Named list operators
   *
   * @param b PerlBuilder
   * @param l Parsing level
   * @return parsing result
   */
  public static boolean isListOperator(PsiBuilder b, int l) {
    PerlTokenData prevTokenData = ((PerlBuilder) b).lookupToken(-1);

    if (prevTokenData != null && prevTokenData.getTokenType() == OPERATOR_DEREFERENCE) return false;

    IElementType tokenType = b.getTokenType();
    IElementType nextTokenType = b.lookAhead(1);

    if (CONVERTABLE_TOKENS.contains(tokenType)
        && nextTokenType != LEFT_PAREN // not function call
        && !PACKAGE_TOKENS.contains(nextTokenType) // not method Package::
        && !(nextTokenType == IDENTIFIER
            && ((PerlBuilder) b)
                .isKnownPackage(
                    ((PerlBuilder) b).lookupToken(1).getTokenText())) // not Method Package
    )
      // todo we should check current namespace here
      return !PerlSubUtil.BUILT_IN_UNARY.contains(b.getTokenText());
    else if (PACKAGE_TOKENS.contains(tokenType)
        && CONVERTABLE_TOKENS.contains(nextTokenType)
        && b.lookAhead(2) != LEFT_PAREN)
      return !PerlSubUtil.isUnary(
          b.getTokenText(), ((PerlBuilder) b).lookupToken(1).getTokenText());

    return false;
  }
  /**
   * Smart parser for ->, makes }->[ optional
   *
   * @param b PerlBuilder
   * @param l parsing level
   * @return parsing result
   */
  public static boolean parseArrowSmart(PsiBuilder b, int l) {
    IElementType tokenType = b.getTokenType();
    if (b.getTokenType() == OPERATOR_DEREFERENCE) {
      return consumeToken(b, OPERATOR_DEREFERENCE);
    } else {
      assert b instanceof PerlBuilder;
      PerlTokenData prevToken = ((PerlBuilder) b).lookupToken(-1);
      IElementType prevTokenType = prevToken == null ? null : prevToken.getTokenType();

      // optional }->[ or ]->{
      if ((prevTokenType == RIGHT_BRACE || prevTokenType == RIGHT_BRACKET)
          && (tokenType == LEFT_BRACE || tokenType == LEFT_BRACKET || tokenType == LEFT_PAREN))
        return true;
    }

    return false;
  }
  /**
   * Named unary operators
   *
   * @param b PerlBuilder
   * @param l parsing level
   * @return parsing result
   */
  public static boolean isUnaryOperator(PsiBuilder b, int l) {
    assert b instanceof PerlBuilder;
    PerlTokenData prevTokenData = ((PerlBuilder) b).lookupToken(-1);

    if (prevTokenData != null && prevTokenData.getTokenType() == OPERATOR_DEREFERENCE) return false;

    IElementType tokenType = b.getTokenType();
    IElementType nextTokenType = b.lookAhead(1);

    if (CONVERTABLE_TOKENS.contains(tokenType)
        && nextTokenType != LEFT_PAREN
        && !PACKAGE_TOKENS.contains(nextTokenType))
      // todo we should check current namespace here
      return PerlSubUtil.BUILT_IN_UNARY.contains(b.getTokenText());
    else if (PACKAGE_TOKENS.contains(tokenType)
        && CONVERTABLE_TOKENS.contains(SUB)
        && b.lookAhead(2) != LEFT_PAREN) {
      PerlTokenData nextToken = ((PerlBuilder) b).lookupToken(1);
      if (nextToken != null) return PerlSubUtil.isUnary(b.getTokenText(), nextToken.getTokenText());
    }

    return false;
  }
  /**
   * Parses invocable method As input we may have: PACKAGE_IDENTIFIER IDENTIFIER Foo::sub IDENTIFIER
   * PACKAGE_IDENTIFIER sub Foo:: IDENTIFIER IDENTIFIER sub Foo
   *
   * @param b PerlBuilder
   * @param l parsing level
   * @return parsing result
   */
  public static boolean parseMethod(PsiBuilder b, int l) {
    IElementType currentTokenType = b.getTokenType();
    IElementType nextTokenType = b.lookAhead(1);

    assert b instanceof PerlBuilder;

    // can be
    // 	Foo::method
    //  Foo::Bar
    if (PACKAGE_TOKENS.contains(currentTokenType) && CONVERTABLE_TOKENS.contains(nextTokenType)) {
      PerlTokenData nextTokenData = ((PerlBuilder) b).lookupToken(1);
      PerlTokenData nextNextTokenData = ((PerlBuilder) b).lookupToken(2);

      IElementType nextNextTokenType =
          nextNextTokenData == null ? null : nextNextTokenData.getTokenType();

      String canonicalPackageName = PerlPackageUtil.getCanonicalPackageName(b.getTokenText());
      String potentialSubName = canonicalPackageName + "::" + nextTokenData.getTokenText();

      if (nextNextTokenType == LEFT_PAREN // Package::Identifier( - what can it be?
          || ((PerlBuilder) b).isKnownSub(potentialSubName) // we know this sub
          || !((PerlBuilder) b).isKnownPackage(potentialSubName)) // we don't know such package
      return convertPackageIdentifier(b, l) && convertIdentifier(b, l, SUB);
      else return false;
    }
    // 	method
    else if (CONVERTABLE_TOKENS.contains(currentTokenType)) {
      PerlTokenData prevTokenData = ((PerlBuilder) b).lookupToken(-1);

      // ->sub
      if (prevTokenData != null && prevTokenData.getTokenType() == OPERATOR_DEREFERENCE)
        return convertIdentifier(b, l, SUB);
      // may be
      // 	method Foo::
      //	method Foo::Bar
      //  method Foo::othermethod
      else if (PACKAGE_TOKENS.contains(nextTokenType)) {
        IElementType nextNextTokenType = b.lookAhead(2);

        // sub Foo::->method
        if (nextNextTokenType == OPERATOR_DEREFERENCE) return convertIdentifier(b, l, SUB);
        // identifier Package::identifier
        else if (CONVERTABLE_TOKENS.contains(nextNextTokenType)) {

          // identifier Package::identifier->
          if (b.lookAhead(3) == OPERATOR_DEREFERENCE) return convertIdentifier(b, l, SUB);

          PerlTokenData nextTokenData = ((PerlBuilder) b).lookupToken(1);
          PerlTokenData nextNextTokenData = ((PerlBuilder) b).lookupToken(2);

          String packageOrSub =
              PerlPackageUtil.getCanonicalPackageName(nextTokenData.getTokenText())
                  + "::"
                  + nextNextTokenData.getTokenText();

          if (((PerlBuilder) b).isKnownSub(packageOrSub)) return convertIdentifier(b, l, SUB);
          else if (((PerlBuilder) b).isKnownPackage(packageOrSub))
            return convertIdentifier(b, l, SUB) && mergePackageName(b, l);
          return convertIdentifier(b, l, SUB);
        } else
          // it's method Package::
          return convertIdentifier(b, l, SUB) && convertPackageIdentifier(b, l);
      }
      // may be
      // 	method Foo
      else if (CONVERTABLE_TOKENS.contains(nextTokenType)
          && b.lookAhead(2) != OPERATOR_DEREFERENCE) {
        PerlTokenData nextTokenData = ((PerlBuilder) b).lookupToken(1);

        String potentialSubName = nextTokenData.getTokenText() + "::" + b.getTokenText();
        if (((PerlBuilder) b).isKnownSub(potentialSubName))
          return convertIdentifier(b, l, SUB) && convertIdentifier(b, l, PACKAGE);
        else return convertIdentifier(b, l, SUB);
      }
      // KnownPackage->
      else if (nextTokenType == OPERATOR_DEREFERENCE
          && ((PerlBuilder) b).isKnownPackage(b.getTokenText())) return false;
      // it's just sub
      else return convertIdentifier(b, l, SUB);
    }

    return false;
  }