cleanup and refactoring

renamed NestedPrefixAlternativesException
cleanup of guard classes
redo of parentheses avoidance
removed general start and end parentheses (antlr should be able to deal
with that)
This commit is contained in:
overflowerror 2022-02-06 21:35:46 +01:00
parent 742b1dbfbc
commit 4a9667ec4e
23 changed files with 429 additions and 297 deletions

View file

@ -105,7 +105,7 @@ class HoistingGeneratorTest extends AbstractXtextTests {
leaveRule();
}:
(
{(p0)}?=>
{p0}?=>
(
(
)
@ -115,7 +115,7 @@ class HoistingGeneratorTest extends AbstractXtextTests {
}
)
|
{(p1)}?=>
{p1}?=>
(
(
)
@ -202,7 +202,7 @@ class HoistingGeneratorTest extends AbstractXtextTests {
leaveRule();
}:
(
{(p0)}?=>
{p0}?=>
(
)
otherlv_1='a'
@ -376,7 +376,7 @@ class HoistingGeneratorTest extends AbstractXtextTests {
int stackSize = keepStackSize();
}
:
{(p0)}?=>(
{p0}?=>(
{ before(grammarAccess.getSAccess().getGroup_0()); }
(
rule__S__Group_0__0
@ -384,7 +384,7 @@ class HoistingGeneratorTest extends AbstractXtextTests {
{ after(grammarAccess.getSAccess().getGroup_0()); }
)
|
{(p1)}?=>(
{p1}?=>(
{ before(grammarAccess.getSAccess().getGroup_1()); }
(
rule__S__Group_1__0
@ -571,7 +571,7 @@ class HoistingGeneratorTest extends AbstractXtextTests {
(
{ before(grammarAccess.getSAccess().getGroup()); }
(
{(p0)}?=>
{p0}?=>
rule__S__Group__0
)
*

View file

@ -22,7 +22,7 @@ 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.guards.hoistingGuards.HoistingGuard;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
@ -108,7 +108,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertFalse(guard.hasTerminal());
assertEquals("(p0)", guard.render());
assertEquals("p0", guard.render());
}
@Test
@ -126,7 +126,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(p0)", guard.render());
assertEquals("p0", guard.render());
}
@Test
@ -180,7 +180,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(p0)", guard.render());
assertEquals("p0", guard.render());
}
@Test
@ -198,7 +198,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((p0) && (p1))", guard.render());
assertEquals("(p0) && (p1)", guard.render());
}
@Test
@ -217,7 +217,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(p0)", guard.render());
assertEquals("p0", guard.render());
}
@Test
@ -253,7 +253,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((p0) && (p1) && (p2))", guard.render());
assertEquals("(p0) && (p1) && (p2)", guard.render());
}
@Test
@ -271,7 +271,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(p0)", guard.render());
assertEquals("p0", guard.render());
}
@Test
@ -289,7 +289,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(" + keyword("a", 1) + " || (p0))", guard.render());
assertEquals(keyword("a", 1) + " || (p0)", guard.render());
}
@Test
@ -307,7 +307,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(" + keyword("a", 1) + " || (p0))", guard.render());
assertEquals(keyword("a", 1) + " || (p0)", guard.render());
}
@Test
@ -327,7 +327,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(" + keyword("a", 1) + " || (p0))", guard.render());
assertEquals(keyword("a", 1) + " || (p0)", guard.render());
}
@Test
@ -345,7 +345,8 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(" + keyword("a", 1) + " || (p0))", guard.render());
System.out.println(guard);
assertEquals(keyword("a", 1) + " || (p0)", guard.render());
}
@Test
@ -382,7 +383,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(" + keyword("a", 1) + " || (p0))", guard.render());
assertEquals(keyword("a", 1) + " || (p0)", guard.render());
}
@ -401,7 +402,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1)))", guard.render());
assertEquals("(" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1))", guard.render());
}
@Test
@ -436,7 +437,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1)))", guard.render());
assertEquals("(" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1))", guard.render());
}
// predicates in unordered group with optional paths are currently not supported by the algorithm
@ -457,7 +458,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1)))", guard.render());
assertEquals("(" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1))", guard.render());
}
// predicates in unordered group with optional paths are currently not supported by the algorithm
@ -478,7 +479,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1)))", guard.render());
assertEquals("(" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1))", guard.render());
}
// predicates in unordered group with optional paths are currently not supported by the algorithm
@ -501,7 +502,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
System.out.println(guard.toString());
assertEquals("((" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1)))", guard.render());
assertEquals("(" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1))", guard.render());
}
@Test
@ -519,7 +520,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1)))", guard.render());
assertEquals("(" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1))", guard.render());
// check sizes of groups in unordered group
Group group = (Group) rule.getAlternatives();
@ -548,7 +549,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertFalse(guard.hasTerminal());
assertEquals("((p0) || (p1))", guard.render());
assertEquals("(p0) || (p1)", guard.render());
}
@Test
@ -567,7 +568,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1)))", guard.render());
assertEquals("(" + keyword("a", 1) + " || (p0)) && (" + keyword("b", 1) + " || (p1))", guard.render());
}
@Test
@ -588,7 +589,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
System.out.println(guard.toString());
assertEquals("((" + keyword("s", 2) + " || (p0)) && (" + keyword("b", 2) + " || (p1)))", guard.render());
assertEquals("(" + keyword("s", 2) + " || (p0)) && (" + keyword("b", 2) + " || (p1))", guard.render());
}
@Test
@ -609,7 +610,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(((" + keyword("a", 1) + " && " + keyword("b", 1) + ") || (p0)) && ((" + keyword("c", 1) + " && " + keyword("d", 1) + ") || (p1)))", guard.render());
assertEquals("((" + keyword("a", 1) + " && " + keyword("b", 1) + ") || (p0)) && ((" + keyword("c", 1) + " && " + keyword("d", 1) + ") || (p1))", guard.render());
}
@Test
@ -629,7 +630,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + keyword("a", 2) + " || (p0)) && (" + keyword("b", 2) + " || (p1)))", guard.render());
assertEquals("(" + keyword("a", 2) + " || (p0)) && (" + keyword("b", 2) + " || (p1))", guard.render());
}
@Test
@ -650,7 +651,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + keyword("j", 10) + " || (p0) || (p1)) && (" + keyword("k", 10) + " || (p2)))", guard.render());
assertEquals("(" + keyword("j", 10) + " || (p0) || (p1)) && (" + keyword("k", 10) + " || (p2))", guard.render());
}
@Test
@ -671,7 +672,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + keyword("a", 1) + " || (p0) || (p1)) && (" + keyword("b", 1) + " || (p2)))", guard.render());
assertEquals("(" + keyword("a", 1) + " || (p0) || (p1)) && (" + keyword("b", 1) + " || (p2))", guard.render());
// number of elements in Alternatives object has to stay the same
// even though the identical paths are collapsed during the hoisting process
@ -712,7 +713,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + keyword("a", 1) + " || (p0)) && ((" + keyword("b", 1) + " && " + keyword("c", 1) + ") || (p1)))", guard.render());
assertEquals("(" + keyword("a", 1) + " || (p0)) && ((" + keyword("b", 1) + " && " + keyword("c", 1) + ") || (p1))", guard.render());
}
@Test
@ -731,7 +732,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(" + keyword("a", 1) + " || (p0))", guard.render());
assertEquals(keyword("a", 1) + " || (p0)", guard.render());
}
@Test
@ -755,8 +756,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((((" + keyword("b", 2) + " || " + keyword("b", 3) + ") && (" + keyword("c", 2) + " || " + keyword("c", 3) + ")) || (p0)) && (((" + keyword("b", 2) + " || " + keyword("c", 3) + ") && (" + keyword("c", 2) + " || " + keyword("b", 3) + ")) || (p1)))", guard.render());
assertEquals("(((" + keyword("b", 2) + " || " + keyword("b", 3) + ") && (" + keyword("c", 2) + " || " + keyword("c", 3) + ")) || (p0)) && (((" + keyword("b", 2) + " || " + keyword("c", 3) + ") && (" + keyword("c", 2) + " || " + keyword("b", 3) + ")) || (p1))", guard.render());
}
@Test
@ -779,7 +779,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + keyword("b", 3) + " || ((p0) && (p2))) && (" + keyword("c", 3) + " || ((p0) && (p3))) && (" + keyword("d", 3) + " || (p1)))", guard.render());
assertEquals("(" + keyword("b", 3) + " || ((p0) && (p2))) && (" + keyword("c", 3) + " || ((p0) && (p3))) && (" + keyword("d", 3) + " || (p1))", guard.render());
}
@Test
@ -802,7 +802,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + keyword("a", 2) + " || " + keyword("b", 1) + " || ((p0) && (p2))) && (" + keyword("b", 2) + " || " + keyword("b", 1) + " || ((p0) && (p3))) && (" + keyword("a", 1) + " || (p1)))", guard.render());
assertEquals("(" + keyword("a", 2) + " || " + keyword("b", 1) + " || ((p0) && (p2))) && (" + keyword("b", 2) + " || " + keyword("b", 1) + " || ((p0) && (p3))) && (" + keyword("a", 1) + " || (p1))", guard.render());
}
// currently not able to find optimal solution
@ -830,10 +830,10 @@ public class HoistingProcessorTest extends AbstractXtextTests {
// algorithm is not optimal
// optimal result
//assertEquals("((" + keyword("a", 1) + " || (" + keyword("c", 3) + " && " + eof(3) + ") || ((p0) && (p2))) && (" + keyword("c", 1) + " || ((p0) && (p3))) && (" + keyword("d", 3) + " || (p1)))", guard.render());
//assertEquals("(" + keyword("a", 1) + " || (" + keyword("c", 3) + " && " + eof(3) + ") || ((p0) && (p2))) && (" + keyword("c", 1) + " || ((p0) && (p3))) && (" + keyword("d", 3) + " || (p1))", guard.render());
// still valid but non-optimal
assertEquals("((" + keyword("a", 1) + " || (" + keyword("a", 3)+ " && " + keyword("b", 3) + " && " + keyword("c", 3) + " && " + eof(3) + ") || ((p0) && (p2))) && (" + keyword("b", 1) + " || (" + keyword("a", 3)+ " && " + keyword("b", 3) + " && " + keyword("c", 3) + " && " + eof(3) + ") || ((p0) && (p3))) && (" + keyword("d", 3) + " || (p1)))", guard.render());
assertEquals("(" + keyword("a", 1) + " || (" + keyword("a", 3)+ " && " + keyword("b", 3) + " && " + keyword("c", 3) + " && " + eof(3) + ") || ((p0) && (p2))) && (" + keyword("b", 1) + " || (" + keyword("a", 3)+ " && " + keyword("b", 3) + " && " + keyword("c", 3) + " && " + eof(3) + ") || ((p0) && (p3))) && (" + keyword("d", 3) + " || (p1))", guard.render());
}
@Test
@ -850,7 +850,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertEquals("((" + keyword("a", 1) + " || (p0)) && (" + eof(1) + " || (p1)))", guard.render());
assertEquals("(" + keyword("a", 1) + " || (p0)) && (" + eof(1) + " || (p1))", guard.render());
}
@Test
@ -867,7 +867,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
AbstractRule rule = getRule(grammar, "S");
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertEquals("((" + eof(3) + " || (p0)) && (" + keyword("c", 3) + " || (p1)))", guard.render());
assertEquals("(" + eof(3) + " || (p0)) && (" + keyword("c", 3) + " || (p1))", guard.render());
}
@Test
@ -885,7 +885,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
AbstractRule rule = getRule(grammar, "A");
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertEquals("((" + keyword("d", 4) + " || (p0)) && (" + keyword("c", 4) + " || (p1)))", guard.render());
assertEquals("(" + keyword("d", 4) + " || (p0)) && (" + keyword("c", 4) + " || (p1))", guard.render());
}
@Test
@ -904,7 +904,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
AbstractRule rule = getRule(grammar, "A");
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertEquals("(((" + keyword("c", 2) + " && " + keyword("d", 2) + ") || (p0)) && (" + keyword("b", 2) + " || (p1)))", guard.render());
assertEquals("((" + keyword("c", 2) + " && " + keyword("d", 2) + ") || (p0)) && (" + keyword("b", 2) + " || (p1))", guard.render());
}
@Test(expected = TokenAnalysisAbortedException.class)
@ -968,12 +968,10 @@ public class HoistingProcessorTest extends AbstractXtextTests {
assertTrue(guard.hasTerminal());
System.out.println(guard.toString());
assertEquals(
"(" +
"(" + keyword("a", 1) + " || " + eof(2) + " || (p0) || (p1)) && " +
"(" + keyword("b", 2) + " || (p0)) && " +
"(" + keyword("c", 2) + " || (p1)) && " +
"(" + keyword("d", 1) + " || (p2))" +
")",
"(" + keyword("a", 1) + " || " + eof(2) + " || (p0) || (p1)) && " +
"(" + keyword("b", 2) + " || (p0)) && " +
"(" + keyword("c", 2) + " || (p1)) && " +
"(" + keyword("d", 1) + " || (p2))",
guard.render()
);
}
@ -996,11 +994,9 @@ public class HoistingProcessorTest extends AbstractXtextTests {
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals(
"(" +
"(" + eof(1) + " || (p0) || (p1)) && " +
"(" + keyword("a", 1) + " || (p0)) && " +
"(" + keyword("b", 1) + " || (p1))" +
")",
"(" + eof(1) + " || (p0) || (p1)) && " +
"(" + keyword("a", 1) + " || (p0)) && " +
"(" + keyword("b", 1) + " || (p1))",
guard.render()
);
@ -1055,7 +1051,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(((" + keyword("a", 2) + " && " + eof(2) + ") || (p0)) && (" + keyword("b", 2) + " || (p1)))", guard.render());
assertEquals("((" + keyword("a", 2) + " && " + eof(2) + ") || (p0)) && (" + keyword("b", 2) + " || (p1))", guard.render());
}
@Test
@ -1075,7 +1071,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(((" + keyword("a", 2) + " && " + eof(2) + ") || (p0)) && (" + keyword("b", 2) + " || (p1)))", guard.render());
assertEquals("((" + keyword("a", 2) + " && " + eof(2) + ") || (p0)) && (" + keyword("b", 2) + " || (p1))", guard.render());
}
@Test
@ -1095,7 +1091,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(((" + keyword("a", 2) + " && " + eof(2) + ") || (p0)) && (" + keyword("b", 2) + " || (p1)))", guard.render());
assertEquals("((" + keyword("a", 2) + " && " + eof(2) + ") || (p0)) && (" + keyword("b", 2) + " || (p1))", guard.render());
}
@Test
@ -1115,7 +1111,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + eof(2) + " || (p0)) && (" + keyword("a", 2) + " || (p1)))", guard.render());
assertEquals("(" + eof(2) + " || (p0)) && (" + keyword("a", 2) + " || (p1))", guard.render());
}
@Test
@ -1134,7 +1130,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("((" + eof(2) + " || (p0)) && (" + keyword("a", 2) + " || (p1)))", guard.render());
assertEquals("(" + eof(2) + " || (p0)) && (" + keyword("a", 2) + " || (p1))", guard.render());
}
@Test
@ -1155,7 +1151,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(((" + eof(2) + " && " + keyword("c", 2) + ") || (p0)) && (" + keyword("b", 2) + " || (p1)))", guard.render());
assertEquals("((" + eof(2) + " && " + keyword("c", 2) + ") || (p0)) && (" + keyword("b", 2) + " || (p1))", guard.render());
}
@Test
@ -1177,7 +1173,7 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(((" + eof(2) + " && " + keyword("c", 2) + ") || (p0)) && (" + keyword("b", 2) + " || (p1)))", guard.render());
assertEquals("((" + eof(2) + " && " + keyword("c", 2) + ") || (p0)) && (" + keyword("b", 2) + " || (p1))", guard.render());
}
@Test
@ -1201,6 +1197,6 @@ public class HoistingProcessorTest extends AbstractXtextTests {
HoistingGuard guard = hoistingProcessor.findHoistingGuard(rule.getAlternatives());
assertFalse(guard.isTrivial());
assertTrue(guard.hasTerminal());
assertEquals("(((" + eof(2) + " && " + keyword("c", 2) + ") || (p0)) && (" + keyword("a", 2) + " || (p1)))", guard.render());
assertEquals("((" + eof(2) + " && " + keyword("c", 2) + ") || (p0)) && (" + keyword("a", 2) + " || (p1))", guard.render());
}
}

