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;
}