refactoring, better exception handling, changes to error detection

exception messages now contain the the rule that caused the problem
This commit is contained in:
overflowerror 2021-11-29 21:12:02 +01:00
parent 5aba8ab724
commit a6993a01f7
14 changed files with 384 additions and 133 deletions

View file

@ -17,8 +17,8 @@ import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.testing.GlobalRegistries;
import org.eclipse.xtext.tests.AbstractXtextTests;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.HoistingProcessor;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions.TokenAnalysisAbortedException;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.HoistingGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.pathAnalysis.TokenAnalysisAbortedException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -79,7 +79,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertTrue(guard.isTrivial());
assertFalse(guard.hasTerminal());
}
@ -95,7 +95,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertFalse(guard.hasTerminal());
assertEquals("(p0)", guard.render());
@ -112,7 +112,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(p0)", guard.render());
@ -129,7 +129,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertTrue(guard.isTrivial());
assertTrue(guard.hasTerminal());
}
@ -146,7 +146,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertTrue(guard.isTrivial());
assertTrue(guard.hasTerminal());
}
@ -163,7 +163,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(p0)", guard.render());
@ -180,7 +180,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((p0) && (p1))", guard.render());
@ -198,7 +198,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(p0)", guard.render());
@ -216,7 +216,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertTrue(guard.isTrivial());
assertTrue(guard.hasTerminal());
}
@ -232,7 +232,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(p0)", guard.render());
@ -249,7 +249,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + getSyntaxForKeywordToken("s", 1) + " || (p1)) && (" + getSyntaxForKeywordToken("a", 1) + " || (p0)))", guard.render());
@ -266,7 +266,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + getSyntaxForKeywordToken("s", 1) + " || (p1)) && (" + getSyntaxForKeywordToken("a", 1) + " || (p0)))", guard.render());
@ -284,7 +284,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
//assertEquals("((" + getSyntaxForKeywordToken("s", 1) + " || (p2)) && ((" + getSyntaxForKeywordToken("a", 1) + " && " + getSyntaxForKeywordToken("b", 1) + ") || ((" + getSyntaxForKeywordToken("a", 1)+ " || (p0)) && (" + getSyntaxForKeywordToken("b", 1) + " || (p1)))))", guard.render());
@ -303,7 +303,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertFalse(guard.hasTerminal());
assertEquals("((p0) || (p1))", guard.render());
@ -321,7 +321,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + getSyntaxForKeywordToken("a", 1) + " || (p0)) && (" + getSyntaxForKeywordToken("b", 1) + " || (p1)))", guard.render());
@ -341,7 +341,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(((" + getSyntaxForKeywordToken("a", 1) + " && " + getSyntaxForKeywordToken("b", 1) + ") || (p0)) && ((" + getSyntaxForKeywordToken("c", 1) + " && " + getSyntaxForKeywordToken("d", 1) + ") || (p1)))", guard.render());
@ -360,7 +360,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + getSyntaxForKeywordToken("a", 2) + " || (p0)) && (" + getSyntaxForKeywordToken("b", 2) + " || (p1)))", guard.render());
@ -380,7 +380,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + getSyntaxForKeywordToken("j", 10) + " || ((p0) || (p1))) && (" + getSyntaxForKeywordToken("k", 10) + " || (p2)))", guard.render());
@ -398,7 +398,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertTrue(guard.isTrivial());
assertTrue(guard.hasTerminal());
}
@ -415,7 +415,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(" + getSyntaxForKeywordToken("a", 1) + " || (p0))", guard.render());
@ -438,7 +438,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + getSyntaxForKeywordToken("b", 2) + " || ((p0) && (p2))) && (" + getSyntaxForKeywordToken("c", 2) + " || ((p0) && (p3))) && (" + getSyntaxForKeywordToken("d", 3) + " || (p1)))", guard.render());
@ -461,7 +461,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
@ -483,7 +483,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
@ -502,7 +502,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
hoistingProcessor.findGuardForElement(rule.getAlternatives());
hoistingProcessor.findHoistingGuard(rule.getAlternatives());
}
@Test(expected = TokenAnalysisAbortedException.class)
@ -517,7 +517,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
hoistingProcessor.findGuardForElement(rule.getAlternatives());
hoistingProcessor.findHoistingGuard(rule.getAlternatives());
}
@Test(expected = TokenAnalysisAbortedException.class)
@ -533,7 +533,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
hoistingProcessor.findGuardForElement(rule.getAlternatives());
hoistingProcessor.findHoistingGuard(rule.getAlternatives());
}
// symbolic analysis not yet implemented
@ -550,7 +550,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
Grammar grammar = ((Grammar) resource.getContents().get(0));
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((p0) || (p1))", guard.render());

