protected boolean expression()

in src/org/intellij/grammar/livePreview/LivePreviewParser.java [134:312]


  protected boolean expression(PsiBuilder builder,
                               int level,
                               BnfRule rule,
                               BnfExpression initialNode,
                               String funcName,
                               Map<String, Parser> externalArguments) {
    boolean isRule = initialNode.getParent() == rule;
    BnfExpression node = getNonTrivialNode(initialNode);

    IElementType type = getEffectiveType(node);

    boolean firstNonTrivial = node == Rule.firstNotTrivial(rule);
    boolean isPrivate = !(isRule || firstNonTrivial) || Rule.isPrivate(rule) || myGrammarRoot == rule;
    boolean isLeft = firstNonTrivial && Rule.isLeft(rule);
    boolean isLeftInner = isLeft && (isPrivate || Rule.isInner(rule));
    boolean isBranch = !isPrivate && Rule.isUpper(rule);
    String recoverWhile = firstNonTrivial ? getAttribute(rule, KnownAttribute.RECOVER_WHILE) : null;
    Map<String, String> hooks = firstNonTrivial ? getAttribute(rule, KnownAttribute.HOOKS).asMap() : Collections.emptyMap();
    boolean canCollapse = !isPrivate && (!isLeft || isLeftInner) && firstNonTrivial && myGraphHelper.canCollapse(rule);

    IElementType elementType = !isPrivate ? getRuleElementType(rule) : null;

    boolean isSingleNode = node instanceof BnfReferenceOrToken || node instanceof BnfLiteralExpression || node instanceof BnfExternalExpression;

    List<BnfExpression> children = isSingleNode ? Collections.singletonList(node) : getChildExpressions(node);
    String frameName = !children.isEmpty() && firstNonTrivial && !Rule.isMeta(rule) ? getRuleDisplayName(rule, !isPrivate) : null;

    if (isSingleNode) {
      children = Collections.singletonList(node);
      if (isPrivate && !isLeftInner && recoverWhile == null && frameName == null) {
        return generateNodeCall(builder, level, rule, node, getNextName(funcName, 0), externalArguments);
      }
      else {
        type = BNF_SEQUENCE;
      }
    }
    if (!children.isEmpty()) {
      if (!recursion_guard_(builder, level, funcName)) return false;
    }

    //if (recoverRoot == null && (isRule || firstNonTrivial)) {
    //  frameName = generateFirstCheck(rule, frameName, true);
    //}

    PinMatcher pinMatcher = new PinMatcher(rule, type, firstNonTrivial ? rule.getName() : funcName);
    boolean pinApplied = false;
    boolean alwaysTrue = type == BNF_OP_OPT || type == BNF_OP_ZEROMORE;

    boolean result_ = type == BNF_OP_ZEROMORE || type == BNF_OP_OPT || children.isEmpty();
    boolean pinned = pinMatcher.active();
    boolean pinned_ = false;

    int modifiers = 0;
    if (canCollapse) modifiers |= _COLLAPSE_;
    if (isLeftInner) modifiers |= _LEFT_INNER_;
    else if (isLeft) modifiers |= _LEFT_;
    if (type == BNF_OP_AND) modifiers |= _AND_;
    else if (type == BNF_OP_NOT) modifiers |= _NOT_;
    if (isBranch) modifiers |= _UPPER_;

    PsiBuilder.Marker marker_ = null;
    boolean sectionRequired = !alwaysTrue || !isPrivate || isLeft || recoverWhile != null;
    boolean sectionRequiredSimple = sectionRequired && modifiers == _NONE_ && recoverWhile == null && !(pinned || frameName != null);
    boolean sectionMaybeDropped = sectionRequiredSimple && type == BNF_CHOICE && elementType == null &&
                                  !ContainerUtil.exists(children, o -> isRollbackRequired(o, myFile));

    if (sectionRequiredSimple) {
      if (!sectionMaybeDropped) {
        marker_ = enter_section_(builder);
      }
    }
    else if (sectionRequired) {
      marker_ = enter_section_(builder, level, modifiers, elementType, frameName);
    }

    int[] skip = {0};
    for (int i = 0, p = 0, childrenSize = children.size(); i < childrenSize; i++) {
      BnfExpression child = children.get(i);

      if (type == BNF_CHOICE) {
        if (i == 0) result_ = generateNodeCall(builder, level, rule, child, getNextName(funcName, i), externalArguments);
        else if (!result_) result_ = generateNodeCall(builder, level, rule, child, getNextName(funcName, i), externalArguments);
      }
      else if (type == BNF_SEQUENCE) {
        if (skip[0] == 0) {
          if (i == 0) {
            result_ = generateTokenSequenceCall(builder, level, rule, children, funcName, i, pinMatcher, pinApplied, skip, externalArguments);
          }
          else {
            if (pinApplied && G.generateExtendedPin) {
              if (i == childrenSize - 1) {
                // do not report error for last child
                if (i == p + 1) {
                  result_ = result_ && generateTokenSequenceCall(builder, level, rule, children, funcName, i, pinMatcher, pinApplied, skip, externalArguments);
                }
                else {
                  result_ = pinned_ && generateTokenSequenceCall(builder, level, rule, children, funcName, i, pinMatcher, pinApplied, skip, externalArguments) && result_;
                }
              }
              else if (i == p + 1) {
                result_ = result_ && report_error_(builder, generateTokenSequenceCall(builder, level, rule, children, funcName, i, pinMatcher, pinApplied, skip, externalArguments));
              }
              else {
                result_ = pinned_ && report_error_(builder, generateTokenSequenceCall(builder, level, rule, children, funcName, i, pinMatcher, pinApplied, skip, externalArguments)) && result_;
              }
            }
            else {
              result_ = result_ && generateTokenSequenceCall(builder, level, rule, children, funcName, i, pinMatcher, pinApplied, skip, externalArguments);
            }
          }
        }
        else {
          skip[0]--; // we are inside already generated token sequence
          if (pinApplied && i == p + 1) p++; // shift pinned index as we skip
        }
        if (!pinApplied && pinMatcher.matches(i, child)) {
          pinApplied = true;
          p = i;
          pinned_ = result_; // pin = pinMatcher.pinValue
        }
      }
      else if (type == BNF_OP_OPT) {
        generateNodeCall(builder, level, rule, child, getNextName(funcName, i), externalArguments);
      }
      else if (type == BNF_OP_ONEMORE || type == BNF_OP_ZEROMORE) {
        if (type == BNF_OP_ONEMORE) {
          result_ = generateNodeCall(builder, level, rule, child, getNextName(funcName, i), externalArguments);
        }
        int pos = current_position_(builder);
        //noinspection LoopConditionNotUpdatedInsideLoop
        while (alwaysTrue || result_) {
          if (!generateNodeCall(builder, level, rule, child, getNextName(funcName, i), externalArguments)) break;
          if (!empty_element_parsed_guard_(builder, funcName, pos)) break;
          pos = current_position_(builder);
        }
      }
      else if (type == BNF_OP_AND) {
        result_ = generateNodeCall(builder, level, rule, child, getNextName(funcName, i), externalArguments);
      }
      else if (type == BNF_OP_NOT) {
        result_ = !generateNodeCall(builder, level, rule, child, getNextName(funcName, i), externalArguments);
      }
      else {
        LOG.warn("unexpected: " + type);
      }
    }

    boolean success = alwaysTrue || result_ || pinned_;

    if (!hooks.isEmpty()) {
      for (Map.Entry<String, String> entry : hooks.entrySet()) {
        if (entry.getValue() == null) continue;
        String name = toIdentifier(entry.getKey(), null, Case.UPPER);
        LiveHooksHelper.registerHook(builder, name, entry.getValue());
      }
    }
    if (sectionRequiredSimple) {
      if (!sectionMaybeDropped) {
        exit_section_(builder, marker_, elementType, alwaysTrue || result_);
      }
    }
    else if (sectionRequired) {
      Parser recoverPredicate;
      BnfRule recoverRule = recoverWhile != null ? myFile.getRule(recoverWhile) : null;
      if (BnfConstants.RECOVER_AUTO.equals(recoverWhile)) {
        IElementType[] nextTokens = generateAutoRecoverCall(rule);
        recoverPredicate = (builder12, level12) -> !nextTokenIsFast(builder12, nextTokens);
      }
      else if (Rule.isMeta(rule) && GrammarUtil.isDoubleAngles(recoverWhile)) {
        recoverPredicate = externalArguments.get(recoverWhile.substring(2, recoverWhile.length() - 2));
      }
      else {
        recoverPredicate = recoverRule == null ? null : (builder1, level1) -> rule(builder1, level1, recoverRule, Collections.emptyMap());
      }
      exit_section_(builder, level, marker_, alwaysTrue || result_, pinned_, recoverPredicate);
    }

    return success;
  }