in src/org/intellij/grammar/generator/RuleGraphHelper.java [499:605]
private Map<PsiElement, Cardinality> joinMaps(@NotNull BnfRule rule, boolean tryCollapse, IElementType type, List<Map<PsiElement, Cardinality>> list) {
if (list.isEmpty()) return Collections.emptyMap();
if (type == BnfTypes.BNF_OP_OPT || type == BnfTypes.BNF_OP_ZEROMORE || type == BnfTypes.BNF_OP_ONEMORE) {
ParserGenerator.LOG.assertTrue(list.size() == 1);
list = compactInheritors(rule, list);
Map<PsiElement, Cardinality> m = list.get(0);
if (tryCollapse && willCollapse(rule, m) && type == BnfTypes.BNF_OP_OPT) {
return Collections.emptyMap();
}
Map<PsiElement, Cardinality> map = psiMap();
boolean leftMarker = m.containsKey(LEFT_MARKER);
for (PsiElement t : m.keySet()) {
Cardinality joinedCard = fromNodeType(type).and(m.get(t));
if (leftMarker) {
joinedCard = joinedCard.single();
}
map.put(t, joinedCard);
}
return map;
}
else if (type == BnfTypes.BNF_SEQUENCE || type == BnfTypes.BNF_EXPRESSION || type == BnfTypes.BNF_REFERENCE_OR_TOKEN) {
list = new ArrayList<>(compactInheritors(rule, list));
list.removeIf(Map::isEmpty);
Map<PsiElement, Cardinality> map = psiMap();
for (Map<PsiElement, Cardinality> m : list) {
Cardinality leftMarker = m.get(LEFT_MARKER);
if (leftMarker == REQUIRED) {
map.clear();
leftMarker = null;
}
else if (leftMarker == OPTIONAL) {
for (PsiElement t : map.keySet()) {
if (!m.containsKey(t)) {
map.put(t, map.get(t).and(OPTIONAL));
}
}
}
for (PsiElement t : m.keySet()) {
if (t == LEFT_MARKER && m != list.get(0)) continue;
Cardinality c1 = map.get(t);
Cardinality c2 = m.get(t);
Cardinality joinedCard;
if (leftMarker == null) {
joinedCard = c2.or(c1);
}
// handle left semantic in a choice-like way
else if (c1 == null) {
joinedCard = c2;
}
else {
if (c1 == REQUIRED) joinedCard = c2.many()? AT_LEAST_ONE : REQUIRED;
else if (c1 == AT_LEAST_ONE) joinedCard = ANY_NUMBER;
else joinedCard = c1;
}
map.put(t, joinedCard);
}
}
if (tryCollapse && willCollapse(rule, map)) {
return Collections.emptyMap();
}
return map;
}
else if (type == BnfTypes.BNF_CHOICE) {
Map<PsiElement, Cardinality> map = psiMap();
list = compactInheritors(rule, list);
if (tryCollapse) {
for (int i = 0, newListSize = list.size(); i < newListSize; i++) {
Map<PsiElement, Cardinality> m = list.get(i);
if (willCollapse(rule, m)) {
list.set(i, Collections.emptyMap());
}
}
}
Map<PsiElement, Cardinality> m0 = list.get(0);
map.putAll(m0);
for (Map<PsiElement, Cardinality> m : list) {
map.keySet().retainAll(m.keySet());
}
for (PsiElement t : new ArrayList<>(map.keySet())) {
map.put(t, REQUIRED.and(m0.get(t)));
for (Map<PsiElement, Cardinality> m : list) {
if (m == list.get(0)) continue;
map.put(t, map.get(t).and(m.get(t)));
}
}
for (Map<PsiElement, Cardinality> m : list) {
if (tryCollapse && willCollapse(rule, m)) continue;
for (PsiElement t : m.keySet()) {
if (map.containsKey(t)) continue;
map.put(t, OPTIONAL.and(m.get(t)));
}
}
boolean notEmpty = true;
empty: for (Map<PsiElement, Cardinality> m : list) {
for (Cardinality c : m.values()) {
if (!c.optional()) continue empty;
}
notEmpty = false;
}
if (notEmpty) map.put(NOT_EMPTY_MARKER, REQUIRED);
return map;
}
else {
throw new AssertionError("unexpected: " + type);
}
}