View file

@ -37,7 +37,6 @@ import static extension org.eclipse.xtext.xtext.generator.parser.antlr.AntlrGram
import static extension org.eclipse.xtext.xtext.generator.parser.antlr.TerminalRuleToLexerBody.*
import org.eclipse.xtext.xtext.generator.util.SyntheticTerminalDetector
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.HoistingProcessor
import java.lang.reflect.Parameter
import org.eclipse.xtext.AbstractSemanticPredicate
import org.eclipse.xtext.JavaAction
import org.eclipse.xtext.GatedSemanticPredicate
@ -337,7 +336,7 @@ abstract class AbstractAntlrGrammarGenerator {
protected dispatch def String dataTypeEbnf2(AbstractElement it, boolean supportActions) '''ERROR «eClass.name» not matched'''
protected dispatch def String dataTypeEbnf2(Alternatives it, boolean supportActions) '''
«FOR e:elements SEPARATOR '\n |'»«e.findGuardForElement.renderPredicate»«e.dataTypeEbnf(supportActions)»«ENDFOR»
«FOR e:elements SEPARATOR '\n |'»«e.findHoistingGuard.renderPredicate»«e.dataTypeEbnf(supportActions)»«ENDFOR»
'''
protected dispatch def String dataTypeEbnf2(Group it, boolean supportActions) '''
@ -359,7 +358,7 @@ abstract class AbstractAntlrGrammarGenerator {
protected dispatch def String ebnf2(AbstractElement it, AntlrOptions options, boolean supportActions) '''ERROR «eClass.name» not matched'''
protected dispatch def String ebnf2(Alternatives it, AntlrOptions options, boolean supportActions) '''
«FOR element:elements SEPARATOR '\n |'»«element.findGuardForElement.renderPredicate»«element.ebnf(options, supportActions)»«ENDFOR»
«FOR element:elements SEPARATOR '\n |'»«element.findHoistingGuard.renderPredicate»«element.ebnf(options, supportActions)»«ENDFOR»
'''
protected dispatch def String ebnf2(Group it, AntlrOptions options, boolean supportActions) '''

View file

@ -161,7 +161,7 @@ class AntlrContentAssistGrammarGenerator extends AbstractAntlrGrammarWithActions
int stackSize = keepStackSize();
}
:
«FOR element : elements SEPARATOR '\n|'»«element.findGuardForElement.renderPredicate»«element.ebnf(options, false)»«ENDFOR»
«FOR element : elements SEPARATOR '\n|'»«element.findHoistingGuard.renderPredicate»«element.ebnf(options, false)»«ENDFOR»
;
finally {
restoreStackSize(stackSize);

View file

@ -24,6 +24,7 @@ import org.eclipse.xtext.AbstractSemanticPredicate;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.Alternatives;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.CompoundElement;
import org.eclipse.xtext.Group;
import org.eclipse.xtext.JavaAction;
import org.eclipse.xtext.RuleCall;
@ -32,6 +33,9 @@ import org.eclipse.xtext.XtextFactory;
import org.eclipse.xtext.util.Tuples;
import static org.eclipse.xtext.GrammarUtil.*;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions.OptionalCardinalityWithoutContextException;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions.TokenAnalysisAbortedException;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions.UnsupportedConstructException;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.AlternativeTokenSequenceGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.AlternativesGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.GroupGuard;
@ -64,12 +68,12 @@ public class HoistingProcessor {
}
// TODO: handling for TokenAnalysisAbortedException
private HoistingGuard findGuardForAlternatives(Alternatives alternatives) {
private HoistingGuard findGuardForAlternatives(Alternatives alternatives, AbstractRule currentRule) {
log.info("find guard for alternative");
List<AbstractElement> paths = alternatives.getElements();
List<MergedPathGuard> guards = paths.stream()
.map(this::findGuardForElement)
.map(p -> findGuardForElement(p, currentRule))
.map(MergedPathGuard::new)
.collect(Collectors.toList());
@ -84,20 +88,24 @@ public class HoistingProcessor {
return HoistingGuard.unguarded();
}
}
log.info("path identity check");
int size = paths.size();
for (int i = 0; i < size; i++) {
for (int j = i + 1; j < size; j++) {
if (analysis.arePathsIdentical(paths.get(i), paths.get(j))) {
guards.get(i).add(guards.get(j));
paths.remove(j);
guards.remove(j);
j--;
size--;
try {
for (int i = 0; i < size; i++) {
for (int j = i + 1; j < size; j++) {
if (analysis.arePathsIdentical(paths.get(i), paths.get(j))) {
guards.get(i).add(guards.get(j));
paths.remove(j);
guards.remove(j);
j--;
size--;
}
}
}
} catch (TokenAnalysisAbortedException e) {
throw new TokenAnalysisAbortedException(e.getMessage(), e, currentRule);
}
log.info("paths:" + paths);
@ -107,21 +115,25 @@ public class HoistingProcessor {
// if all paths are empty the above step will eliminate all paths
// -> size = 1
if (size > 1) {
return StreamUtils.zip(
analysis.findMinimalPathDifference(paths).stream()
.map(a -> a.stream()
.map(s -> s.stream()
.map(SingleTokenGuard::new)
.collect(Collectors.toList())
try {
return StreamUtils.zip(
analysis.findMinimalPathDifference(paths).stream()
.map(a -> a.stream()
.map(s -> s.stream()
.map(SingleTokenGuard::new)
.collect(Collectors.toList())
)
.map(TokenSequenceGuard::new)
.collect(Collectors.toList())
)
.map(TokenSequenceGuard::new)
.collect(Collectors.toList())
)
.map(AlternativeTokenSequenceGuard::new),
guards.stream(),
(TokenGuard tokenGuard, MergedPathGuard pathGuard) -> Tuples.pair(tokenGuard, pathGuard)
).map(p -> new PathGuard(p.getFirst(), p.getSecond()))
.collect(AlternativesGuard.collector());
.map(AlternativeTokenSequenceGuard::new),
guards.stream(),
(TokenGuard tokenGuard, MergedPathGuard pathGuard) -> Tuples.pair(tokenGuard, pathGuard)
).map(p -> new PathGuard(p.getFirst(), p.getSecond()))
.collect(AlternativesGuard.collector());
} catch(TokenAnalysisAbortedException e) {
throw new TokenAnalysisAbortedException(e.getMessage(), e, currentRule);
}
} else {
return guards.get(0);
}
@ -148,7 +160,7 @@ public class HoistingProcessor {
return elements;
}
private HoistingGuard findGuardForGroup(Group group) {
private HoistingGuard findGuardForGroup(Group group, AbstractRule currentRule) {
log.info("find guard for group");
GroupGuard groupGuard = new GroupGuard();
@ -162,7 +174,7 @@ public class HoistingProcessor {
cardinality.equals("") ||
cardinality.equals("+")) {
HoistingGuard guard = findGuardForElementWithTrivialCardinality(element);
HoistingGuard guard = findGuardForElementWithTrivialCardinality(element, currentRule);
groupGuard.add(guard);
// if cardinality is + and there is a terminal we don't need to consider
@ -178,7 +190,6 @@ public class HoistingProcessor {
// rewrite cardinality to alternatives
// A? B -> A B | B
// A* B -> A+ B | B -> A B (see above)
// TODO: what if B is empty?
// we need a clone of the element because we need to set the cardinality without changing the
// original syntax tree
@ -189,6 +200,12 @@ public class HoistingProcessor {
List<AbstractElement> remainingElementsInGroup = StreamUtils.fromIterator(iterator)
.collect(Collectors.toList());
if (remainingElementsInGroup.isEmpty()) {
// B is empty
// context is needed to generate virtual alternatives
throw new OptionalCardinalityWithoutContextException("no context in group", currentRule);
}
// make copy of first branch and add the cloned element
List<AbstractElement> remainingElementsInGroupIncludingCurrent = new LinkedList<>(remainingElementsInGroup);
remainingElementsInGroupIncludingCurrent.add(0, clonedElement);
@ -203,7 +220,7 @@ public class HoistingProcessor {
addElementsToCompoundElement(virtualAlternatives, Arrays.asList(virtualPathRemaining, virtualPathRemainingPlusCurrent));
// get Guard for virtual alternatives
HoistingGuard guard = findGuardForElementWithTrivialCardinality(virtualAlternatives);
HoistingGuard guard = findGuardForElementWithTrivialCardinality(virtualAlternatives, currentRule);
groupGuard.add(guard);
if (guard.hasTerminal()) {
@ -220,81 +237,112 @@ public class HoistingProcessor {
return groupGuard;
}
// issue: groupCache can't be Concurrent because of possible recursive calls to computeIfAbent
// solution: atomic section
public synchronized HoistingGuard findGuardForRule(AbstractRule rule) {
// TODO: make private
public HoistingGuard findGuardForRule(AbstractRule rule) {
HoistingGuard guard = ruleCache.get(rule.getName());
if (guard == null) {
guard = findGuardForElement(rule.getAlternatives());
guard = findGuardForElement(rule.getAlternatives(), rule);
ruleCache.put(rule.getName(), guard);
}
return guard;
}
private boolean pathHasToken(AbstractElement path) {
private boolean pathHasTokenOrAction(AbstractElement path) {
// we are only interested in whether or not there could be any tokens on this path
// -> ignore cardinality
if (Token.isToken(path)) {
return true;
} else if (isEnumRuleCall(path)) {
return true;
} else if (path instanceof JavaAction) {
return true;
} else if (isParserRuleCall(path)) {
// can not be self recursive since ANTLR uses LL(*) and therefore does not support left recursion
return pathHasToken(((RuleCall) path).getRule().getAlternatives());
return pathHasTokenOrAction(((RuleCall) path).getRule().getAlternatives());
} else if (path instanceof Assignment) {
return pathHasToken(((Assignment) path).getTerminal());
} else if (path instanceof Group) {
return ((Group) path).getElements().stream()
.anyMatch(this::pathHasToken);
} else if (path instanceof Alternatives) {
return ((Alternatives) path).getElements().stream()
.anyMatch(this::pathHasToken);
return pathHasTokenOrAction(((Assignment) path).getTerminal());
} else if (path instanceof CompoundElement) {
return ((CompoundElement) path).getElements().stream()
.anyMatch(this::pathHasTokenOrAction);
} else {
// Actions, JavaActions, Predicates, ...
// Actions, Predicates, ...
return false;
}
}
public HoistingGuard findGuardForElement(AbstractElement element) {
String cardinality = element.getCardinality();
if (cardinality == null || cardinality.equals("")) {
return findGuardForElementWithTrivialCardinality(element);
} else if (cardinality.equals("+")) {
return findGuardForElementWithTrivialCardinality(element);
} else if (cardinality.equals("?") ||
cardinality.equals("*")) {
if (pathHasToken(element)) {
@SuppressWarnings("unused")
private boolean pathHasHoistablePredicate(AbstractElement path) {
// we are only interested in whether or not there could be any hoistable predicate on this path
// -> ignore cardinality
if (path instanceof AbstractSemanticPredicate) {
return true;
} else if (isParserRuleCall(path)) {
// can not be self recursive since ANTLR uses LL(*) and therefore does not support left recursion
return pathHasHoistablePredicate(((RuleCall) path).getRule().getAlternatives());
} else if (path instanceof Assignment) {
return pathHasHoistablePredicate(((Assignment) path).getTerminal());
} else if (path instanceof Alternatives ||
path instanceof UnorderedGroup) {
return ((CompoundElement) path).getElements().stream()
.anyMatch(this::pathHasHoistablePredicate);
} else if (path instanceof Group) {
for (AbstractElement element : ((CompoundElement) path).getElements()) {
if (pathHasHoistablePredicate(element)) {
return true;
}
if (pathHasTokenOrAction(element)) {
return false;
}
}
return false;
} else {
// Tokens, EnumRule, Actions, JavaActions, ...
return false;
}
}
public HoistingGuard findHoistingGuard(AbstractElement element) {
return findGuardForElement(element, containingParserRule(element));
}
private HoistingGuard findGuardForElement(AbstractElement element, AbstractRule currentRule) {
if (isTrivialCardinality(element)) {
return findGuardForElementWithTrivialCardinality(element, currentRule);
} else if (isOneOrMoreCardinality(element)) {
return findGuardForElementWithTrivialCardinality(element, currentRule);
} else if (isOptionalCardinality(element)) {
if (pathHasTokenOrAction(element)) {
// there might be a token in this element
// no context accessible to construct guard
// this does only work when analyzing group
// TODO: generate warning
// this is not a terminal, but there is no point constructing guards past this point
return HoistingGuard.terminal();
// TODO: maybe generate warning and return terminal()
throw new OptionalCardinalityWithoutContextException("optional cardinality is only supported in groups", currentRule);
} else {
// element with cardinality ? or * has no token
// element with cardinality ? or * has no token or action
// -> the path is accessible whether or not this element is guarded
// -> we can assume it is unguarded
return HoistingGuard.unguarded();
}
} else {
throw new IllegalArgumentException("unknown cardinality: " + cardinality);
throw new IllegalArgumentException("unknown cardinality: " + element.getCardinality());
}
}
private HoistingGuard findGuardForElementWithTrivialCardinality(AbstractElement element) {
private HoistingGuard findGuardForElementWithTrivialCardinality(AbstractElement element, AbstractRule currentRule) {
log.info("finding guard for element: " + element.toString());
if (element instanceof Alternatives) {
return findGuardForAlternatives((Alternatives) element);
return findGuardForAlternatives((Alternatives) element, currentRule);
} else if (element instanceof Group) {
// issue: groupCache can't be Concurrent because of possible recursive calls to computeIfAbent
// solution: atomic section
synchronized (groupCache) {
HoistingGuard guard = groupCache.get(element);
if (guard == null) {
guard = findGuardForGroup((Group) element);
groupCache.put((Group) element, guard);
}
return guard;
HoistingGuard guard = groupCache.get(element);
if (guard == null) {
guard = findGuardForGroup((Group) element, currentRule);
groupCache.put((Group) element, guard);
}
return guard;
} else if (element instanceof AbstractSemanticPredicate) {
return new PredicateGuard((AbstractSemanticPredicate) element);
} else if (Token.isToken(element)) {
@ -308,12 +356,21 @@ public class HoistingProcessor {
} else if (element instanceof JavaAction) {
return HoistingGuard.action();
} else if (element instanceof UnorderedGroup) {
// TODO: maybe add warning and return unguarded
throw new UnsupportedOperationException("unordered groups are only supported in groups");
if (pathHasTokenOrAction(element)) {
// if unordered group has tokens or actions we need the context which is not available here
// only works when analyzing groups
// TODO: maybe add warning and return unguarded
throw new UnsupportedConstructException("unordered groups are only supported in groups", currentRule);
} else {
// the path is accessible whether or not any guard is satisfied
// -> assume it's unguarded
return HoistingGuard.unguarded();
}
} else if (element instanceof Assignment) {
return findGuardForElement(((Assignment) element).getTerminal());
return findGuardForElement(((Assignment) element).getTerminal(), currentRule);
} else {
throw new UnsupportedOperationException("element not supported: " + element.toString());
throw new UnsupportedConstructException("element not supported: " + element.toString(), currentRule);
}
}
}

View file

@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (c) 2021 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions;
import org.eclipse.xtext.AbstractRule;
/**
* @author overflow - Initial contribution and API
*/
public class HoistingException extends RuntimeException {
private static final long serialVersionUID = 6074715133295241743L;
private AbstractRule rule;
public HoistingException() {
super();
}
public HoistingException(String message, Throwable cause) {
super(message, cause);
}
public HoistingException(String message, Throwable cause, AbstractRule rule) {
super(message, cause);
this.rule = rule;
}
public HoistingException(String message) {
super(message);
}
public HoistingException(String message, AbstractRule rule) {
super(message);
this.rule = rule;
}
public HoistingException(Throwable cause) {
super(cause);
}
public HoistingException(Throwable cause, AbstractRule rule) {
super(cause);
this.rule = rule;
}
@Override
public String getMessage() {
if (rule == null) {
return super.getMessage();
} else {
return rule.getName() + ": " + super.getMessage();
}
}
}

View file

@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2021 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions;
import org.eclipse.xtext.AbstractRule;
/**
* @author overflow - Initial contribution and API
*/
public class OptionalCardinalityWithoutContextException extends UnsupportedConstructException {
private static final long serialVersionUID = -3958747238452206971L;
public OptionalCardinalityWithoutContextException() {
super();
}
public OptionalCardinalityWithoutContextException(String message, AbstractRule rule) {
super(message, rule);
}
public OptionalCardinalityWithoutContextException(String message, Throwable cause, AbstractRule rule) {
super(message, cause, rule);
}
public OptionalCardinalityWithoutContextException(String message, Throwable cause) {
super(message, cause);
}
public OptionalCardinalityWithoutContextException(String message) {
super(message);
}
public OptionalCardinalityWithoutContextException(Throwable cause, AbstractRule rule) {
super(cause, rule);
}
public OptionalCardinalityWithoutContextException(Throwable cause) {
super(cause);
}
}

View file

@ -6,7 +6,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.pathAnalysis;
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions;
/**
* @author overflow - Initial contribution and API

View file

@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2021 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions;
import org.eclipse.xtext.AbstractRule;
/**
* @author overflow - Initial contribution and API
*/
public class TokenAnalysisAbortedException extends HoistingException {
private static final long serialVersionUID = 4303267001950479292L;
public TokenAnalysisAbortedException() {
super();
}
public TokenAnalysisAbortedException(String message, AbstractRule rule) {
super(message, rule);
}
public TokenAnalysisAbortedException(String message, Throwable cause, AbstractRule rule) {
super(message, cause, rule);
}
public TokenAnalysisAbortedException(String message, Throwable cause) {
super(message, cause);
}
public TokenAnalysisAbortedException(String message) {
super(message);
}
public TokenAnalysisAbortedException(Throwable cause, AbstractRule rule) {
super(cause, rule);
}
public TokenAnalysisAbortedException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2021 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions;
import org.eclipse.xtext.AbstractRule;
/**
* @author overflow - Initial contribution and API
*/
public class UnsupportedConstructException extends HoistingException {
private static final long serialVersionUID = 637006930813191613L;
public UnsupportedConstructException() {
super();
}
public UnsupportedConstructException(String message, AbstractRule rule) {
super(message, rule);
}
public UnsupportedConstructException(String message, Throwable cause, AbstractRule rule) {
super(message, cause, rule);
}
public UnsupportedConstructException(String message, Throwable cause) {
super(message, cause);
}
public UnsupportedConstructException(String message) {
super(message);
}
public UnsupportedConstructException(Throwable cause, AbstractRule rule) {
super(cause, rule);
}
public UnsupportedConstructException(Throwable cause) {
super(cause);
}
}

View file

@ -29,6 +29,8 @@ import org.eclipse.xtext.Group;
import org.eclipse.xtext.JavaAction;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.HoistingConfiguration;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions.SymbolicAnalysisFailedException;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions.TokenAnalysisAbortedException;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.token.Token;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.utils.MutablePrimitiveWrapper;
@ -56,7 +58,7 @@ public class TokenAnalysis {
result = prefix;
if (needsLength) {
// analysis is not done but there are no more mandatory tokens
throw new TokenAnalysisAbortedException();
throw new TokenAnalysisAbortedException("needed path length not satisfied due to optional cardinality");
}
} else {
result = TokenAnalysisPaths.empty(prefix);
@ -102,7 +104,7 @@ public class TokenAnalysis {
result = prefix;
if (needsLength) {
// analysis is not done but there are no more mandatory tokens
throw new TokenAnalysisAbortedException();
throw new TokenAnalysisAbortedException("needed path length not satisfied due to optional cardinality");
}
} else {
result = TokenAnalysisPaths.empty(prefix);
@ -123,7 +125,7 @@ public class TokenAnalysis {
if (needsLength && !current.isDone()) {
// analysis is not done but there are no more mandatory tokens
throw new TokenAnalysisAbortedException();
throw new TokenAnalysisAbortedException("needed path length not satisfied");
}
result = result.merge(current);
@ -144,7 +146,7 @@ public class TokenAnalysis {
if (isOptionalCardinality(path)) {
result = prefix;
if (needsLength) {
throw new TokenAnalysisAbortedException();
throw new TokenAnalysisAbortedException("needed path length not satisfied due to optional cardinality");
}
} else {
result = TokenAnalysisPaths.empty(prefix);
@ -178,7 +180,7 @@ public class TokenAnalysis {
}
if (needsLength) {
throw new TokenAnalysisAbortedException();
throw new TokenAnalysisAbortedException("needed path length not satisfied");
}
} while(loop);
@ -248,7 +250,7 @@ public class TokenAnalysis {
// we can't analyze the paths any further
// TODO maybe assume paths are equal and show warning instead of exception
throw new TokenAnalysisAbortedException();
throw new TokenAnalysisAbortedException("token limit exhausted while looking for identical paths");
}
public boolean arePathsIdentical(AbstractElement path1, AbstractElement path2) {
@ -269,8 +271,13 @@ public class TokenAnalysis {
}
}
// we tried all possible combinations
// TODO: can only happen if no symbolic analysis is implemented
// -> abort
throw new TokenAnalysisAbortedException();
if (limit.get().equals(config.getTokenLimit())) {
throw new TokenAnalysisAbortedException("token limit exhausted while searching for minimal differences");
} else {
throw new TokenAnalysisAbortedException("path length exhausted while searching for minimal differences");
}
}
private boolean tokenCombinations(long prefix, int prefixLength, int ones, Function<List<Integer>, Boolean> callback, MutablePrimitiveWrapper<Integer> limit) {
if (ones == 0) {
@ -295,7 +302,6 @@ public class TokenAnalysis {
// tokens exhausted; abort current prefix
// set limit for calling functions so this index is not checked again
limit.set(i);
log.info("tokens exhausted");
return false;
}
}

View file

@ -1,16 +0,0 @@
/*******************************************************************************
* Copyright (c) 2021 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.pathAnalysis;
/**
* @author overflow - Initial contribution and API
*/
public class TokenAnalysisAbortedException extends RuntimeException {
private static final long serialVersionUID = 4303267001950479292L;
}

View file

@ -818,7 +818,7 @@ public abstract class AbstractAntlrGrammarGenerator {
} else {
_builder.appendImmediate("\n |", "");
}
String _renderPredicate = this._hoistingProcessor.findGuardForElement(e).renderPredicate();
String _renderPredicate = this._hoistingProcessor.findHoistingGuard(e).renderPredicate();
_builder.append(_renderPredicate);
String _dataTypeEbnf = this.dataTypeEbnf(e, supportActions);
_builder.append(_dataTypeEbnf);
@ -890,7 +890,7 @@ public abstract class AbstractAntlrGrammarGenerator {
} else {
_builder.appendImmediate("\n |", "");
}
String _renderPredicate = this._hoistingProcessor.findGuardForElement(element).renderPredicate();
String _renderPredicate = this._hoistingProcessor.findHoistingGuard(element).renderPredicate();
_builder.append(_renderPredicate);
String _ebnf = this.ebnf(element, options, supportActions);
_builder.append(_ebnf);

View file

@ -432,7 +432,7 @@ public class AntlrContentAssistGrammarGenerator extends AbstractAntlrGrammarWith
} else {
_builder.appendImmediate("\n|", "\t");
}
String _renderPredicate = this._hoistingProcessor.findGuardForElement(element).renderPredicate();
String _renderPredicate = this._hoistingProcessor.findHoistingGuard(element).renderPredicate();
_builder.append(_renderPredicate, "\t");
String _ebnf = this.ebnf(element, options, false);
_builder.append(_ebnf, "\t");

View file

@ -586,6 +586,10 @@ public class GrammarUtil {
public static boolean isMultipleAssignment(Action a) {
return "+=".equals(a.getOperator());
}
public static boolean isTrivialCardinality(AbstractElement e) {
return e.getCardinality() == null || e.getCardinality().equals("");
}
public static boolean isOptionalCardinality(AbstractElement e) {
return e.getCardinality() != null && (e.getCardinality().equals("?") || e.getCardinality().equals("*"));