View file

@ -212,7 +212,7 @@ public class HoistingGeneratorTest extends AbstractXtextTests {
_builder_1.append("(");
_builder_1.newLine();
_builder_1.append("\t\t");
_builder_1.append("{(p0)}?=>");
_builder_1.append("{p0}?=>");
_builder_1.newLine();
_builder_1.append("\t\t\t");
_builder_1.append("(");
@ -242,7 +242,7 @@ public class HoistingGeneratorTest extends AbstractXtextTests {
_builder_1.append("|");
_builder_1.newLine();
_builder_1.append("\t\t");
_builder_1.append("{(p1)}?=>");
_builder_1.append("{p1}?=>");
_builder_1.newLine();
_builder_1.append("\t\t\t");
_builder_1.append("(");
@ -431,7 +431,7 @@ public class HoistingGeneratorTest extends AbstractXtextTests {
_builder_1.append("(");
_builder_1.newLine();
_builder_1.append("\t\t");
_builder_1.append("{(p0)}?=>");
_builder_1.append("{p0}?=>");
_builder_1.newLine();
_builder_1.append("\t\t");
_builder_1.append("(");
@ -809,7 +809,7 @@ public class HoistingGeneratorTest extends AbstractXtextTests {
_builder_1.append(":");
_builder_1.newLine();
_builder_1.append("\t");
_builder_1.append("{(p0)}?=>(");
_builder_1.append("{p0}?=>(");
_builder_1.newLine();
_builder_1.append("\t\t");
_builder_1.append("{ before(grammarAccess.getSAccess().getGroup_0()); }");
@ -833,7 +833,7 @@ public class HoistingGeneratorTest extends AbstractXtextTests {
_builder_1.append("|");
_builder_1.newLine();
_builder_1.append("\t");
_builder_1.append("{(p1)}?=>(");
_builder_1.append("{p1}?=>(");
_builder_1.newLine();
_builder_1.append("\t\t");
_builder_1.append("{ before(grammarAccess.getSAccess().getGroup_1()); }");
@ -1262,7 +1262,7 @@ public class HoistingGeneratorTest extends AbstractXtextTests {
_builder_1.append("(");
_builder_1.newLine();
_builder_1.append("\t\t\t");
_builder_1.append("{(p0)}?=>");
_builder_1.append("{p0}?=>");
_builder_1.newLine();
_builder_1.append("\t\t\t");
_builder_1.append("rule__S__Group__0");

View file

@ -25,10 +25,7 @@ public class JavaCodeUtils {
}
static public String formatPredicateForGrammar(String predicate) {
return formatCodeForGrammar(
// remove parentheses
predicate.substring(1, predicate.length() - 1)
);
return formatCodeForGrammar(predicate);
}
static public String formatGuardForGrammar(Guard guard) {

View file

@ -42,20 +42,20 @@ import org.eclipse.xtext.util.Tuples;
import static org.eclipse.xtext.GrammarUtil.*;
import org.eclipse.xtext.xtext.generator.parser.antlr.JavaCodeUtils;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions.NestedPrefixAlternativesException;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions.NestedIdenticalPathException;
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;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.Guard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.HoistingGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.MergedPathGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.PathGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.PredicateGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.SingleTokenGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.TokenGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.TokenSequenceGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.hoistingGuards.AlternativesGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.hoistingGuards.GroupGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.hoistingGuards.HoistingGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.hoistingGuards.MergedPathGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.hoistingGuards.PathGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.hoistingGuards.PredicateGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.tokenGuards.AlternativeTokenSequenceGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.tokenGuards.SingleTokenGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.tokenGuards.TokenGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.tokenGuards.TokenSequenceGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.pathAnalysis.TokenAnalysis;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.token.Token;
import static org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.utils.DebugUtils.*;
@ -292,11 +292,11 @@ public class HoistingProcessor {
(TokenGuard tokenGuard, MergedPathGuard pathGuard) -> Tuples.pair(tokenGuard, pathGuard)
).map(p -> new PathGuard(p.getFirst(), p.getSecond()))
.collect(AlternativesGuard.collector());
} catch(NestedPrefixAlternativesException e) {
// nested prefix alternatives
} catch(NestedIdenticalPathException e) {
// nested identical paths
// -> flatten paths to alternative and try again
// this is very inefficient
log.warn("nested prefix alternatives detected");
log.warn("nested identical paths detected");
log.warn("avoid these since they can't be handled efficiently");
if (config.isDebug())
@ -310,7 +310,7 @@ public class HoistingProcessor {
log.info(flattened.getElements().size());
// TODO: value configurable?
if (flattened.getElements().size() > 100) {
throw new NestedPrefixAlternativesException("nested prefix alternatives can't be analysed because of too many paths");
throw new NestedIdenticalPathException("nested identical paths can't be analysed because of too many paths");
}
return findGuardForAlternatives(flattened, currentRule, true);

View file

@ -0,0 +1,46 @@
/*******************************************************************************
* 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 NestedIdenticalPathException extends TokenAnalysisAbortedException {
private static final long serialVersionUID = -1309434841547765441L;
public NestedIdenticalPathException() {
super();
}
public NestedIdenticalPathException(String message, AbstractRule rule) {
super(message, rule);
}
public NestedIdenticalPathException(String message, Throwable cause, AbstractRule rule) {
super(message, cause, rule);
}
public NestedIdenticalPathException(String message, Throwable cause) {
super(message, cause);
}
public NestedIdenticalPathException(String message) {
super(message);
}
public NestedIdenticalPathException(Throwable cause, AbstractRule rule) {
super(cause, rule);
}
public NestedIdenticalPathException(Throwable cause) {
super(cause);
}
}

View file

@ -1,53 +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.exceptions;
import org.eclipse.xtext.AbstractRule;
/**
* @author overflow - Initial contribution and API
*/
public class NestedPrefixAlternativesException extends TokenAnalysisAbortedException {
private static final long serialVersionUID = -1309434841547765441L;
public NestedPrefixAlternativesException() {
super();
// TODO Auto-generated constructor stub
}
public NestedPrefixAlternativesException(String message, AbstractRule rule) {
super(message, rule);
// TODO Auto-generated constructor stub
}
public NestedPrefixAlternativesException(String message, Throwable cause, AbstractRule rule) {
super(message, cause, rule);
// TODO Auto-generated constructor stub
}
public NestedPrefixAlternativesException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public NestedPrefixAlternativesException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public NestedPrefixAlternativesException(Throwable cause, AbstractRule rule) {
super(cause, rule);
// TODO Auto-generated constructor stub
}
public NestedPrefixAlternativesException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}

View file

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

View file

@ -0,0 +1,24 @@
/*******************************************************************************
* Copyright (c) 2022 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.guards;
/**
* @author overflow - Initial contribution and API
*/
public enum ContextConnective {
DISJUNCTION, CONJUNCTION;
public String addParenthesesIfNot(String str, ContextConnective connective) {
if (this != connective) {
return "(" + str + ")";
} else {
return str;
}
}
}

View file

@ -13,5 +13,15 @@ package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards;
*/
public interface Guard {
boolean isTrivial();
/**
* should only be used for the root guard when rendering.
*
* assume context connective matches inner connective
*/
String render();
/**
* don't render parentheses if inner connective matches context
*/
String render(ContextConnective connective);
}

View file

@ -1,75 +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.guards;
/**
* @author overflow - Initial contribution and API
*/
public interface HoistingGuard extends Guard {
default String renderPredicate() {
if (isTrivial()) {
return "";
} else {
return "{" + render() + "}?=>";
}
}
default String renderDescription() {
if (isTrivial()) {
return "trivial";
} else {
return render();
}
}
boolean hasTerminal();
static public HoistingGuard unguarded() {
return new HoistingGuard() {
@Override
public String render() {
return "true";
}
@Override
public boolean isTrivial() {
return true;
}
@Override
public boolean hasTerminal() {
return false;
}
};
}
static public HoistingGuard terminal() {
return new HoistingGuard() {
@Override
public String render() {
return "true";
}
@Override
public boolean isTrivial() {
return true;
}
@Override
public boolean hasTerminal() {
return true;
}
};
}
static HoistingGuard action() {
// technically not a terminal, but it behaves the same
return terminal();
}
}

View file

@ -6,7 +6,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards;
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.hoistingGuards;
import java.util.Arrays;
import java.util.LinkedList;
@ -15,8 +15,13 @@ import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.ContextConnective;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.Guard;
/**
* @author overflow - Initial contribution and API
*
* AlternativesGuards are the guard conditions of non-empty alternatives.
*/
public class AlternativesGuard implements HoistingGuard {
private List<PathGuard> paths;
@ -36,29 +41,49 @@ public class AlternativesGuard implements HoistingGuard {
@Override
public boolean isTrivial() {
// should never be true, otherwise findAlternativeGuard would
// produce unguarded path hoisting guard
return paths.stream().allMatch(Guard::isTrivial);
}
@Override
public String render() {
List<String> renderedGuards = paths.stream()
.filter(Predicate.not(Guard::isTrivial))
.map(Guard::render)
.collect(Collectors.toList());
public String render(ContextConnective connective) {
// there is at least one path that is not trivial
List<HoistingGuard> relevantGuards = paths.stream()
.filter(Predicate.not(Guard::isTrivial))
.collect(Collectors.toList());
if (renderedGuards.size() == 1) {
if (relevantGuards.size() == 1) {
return paths.get(0).render(connective);
} else {
String result = relevantGuards.stream()
.map(g -> g.render(ContextConnective.CONJUNCTION))
.collect(Collectors.joining(" && "));
return connective.addParenthesesIfNot(result, ContextConnective.CONJUNCTION);
}
}
@Override
public String render() {
// there is at least one path that is not trivial
List<HoistingGuard> relevantGuards = paths.stream()
.filter(Predicate.not(Guard::isTrivial))
.collect(Collectors.toList());
if (relevantGuards.size() == 1) {
return paths.get(0).render();
} else {
return "(" +
String.join(" && ", renderedGuards) +
")";
return render(ContextConnective.CONJUNCTION);
}
}
@Override
public boolean hasTerminal() {
// empty paths are only allowed when all paths are empty
// in that case a MergedPathGuard is returned.
// using the current method there is no way of handling predicates
// after alternatives with empty paths the same way antlr does
// => assume that the alternative always contains tokens
// the only case that works is when all paths are empty
// => a MergedPathGuard is returned by findGuardForAlternatives
return true;
}

View file

@ -6,7 +6,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards;
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.hoistingGuards;
import java.util.Arrays;
import java.util.Collection;
@ -16,9 +16,14 @@ import java.util.stream.Collectors;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.ContextConnective;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.Guard;
/**
* @author overflow - Initial contribution and API
*
* GroupGuards represent the conditions for group elements.
*
*/
public class GroupGuard implements HoistingGuard {
private List<Guard> elementGuards = new LinkedList<>();
@ -50,6 +55,7 @@ public class GroupGuard implements HoistingGuard {
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 {
@ -60,7 +66,7 @@ public class GroupGuard implements HoistingGuard {
elementGuards.stream()
.filter(g -> g instanceof AlternativesGuard)
.map(g -> (AlternativesGuard) g)
.findAny().get());
.findFirst().get());
}
}
@ -74,11 +80,19 @@ public class GroupGuard implements HoistingGuard {
if (elementGuards.size() == 1) {
return elementGuards.get(0).render();
} else {
return "(" +
elementGuards.stream()
.map(Guard::render)
.collect(Collectors.joining(" && ")) +
")";
return render(ContextConnective.CONJUNCTION);
}
}
@Override
public String render(ContextConnective connective) {
if (elementGuards.size() == 1) {
return elementGuards.get(0).render(connective);
} else {
String result = elementGuards.stream()
.map(g -> g.render(ContextConnective.CONJUNCTION))
.collect(Collectors.joining(" && "));
return connective.addParenthesesIfNot(result, ContextConnective.CONJUNCTION);
}
}

View file

@ -0,0 +1,95 @@
/*******************************************************************************
* 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.guards.hoistingGuards;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.ContextConnective;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.Guard;
/**
* @author overflow - Initial contribution and API
*
* A HoistingGuard is a condition that has to be true for a certain path to accessible.
*
*/
public interface HoistingGuard extends Guard {
default String renderPredicate() {
if (isTrivial()) {
return "";
} else {
return "{" + render() + "}?=>";
}
}
default String renderDescription() {
if (isTrivial()) {
return "trivial";
} else {
return render();
}
}
boolean hasTerminal();
static public HoistingGuard unguarded() {
return new UnguardedPathHoistingGuard();
}
static public HoistingGuard terminal() {
return new TerminalHoistingGuard();
}
static class UnguardedPathHoistingGuard implements HoistingGuard {
@Override
public String render() {
return "true";
}
@Override
public String render(ContextConnective connective) {
return render();
}
@Override
public boolean isTrivial() {
return true;
}
@Override
public boolean hasTerminal() {
return false;
}
}
static class TerminalHoistingGuard implements HoistingGuard {
@Override
public String render() {
return "true";
}
@Override
public String render(ContextConnective connective) {
return render();
}
@Override
public boolean isTrivial() {
return true;
}
@Override
public boolean hasTerminal() {
return true;
}
}
static HoistingGuard action() {
// technically not a terminal, but it behaves the same
return new TerminalHoistingGuard();
}
}

View file

@ -6,15 +6,21 @@
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards;
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.hoistingGuards;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.ContextConnective;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.Guard;
/**
* @author overflow - Initial contribution and API
*
* MergedPathGuards are used to combine identical paths in alternatives.
* This class is also used for alternatives that only have empty paths.
*/
public class MergedPathGuard implements HoistingGuard {
private List<HoistingGuard> pathGuards = new LinkedList<>();
@ -49,19 +55,26 @@ public class MergedPathGuard implements HoistingGuard {
if (pathGuards.size() == 1) {
return pathGuards.get(0).render();
} else {
return "(" + renderWithoutParentheses() + ")";
return render(ContextConnective.DISJUNCTION);
}
}
String renderWithoutParentheses() {
return pathGuards.stream()
.map(Guard::render)
@Override
public String render(ContextConnective connective) {
if (pathGuards.size() == 1) {
return pathGuards.get(0).render(connective);
} else {
String result = pathGuards.stream()
.map(g -> g.render(ContextConnective.DISJUNCTION))
.collect(Collectors.joining(" || "));
return connective.addParenthesesIfNot(result, ContextConnective.DISJUNCTION);
}
}
@Override
public boolean hasTerminal() {
// only need to check first element since all paths should be identical
// only need to check first element since all paths should be
// identical with regards to tokens.
return pathGuards.get(0).hasTerminal();
}

View file

@ -6,7 +6,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards;
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.hoistingGuards;
import java.util.Arrays;
import java.util.LinkedList;
@ -14,9 +14,16 @@ import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.ContextConnective;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.Guard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.tokenGuards.TokenGuard;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.tokenGuards.TokenSequenceGuard;
/**
* @author overflow - Initial contribution and API
*
* PathGuards represent that conditions for single paths in alternatives.
*
*/
public class PathGuard implements HoistingGuard {
private TokenGuard tokenGuard;
@ -34,32 +41,28 @@ public class PathGuard implements HoistingGuard {
@Override
public boolean hasTerminal() {
// empty paths are only allowed when all paths are empty
// in that case a MergedPathGuard is returned by findGuardForAlternatives.
// using the current method there is no way of handling predicates
// after alternatives with empty paths the same way antlr does
// => assume that alternative paths always contain tokens
// the only case that works is when all paths are empty
// => a MergedPathGuard is returned by findGuardForAlternatives
return true;
}
@Override
public String render() {
// parentheses needed since tokenGuard is never empty
String result = "(";
return render(ContextConnective.DISJUNCTION);
}
if (tokenGuard instanceof TokenSequenceGuard) {
result += ((TokenSequenceGuard) tokenGuard).renderWithoutParenthesis();
} else {
result += tokenGuard.render();
}
@Override
public String render(ContextConnective connective) {
String result = "";
result += tokenGuard.render(ContextConnective.DISJUNCTION);
result += " || ";
result += hoistngGuard.render(ContextConnective.DISJUNCTION);
if (hoistngGuard instanceof MergedPathGuard) {
result += ((MergedPathGuard) hoistngGuard).renderWithoutParentheses();
} else {
result += hoistngGuard.render();
}
result += ")";
return result;
return connective.addParenthesesIfNot(result, ContextConnective.DISJUNCTION);
}
public static List<PathGuard> collapse(List<PathGuard> paths) {

View file

@ -6,13 +6,17 @@
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards;
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.hoistingGuards;
import org.eclipse.xtext.AbstractSemanticPredicate;
import org.eclipse.xtext.xtext.generator.parser.antlr.JavaCodeUtils;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.ContextConnective;
/**
* @author overflow - Initial contribution and API
*
* PredicateGuards represent the atomic guard condition of a predicate.
*
*/
public class PredicateGuard implements HoistingGuard {
@ -29,9 +33,16 @@ public class PredicateGuard implements HoistingGuard {
@Override
public String render() {
return "(" + JavaCodeUtils.getSource(element.getCode()) + ")";
// no parentheses if this is the root element
return JavaCodeUtils.getSource(element.getCode());
}
@Override
public String render(ContextConnective connective) {
return "(" + render() + ")";
}
@Override
public boolean hasTerminal() {
return false;

View file

@ -6,7 +6,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards;
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.tokenGuards;
import java.util.Arrays;
import java.util.Collection;
@ -14,8 +14,13 @@ import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.ContextConnective;
/**
* @author overflow - Initial contribution and API
*
* An AlternativeTokenSequenceGuard represents alternative token paths as a TokenGuard.
*
*/
public class AlternativeTokenSequenceGuard implements TokenGuard {
private Collection<? extends TokenGuard> alternatives;
@ -26,7 +31,7 @@ public class AlternativeTokenSequenceGuard implements TokenGuard {
public TokenGuard reduce() {
if (alternatives.size() == 1) {
return alternatives.stream().findAny().get();
return alternatives.stream().findFirst().get();
} else {
return this;
}
@ -34,14 +39,22 @@ public class AlternativeTokenSequenceGuard implements TokenGuard {
@Override
public String render() {
if (alternatives.size() != 1) {
return "(" +
alternatives.stream()
.map(TokenGuard::render)
.collect(Collectors.joining(" && ")) +
")";
} else {
if (alternatives.size() == 1) {
return alternatives.stream().findAny().get().render();
} else {
return render(ContextConnective.CONJUNCTION);
}
}
@Override
public String render(ContextConnective connective) {
if (alternatives.size() == 1) {
return alternatives.stream().findAny().get().render(connective);
} else {
String result = alternatives.stream()
.map(g -> g.render(ContextConnective.CONJUNCTION))
.collect(Collectors.joining(" && "));
return connective.addParenthesesIfNot(result, ContextConnective.CONJUNCTION);
}
}

View file

@ -6,12 +6,13 @@
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards;
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.tokenGuards;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.ContextConnective;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.token.Token;
/**
@ -34,6 +35,11 @@ public class SingleTokenGuard implements TokenGuard {
return token.negatedCondition();
}
@Override
public String render(ContextConnective connective) {
return render();
}
@Override
public String toString() {
return "SingleTokenGuard (\n" +

View file

@ -6,12 +6,18 @@
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards;
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.tokenGuards;
import java.util.Set;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.Guard;
/**
* @author overflow - Initial contribution and API
*
* TokenGuards represent the tokens necessary for the given path.
* Usually only tokens that distinguish the current path from other alternative paths are used.
*
*/
public interface TokenGuard extends Guard {
@Override

View file

@ -6,7 +6,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards;
package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.tokenGuards;
import java.util.Arrays;
import java.util.Collection;
@ -15,10 +15,13 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.guards.ContextConnective;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.utils.StreamUtils;
/**
* @author overflow - Initial contribution and API
*
* A TokenSequenceGuard represents a sequence of tokens as a TokenGuard.
*/
public class TokenSequenceGuard implements TokenGuard {
private Collection<? extends TokenGuard> sequence;
@ -61,26 +64,23 @@ public class TokenSequenceGuard implements TokenGuard {
@Override
public String render() {
boolean addParentheses = sequence.size() != 1;
String result = "";
if (addParentheses) {
result += "(";
if (sequence.size() == 1) {
return sequence.stream().findAny().get().render();
} else {
return render(ContextConnective.DISJUNCTION);
}
result += renderWithoutParenthesis();
if (addParentheses) {
result += ")";
}
return result;
}
public String renderWithoutParenthesis() {
return sequence.stream()
.map(TokenGuard::render)
@Override
public String render(ContextConnective connective) {
if (sequence.size() == 1) {
return sequence.stream().findAny().get().render(connective);
} else {
String result = sequence.stream()
.map(g -> g.render(ContextConnective.DISJUNCTION))
.collect(Collectors.joining(" || "));
return connective.addParenthesesIfNot(result, ContextConnective.DISJUNCTION);
}
}
@Override

View file

@ -38,7 +38,7 @@ import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.UnorderedGroup;
import org.eclipse.xtext.util.XtextSwitch;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.HoistingConfiguration;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions.NestedPrefixAlternativesException;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions.NestedIdenticalPathException;
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.token.Token;
@ -486,8 +486,8 @@ public class TokenAnalysis {
throw new TokenAnalysisAbortedException("token limit exhausted while searching for minimal differences");
} else {
// path length exhausted while searching for minimal differences
// this indicates nested prefix alternatives
throw new NestedPrefixAlternativesException();
// this indicates the presence of nested identical paths
throw new NestedIdenticalPathException();
}
}
private boolean tokenCombinations(long prefix, int prefixLength, int ones, Function<List<Integer>, Boolean> callback, MutableWrapper<Integer> limit) {

View file

@ -14,6 +14,7 @@ import org.eclipse.xtext.EnumLiteralDeclaration;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.TerminalRule;
import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.exceptions.NotATokenException;
/**
* @author overflow - Initial contribution and API
@ -23,6 +24,8 @@ public interface Token {
AbstractElement getElement();
int getPosition();
static boolean isToken(AbstractElement element) {
if (element == null) {
return true;
@ -53,6 +56,4 @@ public interface Token {
throw new NotATokenException(element.eClass().getName());
}
int getPosition();
}