in src/org/intellij/grammar/generator/ParserGenerator.java [676:893]
void generateNode(BnfRule rule, BnfExpression initialNode, String funcName, Set<BnfExpression> visited) {
boolean isRule = initialNode.getParent() == rule;
BnfExpression node = getNonTrivialNode(initialNode);
List<String> metaParameters = collectMetaParametersFormatted(rule, node);
if (!metaParameters.isEmpty()) {
if (isRule && isUsedAsArgument(rule) || !isRule && isArgument(initialNode)) {
generateMetaMethod(funcName, metaParameters, isRule);
newLine();
}
}
IElementType type = getEffectiveType(node);
for (String s : StringUtil.split((StringUtil.isEmpty(node.getText()) ? initialNode : node).getText(), "\n")) {
out("// " + s);
}
boolean firstNonTrivial = node == Rule.firstNotTrivial(rule);
boolean isPrivate = !(isRule || firstNonTrivial) || Rule.isPrivate(rule) || myGrammarRoot.equals(rule.getName());
boolean isLeft = firstNonTrivial && Rule.isLeft(rule);
boolean isLeftInner = isLeft && (isPrivate || Rule.isInner(rule));
boolean isUpper = !isPrivate && Rule.isUpper(rule);
String recoverWhile = !firstNonTrivial ? null : getAttribute(rule, KnownAttribute.RECOVER_WHILE);
Map<String, String> hooks = firstNonTrivial ? getAttribute(rule, KnownAttribute.HOOKS).asMap() : Collections.emptyMap();
boolean canCollapse = !isPrivate && (!isLeft || isLeftInner) && firstNonTrivial && myGraphHelper.canCollapse(rule);
String elementType = getElementType(rule);
String elementTypeRef = !isPrivate && StringUtil.isNotEmpty(elementType) ? elementType : 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) ? quote(getRuleDisplayName(rule, !isPrivate)) : null;
String extraParameters = metaParameters.stream().map(it -> ", Parser " + it).collect(joining());
out("%sstatic boolean %s(%s %s, int %s%s) {", !isRule ? "private " : isPrivate ? "" : "public ",
funcName, shorten(C.PsiBuilderClass), N.builder, N.level, extraParameters);
if (isSingleNode) {
if (isPrivate && !isLeftInner && recoverWhile == null && frameName == null) {
String nodeCall = generateNodeCall(rule, node, getNextName(funcName, 0)).render(N);
out("return %s;", nodeCall);
out("}");
if (node instanceof BnfExternalExpression && ((BnfExternalExpression)node).getExpressionList().size() > 1) {
generateNodeChildren(rule, funcName, children, visited);
}
return;
}
else {
type = BNF_SEQUENCE;
}
}
if (!children.isEmpty()) {
out("if (!recursion_guard_(%s, %s, \"%s\")) return false;", N.builder, N.level, funcName);
}
if (recoverWhile == null && (isRule || firstNonTrivial)) {
frameName = generateFirstCheck(rule, frameName, getAttribute(rule, KnownAttribute.NAME) == null);
}
PinMatcher pinMatcher = new PinMatcher(rule, type, firstNonTrivial ? rule.getName() : funcName);
boolean pinApplied = false;
boolean alwaysTrue = children.isEmpty() || type == BNF_OP_OPT || type == BNF_OP_ZEROMORE;
boolean pinned = pinMatcher.active() && pinMatcher.shouldGenerate(children);
if (!alwaysTrue) {
out("boolean %s%s%s;", N.result, children.isEmpty() ? " = true" : "", pinned ? format(", %s", N.pinned) : "");
}
List<String> modifierList = new SmartList<>();
if (canCollapse) modifierList.add("_COLLAPSE_");
if (isLeftInner) modifierList.add("_LEFT_INNER_");
else if (isLeft) modifierList.add("_LEFT_");
if (type == BNF_OP_AND) modifierList.add("_AND_");
else if (type == BNF_OP_NOT) modifierList.add("_NOT_");
if (isUpper) modifierList.add("_UPPER_");
if (modifierList.isEmpty() && (pinned || frameName != null)) modifierList.add("_NONE_");
boolean sectionRequired = !alwaysTrue || !isPrivate || isLeft || recoverWhile != null;
boolean sectionRequiredSimple = sectionRequired && modifierList.isEmpty() && recoverWhile == null && frameName == null;
boolean sectionMaybeDropped = sectionRequiredSimple && type == BNF_CHOICE && elementTypeRef == null &&
!ContainerUtil.exists(children, o -> isRollbackRequired(o, myFile));
String modifiers = modifierList.isEmpty()? "_NONE_" : StringUtil.join(modifierList, " | ");
String shortMarker = !G.generateFQN ? "Marker" : C.PsiBuilderClass + ".Marker";
if (sectionRequiredSimple) {
if (!sectionMaybeDropped) {
out("%s %s = enter_section_(%s);", shortMarker, N.marker, N.builder);
}
}
else if (sectionRequired) {
boolean shortVersion = frameName == null && elementTypeRef == null;
if (shortVersion) {
out("%s %s = enter_section_(%s, %s, %s);", shortMarker, N.marker, N.builder, N.level, modifiers);
}
else {
out("%s %s = enter_section_(%s, %s, %s, %s, %s);", shortMarker, N.marker, N.builder, N.level, modifiers, elementTypeRef, frameName);
}
}
int[] skip = {0};
for (int i = 0, p = 0, childrenSize = children.size(); i < childrenSize; i++) {
BnfExpression child = children.get(i);
NodeCall nodeCall = generateNodeCall(rule, child, getNextName(funcName, i));
if (type == BNF_CHOICE) {
if (isRule && i == 0 && G.generateTokenSets) {
ConsumeType consumeType = getEffectiveConsumeType(rule, node, null);
NodeCall tokenChoice = generateTokenChoiceCall(children, consumeType, funcName);
if (tokenChoice != null) {
out("%s = %s;", N.result, tokenChoice.render(N));
break;
}
}
out("%s%s = %s;", i > 0 ? format("if (!%s) ", N.result) : "", N.result, nodeCall.render(N));
}
else if (type == BNF_SEQUENCE) {
if (skip[0] == 0) {
ConsumeType consumeType = getEffectiveConsumeType(rule, node, null);
nodeCall = generateTokenSequenceCall(children, i, pinMatcher, pinApplied, skip, nodeCall, false, consumeType);
if (i == 0) {
out("%s = %s;", N.result, nodeCall.render(N));
}
else {
if (pinApplied && G.generateExtendedPin) {
if (i == childrenSize - 1) {
// do not report error for last child
if (i == p + 1) {
out("%s = %s && %s;", N.result, N.result, nodeCall.render(N));
}
else {
out("%s = %s && %s && %s;", N.result, N.pinned, nodeCall.render(N), N.result);
}
}
else if (i == p + 1) {
out("%s = %s && report_error_(%s, %s);", N.result, N.result, N.builder, nodeCall.render(N));
}
else {
out("%s = %s && report_error_(%s, %s) && %s;", N.result, N.pinned, N.builder, nodeCall.render(N), N.result);
}
}
else {
out("%s = %s && %s;", N.result, N.result, nodeCall.render(N));
}
}
}
else {
skip[0]--; // we are inside already generated token sequence
if (pinApplied && i == p + 1) p++; // shift pinned index as we skip
}
if (pinned && !pinApplied && pinMatcher.matches(i, child)) {
pinApplied = true;
p = i;
out("%s = %s; // pin = %s", N.pinned, N.result, pinMatcher.pinValue);
}
}
else if (type == BNF_OP_OPT) {
out(nodeCall.render(N) + ";");
}
else if (type == BNF_OP_ONEMORE || type == BNF_OP_ZEROMORE) {
if (type == BNF_OP_ONEMORE) {
out("%s = %s;", N.result, nodeCall.render(N));
}
out("while (%s) {", alwaysTrue ? "true" : N.result);
out("int %s = current_position_(%s);", N.pos, N.builder);
out("if (!%s) break;", nodeCall.render(N));
out("if (!empty_element_parsed_guard_(%s, \"%s\", %s)) break;", N.builder, funcName, N.pos);
out("}");
}
else if (type == BNF_OP_AND) {
out("%s = %s;", N.result, nodeCall.render(N));
}
else if (type == BNF_OP_NOT) {
out("%s = !%s;", N.result, nodeCall.render(N));
}
else {
addWarning("unexpected: " + type);
}
}
if (sectionRequired) {
String resultRef = alwaysTrue ? "true" : N.result;
if (!hooks.isEmpty()) {
for (Map.Entry<String, String> entry : hooks.entrySet()) {
String hookName = toIdentifier(entry.getKey(), null, Case.UPPER);
out("register_hook_(%s, %s, %s);", N.builder, hookName, entry.getValue());
}
}
if (sectionRequiredSimple) {
if (!sectionMaybeDropped) {
out("exit_section_(%s, %s, %s, %s);", N.builder, N.marker, elementTypeRef, resultRef);
}
}
else {
String pinnedRef = pinned ? N.pinned : "false";
String recoverCall;
if (recoverWhile != null) {
BnfRule predicateRule = myFile.getRule(recoverWhile);
if (RECOVER_AUTO.equals(recoverWhile)) {
recoverCall = generateAutoRecoverCall(rule);
}
else if (Rule.isMeta(rule) && GrammarUtil.isDoubleAngles(recoverWhile)) {
recoverCall = formatMetaParamName(recoverWhile.substring(2, recoverWhile.length() - 2));
}
else {
recoverCall = predicateRule == null ? null : generateWrappedNodeCall(rule, null, predicateRule.getName()).render();
}
}
else {
recoverCall = null;
}
out("exit_section_(%s, %s, %s, %s, %s, %s);", N.builder, N.level, N.marker, resultRef, pinnedRef, recoverCall);
}
}
out("return %s;", alwaysTrue ? "true" : N.result + (pinned ? format(" || %s", N.pinned) : ""));
out("}");
generateNodeChildren(rule, funcName, children, visited);
}