diff --git a/src/main/jastadd/Analysis.jrag b/src/main/jastadd/Analysis.jrag index 8f586cea6bbd0abd286b15a8fb618ff5660067f3..8959b7b1280f5e1594d436ca369ff3b1da4aede7 100644 --- a/src/main/jastadd/Analysis.jrag +++ b/src/main/jastadd/Analysis.jrag @@ -29,11 +29,15 @@ aspect TypeAnalysis { aspect ComponentAnalysis { syn boolean Component.isTargetOfDirectedRelation() = false; - eq RelationComponent.isTargetOfDirectedRelation() = isTargetOfRightDirection(); + eq RelationComponent.isTargetOfDirectedRelation() = isTargetOfRightDirection() | isTargetOfLeftDirection(); inh boolean RelationComponent.isTargetOfRightDirection(); eq Relation.getRight().isTargetOfRightDirection() = getDirection() instanceof RightDirection; eq Program.getChild().isTargetOfRightDirection() = false; + inh boolean RelationComponent.isTargetOfLeftDirection(); + eq Relation.getLeft().isTargetOfLeftDirection() + = getDirection() instanceof LeftDirection; + eq Program.getChild().isTargetOfLeftDirection() = false; syn String Component.name() = getID(); diff --git a/src/main/jastadd/Backend.jadd b/src/main/jastadd/Backend.jadd index 5401260ffed8245a5dcd999f3d2d13a8811f6312..cde27fe6206c11bdd9f562df733de2443874b48d 100644 --- a/src/main/jastadd/Backend.jadd +++ b/src/main/jastadd/Backend.jadd @@ -216,6 +216,9 @@ aspect BackendDirectedAPI { public void RightDirection.generateAPI(StringBuilder sb) { relation().getLeft().generateDirectedAPI(sb); } + public void LeftDirection.generateAPI(StringBuilder sb) { + relation().getRight().generateDirectedAPI(sb); + } public abstract void RelationComponent.generateDirectedAPI(StringBuilder sb); public void OneRelationComponent.generateDirectedAPI(StringBuilder sb) { @@ -904,6 +907,9 @@ aspect NameResolutionHelper { public void RightDirection.generateContextDependentNameResolution(StringBuilder sb) { relation().getLeft().generateContextDependentNameResolution(sb); } + public void LeftDirection.generateContextDependentNameResolution(StringBuilder sb) { + relation().getRight().generateContextDependentNameResolution(sb); + } public void Bidirectional.generateContextDependentNameResolution(StringBuilder sb) { relation().getLeft().generateContextDependentNameResolution(sb); relation().getRight().generateContextDependentNameResolution(sb); @@ -1451,6 +1457,9 @@ aspect PrettyPrint { public String RightDirection.prettyPrint() { return "->"; } + public String LeftDirection.prettyPrint() { + return "<-"; + } public String Bidirectional.prettyPrint() { return "<->"; } diff --git a/src/main/jastadd/RelAst.ast b/src/main/jastadd/RelAst.ast index 2b468e91198a0c103a4a6fdc5d2ab0f543f22ca8..9b29df1e866d7b2d4cb87e414aa16f9a3fa5feeb 100644 --- a/src/main/jastadd/RelAst.ast +++ b/src/main/jastadd/RelAst.ast @@ -21,4 +21,5 @@ OptionalRelationComponent : RelationComponent; ManyRelationComponent : RelationComponent; abstract Direction; RightDirection : Direction; +LeftDirection : Direction; Bidirectional : Direction; diff --git a/src/main/jastadd/RelAst.flex b/src/main/jastadd/RelAst.flex index 68b2ebfe461ff2b1e35403f25af755bd04449a9e..7cccbe3c484522ca126427e3df9a57681c52d8d4 100644 --- a/src/main/jastadd/RelAst.flex +++ b/src/main/jastadd/RelAst.flex @@ -62,6 +62,7 @@ ID = [a-zA-Z$_][a-zA-Z0-9$_]* "/" { return sym(Terminals.SLASH); } "?" { return sym(Terminals.QUESTION_MARK); } "->" { return sym(Terminals.RIGHT); } +"<-" { return sym(Terminals.LEFT); } "<->" { return sym(Terminals.BIDIRECTIONAL); } // ID diff --git a/src/main/jastadd/RelAst.parser b/src/main/jastadd/RelAst.parser index 24fb22b1a05e7fe0b150ad543ced5febc04095c6..0a097391b2519dd89a44746e0ae092cbfb6af0c0 100644 --- a/src/main/jastadd/RelAst.parser +++ b/src/main/jastadd/RelAst.parser @@ -101,5 +101,6 @@ RelationComponent relation_comp = Direction direction = RIGHT {: return new RightDirection(); :} + | LEFT {: return new LeftDirection(); :} | BIDIRECTIONAL {: return new Bidirectional(); :} ; diff --git a/src/main/java/org/jastadd/relast/compiler/Compiler.java b/src/main/java/org/jastadd/relast/compiler/Compiler.java index 1a2605fc1db58827aa37302b5b318269e536beec..22b6168a311daab77f2119a93a0d33e26f6a7a64 100644 --- a/src/main/java/org/jastadd/relast/compiler/Compiler.java +++ b/src/main/java/org/jastadd/relast/compiler/Compiler.java @@ -14,7 +14,7 @@ import java.util.List; public class Compiler { - private static final String VERSION = "0.2.2"; + private static final String VERSION = "0.2.3"; private ArrayList<Option<?>> options; private FlagOption optionWriteToFile; diff --git a/src/test/jastadd/errors/ErrorsLeft.expected b/src/test/jastadd/errors/ErrorsLeft.expected new file mode 100644 index 0000000000000000000000000000000000000000..155d0ec5110a4f6156e71033e92137a6aebb0492 --- /dev/null +++ b/src/test/jastadd/errors/ErrorsLeft.expected @@ -0,0 +1,5 @@ +Errors: +$FILENAME Line 5, column 10: Role name missing for type 'A' +$FILENAME Line 6, column 15: Role name missing for type 'B' +$FILENAME Line 7, column 5: The target of a directed relation cannot have a role name +$FILENAME Line 8, column 5: The target of a directed relation may only have multiplicity 1 diff --git a/src/test/jastadd/errors/ErrorsLeft.relast b/src/test/jastadd/errors/ErrorsLeft.relast new file mode 100644 index 0000000000000000000000000000000000000000..e381449c06b7f0223b36b8b0936f417c5811bb7b --- /dev/null +++ b/src/test/jastadd/errors/ErrorsLeft.relast @@ -0,0 +1,8 @@ +Program ::= A* B*; +A; +B; + +rel B <- A; +rel A.bs* <-> B*; +rel B.b <- A.b; +rel B* <- A.b2; diff --git a/src/test/jastadd/errors/InheritanceLeft.expected b/src/test/jastadd/errors/InheritanceLeft.expected new file mode 100644 index 0000000000000000000000000000000000000000..ef568b501af914f79b4dfeb6c0d958c3198ca2f7 --- /dev/null +++ b/src/test/jastadd/errors/InheritanceLeft.expected @@ -0,0 +1,3 @@ +Errors: +$FILENAME Line 2, column 12: Component 'X' is already declared for type 'B1' +$FILENAME Line 6, column 10: Component 'X' is already declared for type 'B2' diff --git a/src/test/jastadd/errors/InheritanceLeft.relast b/src/test/jastadd/errors/InheritanceLeft.relast new file mode 100644 index 0000000000000000000000000000000000000000..a63c2668bb0c27348947fbbb8a8f0a72b173d096 --- /dev/null +++ b/src/test/jastadd/errors/InheritanceLeft.relast @@ -0,0 +1,6 @@ +A ::= X; +B1 : A ::= X; +B2 : A; +X; + +rel X <- B2.X; diff --git a/src/test/jastadd/errors/MultipleLeft.expected b/src/test/jastadd/errors/MultipleLeft.expected new file mode 100644 index 0000000000000000000000000000000000000000..4c2b802fb6b18485a702049349343899940219af --- /dev/null +++ b/src/test/jastadd/errors/MultipleLeft.expected @@ -0,0 +1,5 @@ +Errors: +$FILENAME1 Line 5, column 10: Role name missing for type 'A' +$FILENAME1 Line 6, column 15: Role name missing for type 'B' +$FILENAME2 Line 1, column 5: The target of a directed relation cannot have a role name +$FILENAME2 Line 2, column 5: The target of a directed relation may only have multiplicity 1 diff --git a/src/test/jastadd/errors/MultipleLeft_1.relast b/src/test/jastadd/errors/MultipleLeft_1.relast new file mode 100644 index 0000000000000000000000000000000000000000..c0ac803ccb82f9d34aa7653938f041e1d7c71b7d --- /dev/null +++ b/src/test/jastadd/errors/MultipleLeft_1.relast @@ -0,0 +1,6 @@ +Program ::= A* B*; +A; +B; + +rel B <- A; +rel A.bs* <-> B*; diff --git a/src/test/jastadd/errors/MultipleLeft_2.relast b/src/test/jastadd/errors/MultipleLeft_2.relast new file mode 100644 index 0000000000000000000000000000000000000000..9ee592259ec471097d0f0758df2b451584f9f820 --- /dev/null +++ b/src/test/jastadd/errors/MultipleLeft_2.relast @@ -0,0 +1,2 @@ +rel B.b <- A.b; +rel B* <- A.b2; diff --git a/src/test/jastadd/relations/Relations.relast b/src/test/jastadd/relations/Relations.relast index 6a5622902d79ebffe1b679c291fa84dd12b35f67..892ad39a767a72be44ff1cba5d61c98e2998fa6f 100644 --- a/src/test/jastadd/relations/Relations.relast +++ b/src/test/jastadd/relations/Relations.relast @@ -17,3 +17,8 @@ rel A.Bi6? <-> B.Bi6*; rel A.Bi7* <-> B.Bi7; rel A.Bi8* <-> B.Bi8?; rel A.Bi9* <-> B.Bi9*; + +rel A <- Root.AaLeft?; +rel B <- A.Di1Left ; +rel B <- A.Di2Left?; +rel B <- A.Di3Left*; diff --git a/src/test/java/org/jastadd/relast/tests/Errors.java b/src/test/java/org/jastadd/relast/tests/Errors.java index 8154c198c346527271bbd8768e35bf5e4eaa0394..718c049708281b26dea16e8792062be067c2938e 100644 --- a/src/test/java/org/jastadd/relast/tests/Errors.java +++ b/src/test/java/org/jastadd/relast/tests/Errors.java @@ -27,16 +27,31 @@ class Errors { test("Errors"); } + @Test + void test1Left() throws IOException { + test("ErrorsLeft"); + } + @Test void test2() throws IOException { test("Inheritance"); } + @Test + void test2Left() throws IOException { + test("InheritanceLeft"); + } + @Test void test3() throws IOException { test("Multiple", "Multiple_1", "Multiple_2"); } + @Test + void test3Left() throws IOException { + test("MultipleLeft", "MultipleLeft_1", "MultipleLeft_2"); + } + private void test(String name, String... inFilenames) throws IOException { List<String> inFiles = Arrays.stream(inFilenames.length > 0 ? inFilenames : new String[]{name}) .map(f -> "./src/test/jastadd/errors/" + f + ".relast") diff --git a/src/test/java/org/jastadd/relast/tests/Relations.java b/src/test/java/org/jastadd/relast/tests/Relations.java index a23d9982f0c6a8c3464e3f16dc0edb298f40a527..4ad3a15effc046f926b49a47d8cd792c3b3fcd89 100644 --- a/src/test/java/org/jastadd/relast/tests/Relations.java +++ b/src/test/java/org/jastadd/relast/tests/Relations.java @@ -132,6 +132,100 @@ class Relations { assertEquals(a3.getDi3List(), Arrays.asList()); } + /** + * rel A.Di1 -> B; + */ + @Test + void testDi1Left() { + setup(); + a1.setDi1Left(b2); + a2.setDi1Left(b1); + + assertSame(a1.getDi1Left(), b2); + assertSame(a2.getDi1Left(), b1); + + a2.setDi1Left(b2); + + assertSame(a1.getDi1Left(), b2); + assertSame(a2.getDi1Left(), b2); + + try { + a3.setDi1Left(null); + fail("should throw an exception"); + } catch (Exception e) { + // OK + } + } + + + /** + * rel A.Di2? -> B; + */ + @Test + void testDi2Left() { + setup(); + a1.setDi2Left(b2); + a2.setDi2Left(b1); + + assertSame(a1.getDi2Left(), b2); + assertSame(a2.getDi2Left(), b1); + + a2.setDi2Left(b2); + + assertSame(a1.getDi2Left(), b2); + assertSame(a2.getDi2Left(), b2); + + a2.clearDi2Left(); + + assertSame(a1.getDi2Left(), b2); + assertNull(a2.getDi2Left()); + + assertTrue(a1.hasDi2Left()); + assertFalse(a2.hasDi2Left()); + assertFalse(a3.hasDi2Left()); + } + + + /** + * rel A.Di3* -> B; + */ + @Test + void testDi3Left() { + setup(); + a1.addDi3Left(b1); + a1.addDi3Left(b2); + a1.addDi3Left(b3); + a2.addDi3Left(b2); + + assertEquals(a1.getDi3Lefts(), Arrays.asList(b1, b2, b3)); + assertEquals(a1.getDi3LeftList(), Arrays.asList(b1, b2, b3)); + assertEquals(a2.getDi3Lefts(), Arrays.asList(b2)); + assertEquals(a2.getDi3LeftList(), Arrays.asList(b2)); + assertEquals(a3.getDi3Lefts(), Arrays.asList()); + assertEquals(a3.getDi3LeftList(), Arrays.asList()); + + a1.addDi3Left(b1); + a2.addDi3Left(b1); + a2.addDi3Left(b2); + + assertEquals(a1.getDi3Lefts(), Arrays.asList(b1, b2, b3, b1)); + assertEquals(a1.getDi3LeftList(), Arrays.asList(b1, b2, b3, b1)); + assertEquals(a2.getDi3Lefts(), Arrays.asList(b2, b1, b2)); + assertEquals(a2.getDi3LeftList(), Arrays.asList(b2, b1, b2)); + assertEquals(a3.getDi3Lefts(), Arrays.asList()); + assertEquals(a3.getDi3LeftList(), Arrays.asList()); + + a1.removeDi3Left(b1); + a2.removeDi3Left(b2); + + assertEquals(a1.getDi3Lefts(), Arrays.asList(b2, b3, b1)); + assertEquals(a1.getDi3LeftList(), Arrays.asList(b2, b3, b1)); + assertEquals(a2.getDi3Lefts(), Arrays.asList(b1, b2)); + assertEquals(a2.getDi3LeftList(), Arrays.asList(b1, b2)); + assertEquals(a3.getDi3Lefts(), Arrays.asList()); + assertEquals(a3.getDi3LeftList(), Arrays.asList()); + } + /** * rel A.Bi1 <-> B.Bi1;