diff --git a/src/main/jastadd/Errors.jrag b/src/main/jastadd/Errors.jrag index 5cc6aedcbc3c699f8e3c38150177025c8fc90a64..39a1bfd05e9371a646ba7de7c626757b3734be55 100644 --- a/src/main/jastadd/Errors.jrag +++ b/src/main/jastadd/Errors.jrag @@ -50,17 +50,33 @@ aspect HelpAttributes { eq OptionalRelationComponent.multiplicityOpt() = true; syn boolean RelationComponent.multiplicityMany() = false; eq ManyRelationComponent.multiplicityMany() = true; + + inh ContainedInFile Component.enclosingFileContainer(); + eq TypeDecl.getComponent().enclosingFileContainer() = this; + eq Relation.getChild().enclosingFileContainer() = this; + inh ContainedInFile TypeUse.enclosingFileContainer(); + eq SimpleTypeComponent.getTypeUse().enclosingFileContainer() = enclosingFileContainer(); + eq TokenComponent.getTypeUse().enclosingFileContainer() = enclosingFileContainer(); + eq TypeDecl.getSuper().enclosingFileContainer() = this; + + syn String ASTNode.containedFile() = "Unknown"; + eq Component.containedFile() = enclosingFileContainer() == null ? "Unknown2" : enclosingFileContainer().containedFile(); + eq TypeUse.containedFile() = enclosingFileContainer() == null ? "Unknown3" : enclosingFileContainer().containedFile(); + eq TypeDecl.containedFile() = getFileName(); + eq Relation.containedFile() = getFileName(); } aspect ErrorMessage { public class ErrorMessage implements Comparable<ErrorMessage> { private final ASTNode node; + private final String filename; private final int line; private final int col; private final String message; public ErrorMessage(ASTNode node, String message) { this.node = node; + this.filename = node.containedFile(); this.line = node.getStartLine(); this.col = node.getStartColumn(); this.message = message; @@ -80,12 +96,17 @@ aspect ErrorMessage { } public String toString() { - return "Line " + line + ", column " + col + ": " + message; + return filename + " Line " + line + ", column " + col + ": " + message; } @Override public int compareTo(ErrorMessage err) { - int n = line - err.line; + int n = filename.compareTo(err.filename); + if (n != 0) { + return n; + } + + n = line - err.line; if (n != 0) { return n; } @@ -102,4 +123,4 @@ aspect ErrorMessage { protected ErrorMessage ASTNode.error(String message) { return new ErrorMessage(this, message); } -} \ No newline at end of file +} diff --git a/src/main/jastadd/RelAst.ast b/src/main/jastadd/RelAst.ast index 3a17c0cfb7fea19f5b33dc9a9bb8fe613137b12b..2b468e91198a0c103a4a6fdc5d2ab0f543f22ca8 100644 --- a/src/main/jastadd/RelAst.ast +++ b/src/main/jastadd/RelAst.ast @@ -1,5 +1,6 @@ Program ::= TypeDecl* Relation*; -TypeDecl ::= <ID> <Abstract:boolean> [Super:TypeUse] Component*; +abstract ContainedInFile ::= <FileName> ; +TypeDecl : ContainedInFile ::= <ID> <Abstract:boolean> [Super:TypeUse] Component*; abstract Component ::= <ID>; abstract SimpleTypeComponent : Component ::= TypeUse:SimpleTypeUse; @@ -13,7 +14,7 @@ abstract TypeUse ::= <ID>; SimpleTypeUse : TypeUse; ParameterizedTypeUse : TypeUse ::= TypeUse*; -Relation ::= Left:RelationComponent Direction Right:RelationComponent; +Relation : ContainedInFile ::= Left:RelationComponent Direction Right:RelationComponent; abstract RelationComponent : SimpleTypeComponent; OneRelationComponent : RelationComponent; OptionalRelationComponent : RelationComponent; diff --git a/src/main/jastadd/RelAst.parser b/src/main/jastadd/RelAst.parser index 631b89cc3efc15a3634aeade29d1e84cbb96f4ce..24fb22b1a05e7fe0b150ad543ced5febc04095c6 100644 --- a/src/main/jastadd/RelAst.parser +++ b/src/main/jastadd/RelAst.parser @@ -6,9 +6,23 @@ Program goal = TypeDecl type_decl = ID type_decl_super.s components_opt.c SCOL - {: return new TypeDecl(ID, false, s, c); :} + {: + TypeDecl result = new TypeDecl(); + result.setID(ID); + result.setAbstract(false); + result.setSuperOpt(s); + result.setComponentList(c); + return result; + :} | ABSTRACT ID type_decl_super.s components_opt.c SCOL - {: return new TypeDecl(ID, true, s, c); :} + {: + TypeDecl result = new TypeDecl(); + result.setID(ID); + result.setAbstract(true); + result.setSuperOpt(s); + result.setComponentList(c); + return result; + :} ; Opt type_decl_super = @@ -21,62 +35,68 @@ SimpleTypeUse s_type_use = ; TypeUse type_use = - s_type_use.u {: return u; :} - | parameterized_type_use.p {: return p; :} + s_type_use.u {: return u; :} + | parameterized_type_use.p {: return p; :} ; ParameterizedTypeUse parameterized_type_use = - ID LT type_use_list.l GT {: return new ParameterizedTypeUse(ID, l); :} + ID LT type_use_list.l GT {: return new ParameterizedTypeUse(ID, l); :} ; List type_use_list = - type_use.u {: return new List().add(u); :} - | type_use_list.l COMMA type_use.u {: return l.add(u); :} + type_use.u {: return new List().add(u); :} + | type_use_list.l COMMA type_use.u {: return l.add(u); :} ; List components_opt = - /* empty */ {: return new List(); :} - | ASSIGN components.l {: return l; :} + /* empty */ {: return new List(); :} + | ASSIGN components.l {: return l; :} ; List components = - {: return new List(); :} - | components.l component.c {: return l.add(c); :} + /* empty */ {: return new List(); :} + | components.l component.c {: return l.add(c); :} ; Component component = - ID COL s_type_use.u {: return new NormalComponent(ID, u); :} - | s_type_use.u {: return new NormalComponent(u.getID(), u); :} + ID COL s_type_use.u {: return new NormalComponent(ID, u); :} + | s_type_use.u {: return new NormalComponent(u.getID(), u); :} // List - | ID COL s_type_use.u STAR {: return new ListComponent(ID, u); :} - | s_type_use.u STAR {: return new ListComponent(u.getID(), u); :} + | ID COL s_type_use.u STAR {: return new ListComponent(ID, u); :} + | s_type_use.u STAR {: return new ListComponent(u.getID(), u); :} // Opt - | LBRACKET ID COL s_type_use.u RBRACKET {: return new OptComponent(ID, u); :} - | LBRACKET s_type_use.u RBRACKET {: return new OptComponent(u.getID(), u); :} + | LBRACKET ID COL s_type_use.u RBRACKET {: return new OptComponent(ID, u); :} + | LBRACKET s_type_use.u RBRACKET {: return new OptComponent(u.getID(), u); :} // NTA - | SLASH ID COL s_type_use.u SLASH {: return new NTAComponent(ID, u); :} - | SLASH s_type_use.u SLASH {: return new NTAComponent(u.getID(), u); :} + | SLASH ID COL s_type_use.u SLASH {: return new NTAComponent(ID, u); :} + | SLASH s_type_use.u SLASH {: return new NTAComponent(u.getID(), u); :} // NTA Token (same as NTA) - | SLASH LT ID COL s_type_use.u GT SLASH {: return new NTAComponent(ID, u); :} - | SLASH LT s_type_use.u GT SLASH {: return new NTAComponent(u.getID(), u); :} + | SLASH LT ID COL s_type_use.u GT SLASH {: return new NTAComponent(ID, u); :} + | SLASH LT s_type_use.u GT SLASH {: return new NTAComponent(u.getID(), u); :} // Token - | LT ID COL type_use.u GT {: return new TokenComponent(ID, u); :} - | LT ID GT {: return new TokenComponent(ID, new SimpleTypeUse("String")); :} + | LT ID COL type_use.u GT {: return new TokenComponent(ID, u); :} + | LT ID GT {: return new TokenComponent(ID, new SimpleTypeUse("String")); :} ; Relation relation = RELATION relation_comp.l direction relation_comp.r SCOL - {: return new Relation(l, direction, r); :} + {: + Relation result = new Relation(); + result.setLeft(l); + result.setDirection(direction); + result.setRight(r); + return result; + :} ; RelationComponent relation_comp = // One - s_type_use.u DOT ID {: return new OneRelationComponent(ID, u); :} - | s_type_use.u {: return new OneRelationComponent("", u); :} + s_type_use.u DOT ID {: return new OneRelationComponent(ID, u); :} + | s_type_use.u {: return new OneRelationComponent("", u); :} // Optional - | s_type_use.u DOT ID QUESTION_MARK {: return new OptionalRelationComponent(ID, u); :} - | s_type_use.u QUESTION_MARK {: return new OptionalRelationComponent("", u); :} + | s_type_use.u DOT ID QUESTION_MARK {: return new OptionalRelationComponent(ID, u); :} + | s_type_use.u QUESTION_MARK {: return new OptionalRelationComponent("", u); :} // Many - | s_type_use.u DOT ID STAR {: return new ManyRelationComponent(ID, u); :} - | s_type_use.u STAR {: return new ManyRelationComponent("", u); :} + | s_type_use.u DOT ID STAR {: return new ManyRelationComponent(ID, u); :} + | s_type_use.u STAR {: return new ManyRelationComponent("", u); :} ; Direction direction = diff --git a/src/main/java/org/jastadd/relast/compiler/Compiler.java b/src/main/java/org/jastadd/relast/compiler/Compiler.java index cfdfb473517ba71c2ea68b6eb233fda1a4e785a4..0ec91633b0f04ea6415159ae74510184ab4fa97f 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.1"; + private static final String VERSION = "0.2.2"; private ArrayList<Option<?>> options; private FlagOption optionWriteToFile; @@ -177,9 +177,11 @@ public class Compiler { try { Program newProgram = (Program) parser.parse(scanner); for (TypeDecl typeDecl : newProgram.getTypeDeclList()) { + typeDecl.setFileName(file); program.addTypeDecl(typeDecl); } for (Relation relation : newProgram.getRelationList()) { + relation.setFileName(file); program.addRelation(relation); } } catch (IOException e) { diff --git a/src/test/jastadd/errors/Errors.expected b/src/test/jastadd/errors/Errors.expected index 93b0d7d9e8e17a2399c0e5fca93b2af03c0d43e0..01049a4a3ae266a25ed024698f93ad49b129aab2 100644 --- a/src/test/jastadd/errors/Errors.expected +++ b/src/test/jastadd/errors/Errors.expected @@ -1,5 +1,5 @@ Errors: -Line 5, column 5: Role name missing for type 'A' -Line 6, column 15: Role name missing for type 'B' -Line 7, column 12: The target of a directed relation cannot have a role name -Line 8, column 13: The target of a directed relation may only have multiplicity 1 +$FILENAME Line 5, column 5: Role name missing for type 'A' +$FILENAME Line 6, column 15: Role name missing for type 'B' +$FILENAME Line 7, column 12: The target of a directed relation cannot have a role name +$FILENAME Line 8, column 13: The target of a directed relation may only have multiplicity 1 diff --git a/src/test/jastadd/errors/Inheritance.expected b/src/test/jastadd/errors/Inheritance.expected index 42aab57d7f2ea21c0440ff60a46e30191482c7de..048fc9760094a975cbe8e5ffec1207b72fcd55c9 100644 --- a/src/test/jastadd/errors/Inheritance.expected +++ b/src/test/jastadd/errors/Inheritance.expected @@ -1,3 +1,3 @@ Errors: -Line 2, column 12: Component 'X' is already declared for type 'B1' -Line 6, column 5: Component 'X' is already declared for type 'B2' +$FILENAME Line 2, column 12: Component 'X' is already declared for type 'B1' +$FILENAME Line 6, column 5: Component 'X' is already declared for type 'B2' diff --git a/src/test/jastadd/errors/Multiple.expected b/src/test/jastadd/errors/Multiple.expected new file mode 100644 index 0000000000000000000000000000000000000000..f2e680e63959a02ce82d6a13ef730dcaa1b571b0 --- /dev/null +++ b/src/test/jastadd/errors/Multiple.expected @@ -0,0 +1,5 @@ +Errors: +$FILENAME1 Line 5, column 5: Role name missing for type 'A' +$FILENAME1 Line 6, column 15: Role name missing for type 'B' +$FILENAME2 Line 1, column 12: The target of a directed relation cannot have a role name +$FILENAME2 Line 2, column 13: The target of a directed relation may only have multiplicity 1 diff --git a/src/test/jastadd/errors/Multiple_1.relast b/src/test/jastadd/errors/Multiple_1.relast new file mode 100644 index 0000000000000000000000000000000000000000..e66cfac704e5b63c965d4339ea6984815f650ed2 --- /dev/null +++ b/src/test/jastadd/errors/Multiple_1.relast @@ -0,0 +1,6 @@ +Program ::= A* B*; +A; +B; + +rel A -> B; +rel A.bs* <-> B*; diff --git a/src/test/jastadd/errors/Multiple_2.relast b/src/test/jastadd/errors/Multiple_2.relast new file mode 100644 index 0000000000000000000000000000000000000000..4ffd076b84de87f180b274f6ee4af5abbcd6a29b --- /dev/null +++ b/src/test/jastadd/errors/Multiple_2.relast @@ -0,0 +1,2 @@ +rel A.b -> B.b; +rel A.b2 -> B*; diff --git a/src/test/java/org/jastadd/relast/tests/Errors.java b/src/test/java/org/jastadd/relast/tests/Errors.java index 63b597f3a1fb6eee6f969619ad9047f7c4736a73..8154c198c346527271bbd8768e35bf5e4eaa0394 100644 --- a/src/test/java/org/jastadd/relast/tests/Errors.java +++ b/src/test/java/org/jastadd/relast/tests/Errors.java @@ -9,6 +9,9 @@ import org.junit.jupiter.api.Test; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; import static org.jastadd.relast.tests.TestHelpers.exec; import static org.jastadd.relast.tests.TestHelpers.readFile; @@ -17,39 +20,33 @@ import static org.jastadd.relast.tests.TestHelpers.readFile; class Errors { private static final Logger logger = LogManager.getLogger(Errors.class); + private static final String FILENAME_PATTERN = "$FILENAME"; + @Test void test1() throws IOException { - - String inFile = "./src/test/jastadd/errors/Errors.relast"; - String outFile = "./src/test/jastadd/errors/Errors.out"; - String expectedFile = "./src/test/jastadd/errors/Errors.expected"; - - try { - System.out.println(System.getProperty("user.dir")); - int returnValue = exec(Compiler.class, new String[]{inFile}, new File(outFile)); - Assertions.assertEquals(1, returnValue, "Relast did not return with value 1"); - } catch (IOException | InterruptedException e) { - e.printStackTrace(); - } - - String out = readFile(outFile, Charset.defaultCharset()); - String expected = readFile(expectedFile, Charset.defaultCharset()); - - Assertions.assertEquals(out, expected); - - logger.info("'relast Errors.relast' returned \n{}", out); + test("Errors"); } @Test void test2() throws IOException { + test("Inheritance"); + } - String inFile = "./src/test/jastadd/errors/Inheritance.relast"; - String outFile = "./src/test/jastadd/errors/Inheritance.out"; - String expectedFile = "./src/test/jastadd/errors/Inheritance.expected"; + @Test + void test3() throws IOException { + test("Multiple", "Multiple_1", "Multiple_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") + .collect(Collectors.toList()); + String outFile = "./src/test/jastadd/errors/" + name + ".out"; + String expectedFile = "./src/test/jastadd/errors/" + name + ".expected"; try { System.out.println(System.getProperty("user.dir")); - int returnValue = exec(Compiler.class, new String[]{inFile}, new File(outFile)); + int returnValue = exec(Compiler.class, inFiles.toArray(new String[0]), new File(outFile)); Assertions.assertEquals(1, returnValue, "Relast did not return with value 1"); } catch (IOException | InterruptedException e) { e.printStackTrace(); @@ -57,10 +54,16 @@ class Errors { String out = readFile(outFile, Charset.defaultCharset()); String expected = readFile(expectedFile, Charset.defaultCharset()); + if (inFiles.size() == 1) { + expected = expected.replace(FILENAME_PATTERN, inFiles.get(0)); + } else { + for (int i = 0; i < inFiles.size(); i++) { + expected = expected.replace(FILENAME_PATTERN + (i + 1), inFiles.get(i)); + } + } + Assertions.assertEquals(expected, out); - Assertions.assertEquals(out, expected); - - logger.info("'relast Inheritance.relast' returned \n{}", out); + logger.info("relast for " + name + " returned \n{}", out); }