collapse nested alternatives to reduce generated code

doesn't work if nested alternative is in nested group
doesn't work if nested alternative is in merged path
This commit is contained in:
overflowerror 2021-11-29 18:33:45 +01:00
parent 2dc2b23ad9
commit 5aba8ab724
5 changed files with 90 additions and 3 deletions

View file

@ -441,7 +441,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findGuardForElement(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + getSyntaxForKeywordToken("c", 3) + " || ((p0) && ((" + getSyntaxForKeywordToken("b", 2) + " || (p2)) && (" + getSyntaxForKeywordToken("c", 2) + " || (p3))))) && (" + getSyntaxForKeywordToken("d", 3) + " || (p1)))", guard.render());
assertEquals("((" + getSyntaxForKeywordToken("b", 2) + " || ((p0) && (p2))) && (" + getSyntaxForKeywordToken("c", 2) + " || ((p0) && (p3))) && (" + getSyntaxForKeywordToken("d", 3) + " || (p1)))", guard.render());
}
@Test

View file

@ -21,7 +21,12 @@ public class AlternativesGuard implements HoistingGuard {
private List<PathGuard> paths;
private AlternativesGuard(List<PathGuard> paths) {
this.paths = paths;
this.paths = PathGuard.collapse(paths);
}
// package private so PathGuard can access this method
List<PathGuard> getPaths() {
return paths;
}
@Override

View file

@ -8,10 +8,14 @@
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
/**
* @author overflow - Initial contribution and API
*/
@ -19,6 +23,13 @@ public class GroupGuard implements HoistingGuard {
private List<Guard> elementGuards = new LinkedList<>();
private boolean hasTerminal = false;
public GroupGuard() {
}
GroupGuard(Collection<Guard> guards) {
guards.forEach(g -> add(g));
}
public void add(Guard guard) {
if (!guard.isTrivial())
elementGuards.add(guard);
@ -28,6 +39,23 @@ public class GroupGuard implements HoistingGuard {
hasTerminal = true;
}
Pair<List<Guard>, AlternativesGuard> deconstructPaths() {
// there can be at most 1 AlternativesGuard since they always have tokens
// if alternatives don't have token the result will be a MergedPathGuard
if (!elementGuards.stream().anyMatch(g -> g instanceof AlternativesGuard)) {
return null;
} else {
return Tuples.pair(
elementGuards.stream()
.filter(g -> !(g instanceof AlternativesGuard))
.collect(Collectors.toList()),
elementGuards.stream()
.filter(g -> g instanceof AlternativesGuard)
.map(g -> (AlternativesGuard) g)
.findAny().get());
}
}
@Override
public boolean isTrivial() {
return elementGuards.isEmpty();
@ -50,5 +78,5 @@ public class GroupGuard implements HoistingGuard {
public boolean hasTerminal() {
return hasTerminal;
}
}

View file

@ -30,6 +30,14 @@ public class MergedPathGuard implements HoistingGuard {
pathGuards.addAll(mergedPathGuard.pathGuards);
}
HoistingGuard simplify() {
if (pathGuards.size() == 1) {
return pathGuards.get(0);
} else {
return this;
}
}
@Override
public boolean isTrivial() {
return pathGuards.stream().anyMatch(Guard::isTrivial);

View file

@ -8,6 +8,11 @@
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.xtext.util.Pair;
/**
* @author overflow - Initial contribution and API
*/
@ -37,4 +42,45 @@ public class PathGuard implements HoistingGuard {
// parentheses needed since tokenGuard is never empty
return "(" + tokenGuard.render() + " || " + hoistngGuard.render() + ")";
}
public static List<PathGuard> collapse(List<PathGuard> paths) {
// we can collapse all paths of alternatives in paths
// because the direct paths of this alternatives instance are already token-guarded
// against all other paths (otherwise the token guard wouldn't be distinct)
List<PathGuard> result = new LinkedList<>();
for (PathGuard path : paths) {
HoistingGuard guard = path.hoistngGuard;
// TODO: allow merged paths
if (guard instanceof MergedPathGuard) {
guard = ((MergedPathGuard) guard).simplify();
}
if (guard instanceof AlternativesGuard) {
result.addAll(((AlternativesGuard) guard).getPaths());
} else if (guard instanceof GroupGuard) {
// TODO: maybe allow nested groups?
Pair<List<Guard>, AlternativesGuard> destructedPaths = ((GroupGuard) guard).deconstructPaths();
if (destructedPaths == null) {
result.add(path);
} else {
destructedPaths.getSecond().getPaths().stream()
.forEach(p -> {
// combine hoisting guards of current sub path with destructed path guards
// construct new path guard and add to result
GroupGuard groupGuard = new GroupGuard(destructedPaths.getFirst());
groupGuard.add(p.hoistngGuard);
result.add(new PathGuard(p.tokenGuard, groupGuard));
});
}
} else {
result.add(path);
}
}
return result;
}
}