fixed problem with context analysis

non-trivial quantifiers in context path are not handled correctly.

change to getNextElementsInContext():
add non-trivially quantified elements in path (except first element
because of potential endless recursion) to result set
This commit is contained in:
overflowerror 2022-01-14 17:22:15 +01:00
parent e5f08c34aa
commit a5df42591b

View file

@ -11,7 +11,6 @@ package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.pathAnalysis;
import static org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.utils.DebugUtils.*; import static org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.utils.DebugUtils.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -45,7 +44,6 @@ import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.utils.DebugUtils;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.utils.MutablePrimitiveWrapper; import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.utils.MutablePrimitiveWrapper;
import static org.eclipse.xtext.GrammarUtil.*; import static org.eclipse.xtext.GrammarUtil.*;
import static org.eclipse.xtext.EcoreUtil2.*;
/** /**
* @author overflow - Initial contribution and API * @author overflow - Initial contribution and API
@ -61,65 +59,65 @@ public class TokenAnalysis {
this.grammar = grammar; this.grammar = grammar;
} }
private AbstractElement getContainer(AbstractElement element) { private List<AbstractElement> getNextElementsInContext(AbstractElement last) {
EObject tmp = element.eContainer(); List<AbstractElement> result = new ArrayList<>();
while (!(tmp instanceof AbstractElement)) {
if (tmp == null) { AbstractElement _last = last;
return null; AbstractElement container = last;
}
tmp = tmp.eContainer(); while(container != null && (
} container == last ||
return (AbstractElement) tmp; !(container instanceof CompoundElement) ||
} container instanceof Alternatives
)
private CompoundElement getCompoundContainer(AbstractElement element) {
if (element instanceof CompoundElement) {
// get container of compoundElement since getContainerOfType
// would return the same element
element = getContainer(element);
if (element == null) {
return null;
}
}
return getContainerOfType(element, CompoundElement.class);
}
private List<AbstractElement> getNextElementsInContext(AbstractElement last) {
// TODO: deal with non-trivial cardinalities
CompoundElement container = getCompoundContainer(last);
while (container instanceof Alternatives ||
last.eContainer() instanceof Assignment
) { ) {
// skip alternatives since they have to be covered separately EObject _container = _last;
last = getContainer(last); while (_container == _last ||
if (last == null) { !(_container instanceof AbstractElement)
return Arrays.asList((AbstractElement) null); ) {
_container = _container.eContainer();
if (_container == null)
break;
}
_last = container;
container = (AbstractElement) _container;
if (last != _last && isMultipleCardinality(_last)) {
// last is + or * quantified
result.add(_last);
} }
container = getCompoundContainer(last);
} }
last = _last;
log.info("getNext: " + abstractElementToShortString(last)); CompoundElement compoundContainer = (CompoundElement) container;
log.info("container: " + abstractElementToShortString(container));
if (container instanceof UnorderedGroup) { if (compoundContainer == null) {
List<AbstractElement> result = new ArrayList<>(); // no container element; this is last element in a rule definition
result.addAll(container.getElements().stream() AbstractRule rule = containingRule(last);
.collect(Collectors.toList()) List<RuleCall> calls = findAllRuleCalls(grammar, rule);
);
result.addAll(getNextElementsInContext(container)); if (calls.isEmpty()) {
return result; // has to be start rule
} else if (container instanceof Group) { // context is EOF
List<AbstractElement> elements = container.getElements(); result.add(null);
}
for (RuleCall call : calls) {
result.addAll(getNextElementsInContext(call));
}
} else if (compoundContainer instanceof Group) {
List<AbstractElement> elements = compoundContainer.getElements();
int index = elements.indexOf(last); int index = elements.indexOf(last);
if (index < 0) { if (index < 0) {
log.error("context analysis: element not part of compound"); log.error("context analysis: element not part of compound");
log.info(last.eClass().getName()); log.info(last.eClass().getName());
log.info(abstractElementToString(container)); log.info(abstractElementToString(compoundContainer));
} }
int size = elements.size(); int size = elements.size();
AbstractElement next = null; AbstractElement next = null;
// skip simple non-token-elements
while (index < size - 1) { while (index < size - 1) {
next = elements.get(index + 1); next = elements.get(index + 1);
if (!( if (!(
@ -133,34 +131,27 @@ public class TokenAnalysis {
index++; index++;
} }
if (index < size - 1) { if (index < size - 1) {
return Arrays.asList(next); result.add(next);
} else { } else {
// this is the last element // this is the last element
return getNextElementsInContext(container); if (isMultipleCardinality(compoundContainer)) {
result.add(compoundContainer);
}
result.addAll(getNextElementsInContext(compoundContainer));
} }
} else if (container == null) { } else if (compoundContainer instanceof UnorderedGroup) {
// end of rule result.addAll(compoundContainer.getElements().stream()
AbstractRule rule = containingRule(last); .collect(Collectors.toList())
List<RuleCall> calls = findAllRuleCalls(grammar, rule); );
result.addAll(getNextElementsInContext(compoundContainer));
if (calls.isEmpty()) {
// has to be start rule
// context is EOF
return Arrays.asList((AbstractElement) null);
}
List<AbstractElement> result = new ArrayList<>();
for (RuleCall call : calls) {
result.addAll(getNextElementsInContext(call));
}
return result;
} else { } else {
throw new IllegalArgumentException("unknown compound element: " + container.eClass().getName()); throw new IllegalArgumentException("unknown compound element: " + container.eClass().getName());
} }
return result;
} }
private TokenAnalysisPaths getTokenPathsContext(AbstractElement last, TokenAnalysisPaths prefix, boolean shortcutEndlessLoops) { private TokenAnalysisPaths getTokenPathsContext(AbstractElement last, TokenAnalysisPaths prefix, boolean shortcutEndlessLoops) {
log.info("get context for: " + abstractElementToShortString(last)); log.info("get context for: " + abstractElementToShortString(last));
@ -180,7 +171,7 @@ public class TokenAnalysis {
log.info("context element: " + abstractElementToShortString(element)); log.info("context element: " + abstractElementToShortString(element));
TokenAnalysisPaths path = new TokenAnalysisPaths(prefix); TokenAnalysisPaths path = new TokenAnalysisPaths(prefix);
path = getTokenPaths(element, path, false, false, shortcutEndlessLoops); path = getTokenPaths(element, path, false, false, shortcutEndlessLoops);
if (!path.isDone() && element != null) { if (!path.isDone() && element != null) {
path = getTokenPathsContext(element, path, shortcutEndlessLoops); path = getTokenPathsContext(element, path, shortcutEndlessLoops);
} }
if (path.isDone()) { if (path.isDone()) {
@ -189,7 +180,7 @@ public class TokenAnalysis {
throw new TokenAnalysisAbortedException("context analysis failed"); throw new TokenAnalysisAbortedException("context analysis failed");
} }
} }
log.info("done"); log.info("done");
return result; return result;
} }