From e4ea85c0ced3beb63ecafd7858358dab890caa97 Mon Sep 17 00:00:00 2001 From: Niklas Fors <niklas.fors@cs.lth.se> Date: Fri, 6 Jul 2018 16:30:18 +0200 Subject: [PATCH] Generate method violateLowerBounds() and fix minor things with lists/opts --- spec/jastadd/Analysis.jrag | 10 +++ spec/jastadd/Backend.jadd | 100 +++++++++++++++++++++++++++- spec/parser/RelAstBase.parser | 4 +- spec/scanner/RelAst.flex | 2 + tests/valid/.gitignore | 6 +- tests/valid/AbstractTests.java | 25 +++++++ tests/valid/{Test.java => All.java} | 29 +------- tests/valid/LowerBounds.java | 47 +++++++++++++ tests/valid/LowerBounds.relast | 8 +++ tests/valid/Makefile | 17 +++-- 10 files changed, 206 insertions(+), 42 deletions(-) create mode 100644 tests/valid/AbstractTests.java rename tests/valid/{Test.java => All.java} (93%) create mode 100644 tests/valid/LowerBounds.java create mode 100644 tests/valid/LowerBounds.relast diff --git a/spec/jastadd/Analysis.jrag b/spec/jastadd/Analysis.jrag index 6ee9e64..a9d3711 100644 --- a/spec/jastadd/Analysis.jrag +++ b/spec/jastadd/Analysis.jrag @@ -85,6 +85,16 @@ aspect ComponentAnalysis { when !isTargetOfDirectedRelation() && toTypeDecl() != null to TypeDecl.relationComponents() for toTypeDecl(); + + syn Set<OneRelationComponent> TypeDecl.oneRelationComponents() { + Set<OneRelationComponent> set = new HashSet<>(); + for (RelationComponent rc: relationComponents()) { + if (rc instanceof OneRelationComponent) { + set.add((OneRelationComponent) rc); + } + } + return set; + } } aspect Constructors { diff --git a/spec/jastadd/Backend.jadd b/spec/jastadd/Backend.jadd index 763091d..f2bb942 100644 --- a/spec/jastadd/Backend.jadd +++ b/spec/jastadd/Backend.jadd @@ -75,6 +75,7 @@ aspect BackendAspect { sb.append("import java.util.ArrayList;\n"); sb.append("import java.util.Collections;\n"); sb.append("aspect RelAstAPI {\n"); + for (TypeDecl td: getTypeDecls()) { if (td.needsConstructor()) { td.generateConstructor(sb); @@ -84,7 +85,9 @@ aspect BackendAspect { r.generateAPI(sb); } - sb.append(ind(1) + "public void ASTNode.assertNotNull(Object obj) {\n"); + generateLowerBoundCheck(sb); + + sb.append(ind(1) + "public static void ASTNode.assertNotNull(Object obj) {\n"); sb.append(ind(2) + "if (obj == null) {\n"); sb.append(ind(3) + "throw new NullPointerException();\n"); sb.append(ind(2) + "}\n"); @@ -96,17 +99,35 @@ aspect BackendAspect { sb.append(ind(1) + "public " + getID() + "." + getID() + "("); int i = 0; for (Component c: componentsTransitive()) { - sb.append(c.getTypeUse() + " " + c.getID()); + sb.append(c.constructorParameter()); if (++i < componentsTransitive().size()) { sb.append(", "); } } sb.append(") {\n"); for (Component c: componentsTransitive()) { - sb.append(ind(2) + "set" + c.getID() + "(" + c.getID() + ");\n"); + sb.append(ind(2) + c.constructorSetMethod() + "(" + c.getID() + ");\n"); } sb.append(ind(1) + "}\n"); } + public String Component.constructorParameter() { + return getTypeUse() + " " + getID(); + } + public String ListComponent.constructorParameter() { + return "List<" + getTypeUse() + "> " + getID(); + } + public String OptComponent.constructorParameter() { + return "Opt<" + getTypeUse() + "> " + getID(); + } + public String Component.constructorSetMethod() { + return "set" + getID(); + } + public String ListComponent.constructorSetMethod() { + return "set" + getID() + "List"; + } + public String OptComponent.constructorSetMethod() { + return "set" + getID() + "Opt"; + } } aspect BackendAPI { @@ -403,6 +424,79 @@ aspect BackendBidirectionalAPI { } } +aspect LowerBoundCheck { + public void Program.generateLowerBoundCheck(StringBuilder sb) { + sb.append(ind(1) + "public boolean ASTNode.violateLowerBounds() {\n"); + sb.append(ind(2) + "return !getLowerBoundsViolations().isEmpty();\n"); + sb.append(ind(1) + "}\n"); + + sb.append(ind(1) + "public java.util.List<Pair<ASTNode, String>> " + + "ASTNode.getLowerBoundsViolations() {\n"); + sb.append(ind(2) + "ArrayList<Pair<ASTNode, String>> list = new ArrayList<>();\n"); + sb.append(ind(2) + "computeLowerBoundsViolations(list);\n"); + sb.append(ind(2) + "return list;\n"); + sb.append(ind(1) + "}\n"); + + sb.append(ind(1) + "public void ASTNode.computeLowerBoundsViolations(" + + "java.util.List<Pair<ASTNode, String>> list) {\n"); + sb.append(ind(2) + "for (int i = 0; i < getNumChildNoTransform(); i++) {\n"); + sb.append(ind(3) + "getChildNoTransform(i).computeLowerBoundsViolations(list);\n"); + sb.append(ind(2) + "}\n"); + sb.append(ind(1) + "}\n"); + + for (TypeDecl td: getTypeDecls()) { + td.generateLowerBoundCheck(sb); + } + + generatePairClass(sb); + } + + public void TypeDecl.generateLowerBoundCheck(StringBuilder sb) { + if (!oneRelationComponents().isEmpty()) { + sb.append(ind(1) + "public void " + getID() + ".computeLowerBoundsViolations(" + + "java.util.List<Pair<ASTNode, String>> list) {\n"); + for (OneRelationComponent o: oneRelationComponents()) { + o.generateLowerBoundCheck(sb); + } + sb.append(ind(2) + "super.computeLowerBoundsViolations(list);\n"); + sb.append(ind(1) + "}\n"); + } + } + + public void OneRelationComponent.generateLowerBoundCheck(StringBuilder sb) { + sb.append(ind(2) + "if (" + name() + "() == null) {\n"); + sb.append(ind(3) + "list.add(new Pair<>(this, \"" + name() + "\"));\n"); + sb.append(ind(2) + "}\n"); + } + + + public void Program.generatePairClass(StringBuilder sb) { + sb.append(ind(1) + "public class Pair<T1, T2> {\n"); + sb.append(ind(2) + "public final T1 _1;\n"); + sb.append(ind(2) + "public final T2 _2;\n"); + // Constructor + sb.append(ind(2) + "public Pair(T1 _1, T2 _2) {\n"); + sb.append(ind(3) + "ASTNode.assertNotNull(_1);\n"); + sb.append(ind(3) + "ASTNode.assertNotNull(_2);\n"); + sb.append(ind(3) + "this._1 = _1;\n"); + sb.append(ind(3) + "this._2 = _2;\n"); + sb.append(ind(2) + "}\n"); + // equals + sb.append(ind(2) + "public boolean equals(Object other) {\n"); + sb.append(ind(3) + "if (other instanceof Pair) {\n"); + sb.append(ind(4) + "Pair<?,?> p = (Pair<?,?>) other;\n"); + sb.append(ind(4) + "return _1.equals(p._1) && _2.equals(p._2);\n"); + sb.append(ind(3) + "} else {\n"); + sb.append(ind(4) + "return false;\n"); + sb.append(ind(3) + "}\n"); + sb.append(ind(2) + "}\n"); + // hashCode + sb.append(ind(2) + "public int hashCode() {\n"); + sb.append(ind(3) + "return 31*_1.hashCode() + _2.hashCode();\n"); + sb.append(ind(2) + "}\n"); + sb.append(ind(1) + "}\n"); + } +} aspect PrettyPrint { public String Relation.prettyPrint() { diff --git a/spec/parser/RelAstBase.parser b/spec/parser/RelAstBase.parser index 6b99a76..15fca6a 100644 --- a/spec/parser/RelAstBase.parser +++ b/spec/parser/RelAstBase.parser @@ -53,8 +53,8 @@ Component component = | ID COL s_type_use.u STAR {: return new ListComponent(ID, u); :} | s_type_use.u STAR {: return new ListComponent(u.getID(), u); :} // Opt - | ID COL s_type_use.u QUESTION_MARK {: return new OptComponent(ID, u); :} - | s_type_use.u QUESTION_MARK {: 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); :} // Token | LT ID COL type_use.u GT {: return new TokenComponent(ID, u); :} | LT ID GT {: return new TokenComponent(ID, new SimpleTypeUse("String")); :} diff --git a/spec/scanner/RelAst.flex b/spec/scanner/RelAst.flex index 44ec2a2..27d068b 100644 --- a/spec/scanner/RelAst.flex +++ b/spec/scanner/RelAst.flex @@ -60,6 +60,8 @@ ID = [a-zA-Z$_][a-zA-Z0-9$_]* "," { return sym(Terminals.COMMA); } "<" { return sym(Terminals.LT); } ">" { return sym(Terminals.GT); } + "[" { return sym(Terminals.LBRACKET); } + "]" { return sym(Terminals.RBRACKET); } "?" { return sym(Terminals.QUESTION_MARK); } "->" { return sym(Terminals.RIGHT); } "<->" { return sym(Terminals.BIDIRECTIONAL); } diff --git a/tests/valid/.gitignore b/tests/valid/.gitignore index 12f898e..670d442 100644 --- a/tests/valid/.gitignore +++ b/tests/valid/.gitignore @@ -1,5 +1,3 @@ /AST/* -/AllGen.ast -/AllGen.jadd -/AllGenGen.ast -/AllGenGen.jadd +/*Gen.ast +/*Gen.jadd diff --git a/tests/valid/AbstractTests.java b/tests/valid/AbstractTests.java new file mode 100644 index 0000000..3b86096 --- /dev/null +++ b/tests/valid/AbstractTests.java @@ -0,0 +1,25 @@ +public class AbstractTests { + protected void assertException() { + check(false, "should throw exception"); + } + protected void assertTrue(boolean b) { + check(b, "value should be true (is false)"); + } + protected void assertFalse(boolean b) { + check(!b, "value should be flase (is true)"); + } + protected void assertNull(Object obj) { + check(obj == null, "Object not null: " + obj); + } + protected void assertSame(Object o1, Object o2) { + check(o1 == o2, "Objects not same: " + o1 + ", " + o2); + } + protected void assertEquals(Object o1, Object o2) { + check(o1.equals(o2), "Objects not equals: " + o1 + ", " + o2); + } + protected void check(boolean b, String message) { + if (!b) { + throw new RuntimeException(message); + } + } +} \ No newline at end of file diff --git a/tests/valid/Test.java b/tests/valid/All.java similarity index 93% rename from tests/valid/Test.java rename to tests/valid/All.java index c2f0665..3771423 100644 --- a/tests/valid/Test.java +++ b/tests/valid/All.java @@ -1,7 +1,7 @@ import AST.*; import java.util.*; -public class Test { +public class All extends AbstractTests { private Root r; private A a1; private A a2; @@ -11,7 +11,7 @@ public class Test { private B b3; public static void main(String args[]) { - new Test().test(); + new All().test(); } public void test() { @@ -551,29 +551,4 @@ public class Test { r.addB(b2); r.addB(b3); } - - private void assertException() { - check(false, "should throw exception"); - } - private void assertTrue(boolean b) { - check(b, "value should be true (is false)"); - } - private void assertFalse(boolean b) { - check(!b, "value should be flase (is true)"); - } - private void assertNull(Object obj) { - check(obj == null, "Object not null: " + obj); - } - private void assertSame(Object o1, Object o2) { - check(o1 == o2, "Objects not same: " + o1 + ", " + o2); - } - private void assertEquals(Object o1, Object o2) { - check(o1.equals(o2), "Objects not equals: " + o1 + ", " + o2); - } - private void check(boolean b, String message) { - if (!b) { - throw new RuntimeException(message); - } - - } } \ No newline at end of file diff --git a/tests/valid/LowerBounds.java b/tests/valid/LowerBounds.java new file mode 100644 index 0000000..b2f7832 --- /dev/null +++ b/tests/valid/LowerBounds.java @@ -0,0 +1,47 @@ +import AST.*; + +public class LowerBounds extends AbstractTests { + public static void main(String args[]) { + new LowerBounds().test(); + } + + /* + * Root ::= A* B*; + * A ::= <Name> [C]; + * B ::= <Name>; + * C ::= <Name>; + * rel A.b -> B; + * rel B.c <-> C.b; + * rel Root.aa? -> A; + */ + public void test() { + Root r = new Root(); + C c1 = new C("c1"); + C c2 = new C("c2"); + A a1 = new A("a1", new Opt<>(c1)); + A a2 = new A("a2", new Opt<>(c2)); + B b1 = new B("b1"); + B b2 = new B("b2"); + r.addA(a1); + r.addA(a2); + r.addB(b1); + r.addB(b2); + + assertTrue(r.violateLowerBounds()); + + a1.setB(b1); + a2.setB(b2); + b1.setC(c1); + b2.setC(c2); + + assertFalse(r.violateLowerBounds()); + + b2.setC(c1); + + assertTrue(r.violateLowerBounds()); + + b1.setC(c2); + + assertFalse(r.violateLowerBounds()); + } +} \ No newline at end of file diff --git a/tests/valid/LowerBounds.relast b/tests/valid/LowerBounds.relast new file mode 100644 index 0000000..43b2340 --- /dev/null +++ b/tests/valid/LowerBounds.relast @@ -0,0 +1,8 @@ +Root ::= A* B*; +A ::= <Name> [C]; +B ::= <Name>; +C ::= <Name>; + +rel A.b -> B; +rel B.c <-> C.b; +rel Root.aa? -> A; diff --git a/tests/valid/Makefile b/tests/valid/Makefile index 786fe1f..6db0a7e 100644 --- a/tests/valid/Makefile +++ b/tests/valid/Makefile @@ -1,17 +1,22 @@ all: build-jar test -test: compile run check-idempotent +test: test1 test2 @echo "#" @echo "# VALID TESTS OK" @echo "#" build-jar: (cd ../../ && ant jar) -compile: +test1: java -jar ../../relast-compiler.jar All.relast --file + rm -rf AST java -jar ../../tools/jastadd2.jar --package=AST AllGen.ast AllGen.jadd Utils.jadd -run: - javac AST/*.java Test.java - java Test -check-idempotent: + javac AST/*.java All.java + java All java -jar ../../relast-compiler.jar AllGen.ast --file diff AllGen.ast AllGenGen.ast +test2: + java -jar ../../relast-compiler.jar LowerBounds.relast --file + rm -rf AST + java -jar ../../tools/jastadd2.jar --package=AST LowerBoundsGen.ast LowerBoundsGen.jadd Utils.jadd + javac AST/*.java LowerBounds.java + java LowerBounds -- GitLab