diff --git a/build.gradle b/build.gradle index 9e0bde27ffc2908022aa1a0148b6dc8acec2be35..a6a7882eeee6db3cc34887d5b8cce3fe267905d5 100644 --- a/build.gradle +++ b/build.gradle @@ -17,10 +17,9 @@ buildscript { } dependencies { - implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8' -// api 'org.jastadd:jastadd:2.3.4' - runtime 'org.jastadd:jastadd:2.3.4' + implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: '0.9.6' + implementation group: 'org.apache.logging.log4j', name: 'log4j-jul', version: '2.11.2' + runtime group: 'org.jastadd', name: 'jastadd', version: '2.3.4' api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' } @@ -62,6 +61,7 @@ task relast(type: JavaExec) { "../libs/relast.jar", "../relast.preprocessor/src/main/jastadd/RelAst.relast", "./src/main/jastadd/Ros2Rag.relast", + "./src/main/jastadd/MustacheNodes.relast", "--listClass=java.util.ArrayList", "--jastAddList=JastAddList", "--useJastAddNames", @@ -72,11 +72,12 @@ task relast(type: JavaExec) { inputs.files file("../libs/relast.jar"), file("../relast.preprocessor/src/main/jastadd/RelAST.relast"), - file("src/main/jastadd/Ros2Rag.relast") + file("./src/main/jastadd/Ros2Rag.relast") + file("./src/main/jastadd/MustacheNodes.relast") outputs.files file("./src/gen/jastadd/Ros2Rag.ast"), - file("src/gen/jastadd/Ros2Rag.jadd"), - file("src/gen/jastadd/Ros2RagRefResolver.jadd"), - file('src/gen/jastadd/Ros2RagResolverStubs.jrag') + file("./src/gen/jastadd/Ros2Rag.jadd"), + file("./src/gen/jastadd/Ros2RagRefResolver.jadd"), + file('./src/gen/jastadd/Ros2RagResolverStubs.jrag') } jastadd { diff --git a/src/main/jastadd/MustacheNodes.relast b/src/main/jastadd/MustacheNodes.relast new file mode 100644 index 0000000000000000000000000000000000000000..69d4280f9d9038c838e83767c6e72be9a8d6d5d8 --- /dev/null +++ b/src/main/jastadd/MustacheNodes.relast @@ -0,0 +1,22 @@ +//TypeComponentMustache ; +//rel TypeComponentMustache.TypeComponent -> TypeComponent ; + +MRos2Rag ::= ReadDefinition:MReadDefinition* WriteDefinition:MWriteDefinition* MappingDefinition:MMappingDefinition* DependencyDefinition:MDependencyDefinition* RootTypeComponent:MTypeComponent* TokenComponent:MTokenComponent*; +abstract MUpdateDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*; +MReadDefinition : MUpdateDefinition; +MWriteDefinition : MUpdateDefinition; +MMappingDefinition; +MInnerMappingDefinition; +MDependencyDefinition; +MTypeComponent; +MTokenComponent; + +rel MRos2Rag.Ros2Rag -> Ros2Rag; +rel MInnerMappingDefinition.MappingDefinition -> MappingDefinition; +rel MReadDefinition.ReadFromMqttDefinition -> ReadFromMqttDefinition; +rel MWriteDefinition.WriteToMqttDefinition -> WriteToMqttDefinition; +rel MMappingDefinition.MappingDefinition -> MappingDefinition; +rel MDependencyDefinition.DependencyDefinition -> DependencyDefinition; +rel MTypeComponent.TypeComponent -> TypeComponent; +rel MTokenComponent.TokenComponent -> TokenComponent; +rel MTokenComponent.DependencyDefinition* -> MDependencyDefinition; diff --git a/src/main/jastadd/Navigation.jrag b/src/main/jastadd/Navigation.jrag index f56d99825c376248ce8f9a2dae1388ed3898ea64..a4f21e6e8d79c549a63d54474792382457bf74fd 100644 --- a/src/main/jastadd/Navigation.jrag +++ b/src/main/jastadd/Navigation.jrag @@ -2,10 +2,12 @@ aspect Navigation { // --- program --- eq Ros2Rag.getChild().program() = getProgram(); + eq MRos2Rag.getChild().program() = getRos2Rag().program(); - // --- ros2rag + // --- ros2rag --- inh Ros2Rag ASTNode.ros2rag(); eq Ros2Rag.getChild().ros2rag() = this; + eq MRos2Rag.getChild().ros2rag() = getRos2Rag(); // --- containedFile (first equation should be in preprocessor) --- eq Program.getChild().containedFile() = null; @@ -32,6 +34,7 @@ aspect Navigation { eq GrammarFile.getChild().containedFileName() = getFileName(); eq Ros2Rag.getChild().containedFileName() = getFileName(); eq Program.getChild().containedFileName() = null; + eq MRos2Rag.getChild().containedFileName() = null; // --- isTokenUpdateDefinition --- syn boolean UpdateDefinition.isTokenUpdateDefinition() = false; @@ -49,6 +52,10 @@ aspect Navigation { syn WriteToMqttDefinition UpdateDefinition.asWriteToMqttDefinition() = null; eq WriteToMqttDefinition.asWriteToMqttDefinition() = this; + // --- asReadFromMqttDefinition --- + syn ReadFromMqttDefinition UpdateDefinition.asReadFromMqttDefinition() = null; + eq ReadFromMqttDefinition.asReadFromMqttDefinition() = this; + // --- targetUpdateDefinition --- syn WriteToMqttDefinition DependencyDefinition.targetUpdateDefinition() { // resolve definition in here, as we do not need resolveMethod in any other place (yet) diff --git a/src/main/jastadd/Ros2Rag.relast b/src/main/jastadd/Ros2Rag.relast index a5499702f44facd5b649a811a958b890d4565d0a..e1a84e8beac44fcbc23d09fc56ed536586bde4e8 100644 --- a/src/main/jastadd/Ros2Rag.relast +++ b/src/main/jastadd/Ros2Rag.relast @@ -10,7 +10,6 @@ rel TokenUpdateDefinition.Token -> TokenComponent; ReadFromMqttDefinition : TokenUpdateDefinition; WriteToMqttDefinition : TokenUpdateDefinition; -// example: RobotArm._AppropriateSpeed canDependOn Joint.CurrentPosition as dependency1 DependencyDefinition ::= <ID>; rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*; rel DependencyDefinition.Target -> TokenComponent; diff --git a/src/main/jastadd/backend/Configuration.jadd b/src/main/jastadd/backend/Configuration.jadd index 711ba2511d1e0c8535007cacccac99872128e58f..d4f5b43f27cb329f1ff82318c6f383f0f69ebbd6 100644 --- a/src/main/jastadd/backend/Configuration.jadd +++ b/src/main/jastadd/backend/Configuration.jadd @@ -1,4 +1,5 @@ aspect Configuration { public static boolean ASTNode.loggingEnabledForReads = false; public static boolean ASTNode.loggingEnabledForWrites = false; + public static TypeDecl ASTNode.rootNode; } diff --git a/src/main/jastadd/backend/Generation.jadd b/src/main/jastadd/backend/Generation.jadd index 1b6dfff3386d079e75163c4725681a87904f6786..7aa915a421b4034ffa7f20406b22a041a0868bb9 100644 --- a/src/main/jastadd/backend/Generation.jadd +++ b/src/main/jastadd/backend/Generation.jadd @@ -21,11 +21,188 @@ aspect GenerationUtils { } } +/* Open questions +- Should all string constants be defined on the normal AST, or on the special mustache AST? +*/ + +aspect AttributesForMustache { + // --- MRos2Rag --- + eq MRos2Rag.getChild().mqttUpdaterAttribute() = mqttUpdaterAttribute(); + eq MRos2Rag.getChild().mqttUpdaterField() = mqttUpdaterField(); + + syn String MRos2Rag.mqttUpdaterAttribute() = getRos2Rag().mqttUpdaterAttribute(); + syn String MRos2Rag.mqttUpdaterField() = getRos2Rag().mqttUpdaterField(); + syn String MRos2Rag.mqttSetHostMethod() = getRos2Rag().mqttSetHostMethod(); + syn String MRos2Rag.mqttWaitUntilReadyMethod() = getRos2Rag().mqttWaitUntilReadyMethod(); + syn String MRos2Rag.mqttCloseMethod() = getRos2Rag().mqttCloseMethod(); + + // --- MUpdateDefinition --- + syn String MUpdateDefinition.preemptiveExpectedValue(); + syn String MUpdateDefinition.preemptiveReturn(); + syn TokenUpdateDefinition MUpdateDefinition.updateDef(); + syn String MUpdateDefinition.firstInputVarName(); + + eq MUpdateDefinition.getInnerMappingDefinition(int i).isLast() = i == getNumInnerMappingDefinition() - 1; + eq MUpdateDefinition.getInnerMappingDefinition().resultVarPrefix() = resultVarPrefix(); + eq MUpdateDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : resultVarPrefix() + getInnerMappingDefinition(i - 1).getMappingDefinition().methodName(); + + inh String MUpdateDefinition.mqttUpdaterAttribute(); + + syn String MUpdateDefinition.connectMethod() = updateDef().connectMethod(); + syn TokenComponent MUpdateDefinition.token() = updateDef().getToken(); + syn boolean MUpdateDefinition.alwaysApply() = updateDef().getAlwaysApply(); + syn String MUpdateDefinition.resultVarPrefix() = "result"; // we do not need "_" here, because methodName begins with one + syn String MUpdateDefinition.parentTypeName() = token().containingTypeDecl().getName(); + syn String MUpdateDefinition.tokenName() = token().getName(); + syn MInnerMappingDefinition MUpdateDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1); + syn String MUpdateDefinition.lastDefinitionToType() = lastDefinition().ToType(); + syn String MUpdateDefinition.lastDefinitionName() = lastDefinition().methodName(); + syn String MUpdateDefinition.lastResult() = resultVarPrefix() + lastDefinitionName(); + syn String MUpdateDefinition.condition() { + if (lastDefinition().getMappingDefinition().getToType().isArray()) { + return "java.util.Arrays.equals(" + preemptiveExpectedValue() + ", " + lastResult() + ")"; + } + if (token().isPrimitiveType() && lastDefinition().getMappingDefinition().getToType().isPrimitiveType()) { + return preemptiveExpectedValue() + " == " + lastResult(); + } + if (lastDefinition().getMappingDefinition().isDefaultMappingDefinition()) { + return preemptiveExpectedValue() + " != null && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")"; + } + return preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null"; + } + + // --- MInnerMappingDefinition --- + inh boolean MInnerMappingDefinition.isLast(); + inh String MInnerMappingDefinition.resultVarPrefix(); + syn String MInnerMappingDefinition.ToType() = getMappingDefinition().getToType().prettyPrint(); + syn String MInnerMappingDefinition.methodName() = getMappingDefinition().methodName(); + inh String MInnerMappingDefinition.inputVarName(); + + // --- MReadDefinition --- + eq MReadDefinition.preemptiveExpectedValue() = "get" + tokenName() + "()"; + eq MReadDefinition.preemptiveReturn() = "return;"; + eq MReadDefinition.updateDef() = getReadFromMqttDefinition(); + eq MReadDefinition.firstInputVarName() = "message"; + + // --- MWriteDefinition --- + eq MWriteDefinition.preemptiveExpectedValue() = lastValue(); + eq MWriteDefinition.preemptiveReturn() = "return false;"; + eq MWriteDefinition.updateDef() = getWriteToMqttDefinition(); + eq MWriteDefinition.firstInputVarName() = "get" + tokenName() + "()"; + + syn String MWriteDefinition.writeTopic() = getWriteToMqttDefinition().writeTopic(); + syn String MWriteDefinition.lastValue() = getWriteToMqttDefinition().lastValue(); + syn String MWriteDefinition.updateMethod() = getWriteToMqttDefinition().updateMethod(); + syn String MWriteDefinition.writeMethod() = getWriteToMqttDefinition().writeMethod(); + syn String MWriteDefinition.tokenResetMethod() = getWriteToMqttDefinition().tokenResetMethod(); + + // --- MMappingDefinition --- + syn String MMappingDefinition.toType() = getMappingDefinition().getToType().prettyPrint(); + syn String MMappingDefinition.methodName() = getMappingDefinition().methodName(); + syn String MMappingDefinition.fromType() = getMappingDefinition().getFromType().prettyPrint(); + syn String MMappingDefinition.fromVariableName() = getMappingDefinition().getFromVariableName(); + syn String MMappingDefinition.content() = getMappingDefinition().getContent(); + + // --- MDependencyDefinition --- + syn String MDependencyDefinition.targetParentTypeName() = getDependencyDefinition().getTarget().containingTypeDecl().getName(); + syn String MDependencyDefinition.dependencyMethod() = getDependencyDefinition().dependencyMethod(); + syn String MDependencyDefinition.sourceParentTypeName() = getDependencyDefinition().getSource().containingTypeDecl().getName(); + syn String MDependencyDefinition.internalRelationPrefix() = getDependencyDefinition().internalRelationPrefix(); + syn nta MUpdateDefinition MDependencyDefinition.targetUpdateDefinition() { + return getDependencyDefinition().targetUpdateDefinition().toMustache(); + } + + // --- MTypeComponent --- + syn String MTypeComponent.name() = getTypeComponent().getName(); + inh String MTypeComponent.mqttUpdaterAttribute(); + inh String MTypeComponent.mqttUpdaterField(); + + // --- MTokenComponent --- + syn String MTokenComponent.parentTypeName() = getTokenComponent().containingTypeDecl().getName(); + syn String MTokenComponent.name() = getTokenComponent().getName(); + syn String MTokenComponent.javaType() = getTokenComponent().getJavaTypeUse().prettyPrint(); + syn String MTokenComponent.internalName() = getTokenComponent().internalName(); + + // --- toMustache --- + syn lazy MRos2Rag Ros2Rag.toMustache() { + MRos2Rag result = new MRos2Rag(); + result.setRos2Rag(this); + for (UpdateDefinition def : getUpdateDefinitionList()) { + if (def.isWriteToMqttDefinition()) { + result.addWriteDefinition(def.asWriteToMqttDefinition().toMustache()); + } else { + result.addReadDefinition(def.asReadFromMqttDefinition().toMustache()); + } + } + for (MappingDefinition def : allMappingDefinitions()) { + result.addMappingDefinition(def.toMustache()); + } + for (DependencyDefinition def : getDependencyDefinitionList()) { + result.addDependencyDefinition(def.toMustache()); + } + for (TokenComponent token : getProgram().allTokenComponents()) { + if (!token.getDependencySourceDefinitionList().isEmpty()) { + result.addTokenComponent(token.toMustache()); + } + } + for (Component child : rootNode.getComponentList()) { + if (child.isTypeComponent()) { + result.addRootTypeComponent(child.asTypeComponent().toMustache()); + } + } + return result; + } + +//MInnerMappingDefinition.MappingDefinition -> MappingDefinition; + protected void MUpdateDefinition.addInnerMappings() { + for (MappingDefinition def : updateDef().effectiveMappings()) { + MInnerMappingDefinition inner = new MInnerMappingDefinition(); + inner.setMappingDefinition(def); + addInnerMappingDefinition(inner); + } + } + syn lazy MReadDefinition ReadFromMqttDefinition.toMustache() { + MReadDefinition result = new MReadDefinition(); + result.setReadFromMqttDefinition(this); + result.addInnerMappings(); + return result; + } + syn lazy MWriteDefinition WriteToMqttDefinition.toMustache() { + MWriteDefinition result = new MWriteDefinition(); + result.setWriteToMqttDefinition(this); + result.addInnerMappings(); + return result; + } + syn lazy MMappingDefinition MappingDefinition.toMustache() { + MMappingDefinition result = new MMappingDefinition(); + result.setMappingDefinition(this); + return result; + } + syn lazy MDependencyDefinition DependencyDefinition.toMustache() { + MDependencyDefinition result = new MDependencyDefinition(); + result.setDependencyDefinition(this); + return result; + } + syn lazy MTypeComponent TypeComponent.toMustache() { + MTypeComponent result = new MTypeComponent(); + result.setTypeComponent(this); + return result; + } + syn lazy MTokenComponent TokenComponent.toMustache() { + MTokenComponent result = new MTokenComponent(); + result.setTokenComponent(this); + for (DependencyDefinition def : getDependencySourceDefinitionList()) { + result.addDependencyDefinition(def.toMustache()); + } + return result; + } +} + aspect AspectGeneration { + // naming convention attributes syn String TokenComponent.internalName() = getDependencySourceDefinitionList().isEmpty() ? externalName() : "_internal_" + getName(); syn String TokenComponent.externalName() = getName(); - // naming convention attributes syn String TokenUpdateDefinition.connectMethod() = "connect" + getToken().getName(); syn String WriteToMqttDefinition.writeTopic() = "_topic_" + getToken().getName(); syn String WriteToMqttDefinition.lastValue() = "_lastValue" + getToken().getName(); @@ -39,10 +216,6 @@ aspect AspectGeneration { syn String DependencyDefinition.internalRelationPrefix() = "_internal_" + getID(); syn String DependencyDefinition.internalTokenName() = getSource().internalName(); - inh String UpdateDefinition.mqttUpdaterAttribute(); - inh String MappingDefinition.mqttUpdaterAttribute(); - inh String DependencyDefinition.mqttUpdaterAttribute(); - eq Ros2Rag.getChild().mqttUpdaterAttribute() = mqttUpdaterAttribute(); syn String Ros2Rag.mqttUpdaterAttribute() = "_mqttUpdater"; syn String Ros2Rag.mqttUpdaterField() = "_mqttUpdater"; @@ -50,247 +223,43 @@ aspect AspectGeneration { syn String Ros2Rag.mqttWaitUntilReadyMethod() = "MqttWaitUntilReady"; syn String Ros2Rag.mqttCloseMethod() = "MqttCloseConnections"; - public String Ros2Rag.generateAspect(String rootNodeName) { - StringBuilder sb = new StringBuilder(); - TypeDecl rootNode = getProgram().resolveTypeDecl(rootNodeName); - generateMqttAspect(sb, rootNode); - generateGrammarExtension(sb); - return sb.toString(); - } - - public void Ros2Rag.generateMqttAspect(StringBuilder sb, TypeDecl rootNode) { - String rootNodeName = rootNode.getName(); - sb.append("aspect MQTT {\n"); - sb.append(ind(1)).append("private MqttUpdater ").append(rootNodeName) - .append(".").append(mqttUpdaterField()).append(" = new MqttUpdater();\n"); - - // mqttSetHost(String host) - sb.append(ind(1)).append("public void ").append(rootNodeName).append(".") - .append(mqttSetHostMethod()).append("(String host) throws java.io.IOException {\n"); - sb.append(ind(2)).append(mqttUpdaterField()).append(".setHost(host);\n"); - sb.append(ind(1)).append("}\n"); - - // mqttSetHost(String host, int port) - sb.append(ind(1)).append("public void ").append(rootNodeName).append(".") - .append(mqttSetHostMethod()).append("(String host, int port) throws java.io.IOException {\n"); - sb.append(ind(2)).append(mqttUpdaterField()).append(".setHost(host, port);\n"); - sb.append(ind(1)).append("}\n\n"); - - // mqttWaitUntilReady - sb.append(ind(1)).append("public boolean ").append(rootNodeName).append(".") - .append(mqttWaitUntilReadyMethod()).append("(long time, java.util.concurrent.TimeUnit unit) {\n"); - sb.append(ind(2)).append("return ").append(mqttUpdaterField()).append(".waitUntilReady(time, unit);\n"); - sb.append(ind(1)).append("}\n\n"); - - // mqttClose - sb.append(ind(1)).append("public void ").append(rootNodeName).append(".") - .append(mqttCloseMethod()).append("() {\n"); - sb.append(ind(2)).append(mqttUpdaterField()).append(".close();\n"); - sb.append(ind(1)).append("}\n\n"); - - // mqttUpdater - sb.append(ind(1)).append("inh MqttUpdater ASTNode.").append(mqttUpdaterAttribute()).append("();\n"); - for (Component child : rootNode.getComponentList()) { - if (child.isTypeComponent()) { - sb.append(ind(1)).append("eq ").append(rootNodeName) - .append(".get").append(child.getName()).append("().") - .append(mqttUpdaterAttribute()).append("() = ").append(mqttUpdaterField()).append(";\n"); - } - } - sb.append("}\n\n"); - } - - public void Ros2Rag.generateGrammarExtension(StringBuilder sb) { - sb.append("aspect ROS2RAG {\n"); - - for (UpdateDefinition def : getUpdateDefinitionList()) { - def.generateAspect(sb); - } - for (MappingDefinition def : allMappingDefinitions()) { - def.generateAspect(sb); - } - for (DependencyDefinition def : getDependencyDefinitionList()) { - def.generateAspect(sb); - } - for (TokenComponent token : getProgram().allTokenComponents()) { - token.generateAspect(sb); - } - - sb.append("}\n"); - } - - abstract void UpdateDefinition.generateAspect(StringBuilder sb); - - String TokenUpdateDefinition.generateMappingApplication(StringBuilder sb, int indent, - String initialInputVariableName) { - final String resultVariablePrefix = "result"; // we do not need "_" here, because methodName begins with one - String inputVariableName = initialInputVariableName; - // last variable need to be declared before begin of try - MappingDefinition lastDefinition = effectiveMappings().get(effectiveMappings().size() - 1); - sb.append(ind(indent)).append(lastDefinition.getToType().prettyPrint()).append(" ") - .append(resultVariablePrefix).append(lastDefinition.methodName()).append(";\n"); - sb.append(ind(indent)).append("try {\n"); - for (MappingDefinition mappingDefinition : effectiveMappings()) { - String resultVariableName = resultVariablePrefix + mappingDefinition.methodName(); - sb.append(ind(indent + 1)); - if (mappingDefinition != lastDefinition) { - sb.append(mappingDefinition.getToType().prettyPrint()).append(" "); - } - sb.append(resultVariablePrefix).append(mappingDefinition.methodName()) - .append(" = ").append(mappingDefinition.methodName()).append("(") - .append(inputVariableName).append(");\n"); - inputVariableName = resultVariableName; - } - sb.append(ind(indent)).append("} catch (Exception e) {\n"); - sb.append(ind(indent + 1)).append("e.printStackTrace();\n"); - sb.append(ind(indent + 1)).append(preemptiveReturnStatement()).append("\n"); - sb.append(ind(indent)).append("}\n"); - if (!getAlwaysApply()) { - MappingDefinition lastMapping = effectiveMappings().get(effectiveMappings().size() - 1); - sb.append(ind(indent)).append("if ("); - if (lastMapping.getToType().isArray()) { - sb.append("java.util.Arrays.equals(").append(preemptiveExpectedValue()) - .append(", ").append(inputVariableName).append(")"); - } else { - sb.append(preemptiveExpectedValue()); - if (getToken().isPrimitiveType() && lastMapping.getToType().isPrimitiveType()) { - sb.append(" == ").append(inputVariableName); - } else if (lastMapping.isDefaultMappingDefinition()) { - sb.append(" != null && ").append(preemptiveExpectedValue()).append(".equals(") - .append(inputVariableName).append(")"); - } else { - sb.append(" != null ? ").append(preemptiveExpectedValue()).append(".equals(") - .append(inputVariableName).append(")").append(" : ") - .append(inputVariableName).append(" == null"); - } - } - sb.append(") { ").append(preemptiveReturnStatement()).append(" }\n"); - } - return inputVariableName; - } - - syn String TokenUpdateDefinition.preemptiveExpectedValue(); - eq ReadFromMqttDefinition.preemptiveExpectedValue() = "get" + getToken().getName() + "()"; - eq WriteToMqttDefinition.preemptiveExpectedValue() = lastValue(); - - syn String TokenUpdateDefinition.preemptiveReturnStatement(); - eq ReadFromMqttDefinition.preemptiveReturnStatement() = "return;"; - eq WriteToMqttDefinition.preemptiveReturnStatement() = "return false;"; - - @Override - void ReadFromMqttDefinition.generateAspect(StringBuilder sb) { - sb.append(ind(1)).append("public void ").append(getToken().containingTypeDecl().getName()).append(".") - .append(connectMethod()).append("(String topic) {\n"); - sb.append(ind(2)).append(mqttUpdaterAttribute()).append("().newConnection(topic, message -> {\n"); - String lastResult = generateMappingApplication(sb, 3, "message"); - if (loggingEnabledForReads) { - sb.append(ind(3)).append("System.out.println(\"[Read] \" + topic + \" -> ") - .append(getToken().getName()).append(" = \" + ").append(lastResult) - .append(");\n"); - } - sb.append(ind(3)).append("set").append(getToken().getName()).append("(").append(lastResult).append(");\n"); - sb.append(ind(2)).append("});\n"); - sb.append(ind(1)).append("}\n\n"); - } - - @Override - void WriteToMqttDefinition.generateAspect(StringBuilder sb) { - String parentTypeName = getToken().containingTypeDecl().getName(); - // fields - sb.append(ind(1)).append("private String ").append(parentTypeName).append(".") - .append(writeTopic()).append(" = null;\n"); - sb.append(ind(1)).append("private byte[] ").append(parentTypeName).append(".") - .append(lastValue()).append(" = null;\n"); - - // connect method - sb.append(ind(1)).append("public void ").append(parentTypeName).append(".") - .append(connectMethod()).append("(String topic, boolean writeCurrentValue) {\n"); - sb.append(ind(2)).append(writeTopic()).append(" = topic;\n"); - sb.append(ind(2)).append(updateMethod()).append("();\n"); - sb.append(ind(2)).append("if (writeCurrentValue) {\n"); - sb.append(ind(3)).append(writeMethod()).append("();\n"); - sb.append(ind(2)).append("}\n"); - sb.append(ind(1)).append("}\n\n"); + // naming copy attributes + // --- mqttUpdaterAttribute --- + inh String UpdateDefinition.mqttUpdaterAttribute(); + inh String MappingDefinition.mqttUpdaterAttribute(); + inh String DependencyDefinition.mqttUpdaterAttribute(); + eq Ros2Rag.getChild().mqttUpdaterAttribute() = mqttUpdaterAttribute(); - // update method - sb.append(ind(1)).append("protected boolean ").append(parentTypeName).append(".") - .append(updateMethod()).append("() {\n"); - sb.append(ind(2)).append(tokenResetMethod()).append("();\n"); - String lastResult = generateMappingApplication(sb, 2, "get" + getToken().getName() + "()"); - sb.append(ind(2)).append(lastValue()).append(" = ").append(lastResult).append(";\n"); - sb.append(ind(2)).append("return true;\n"); - sb.append(ind(1)).append("}\n\n"); + // --- rootNodeName --- + syn String ASTNode.rootNodeName() = rootNode.getName(); - // write method - sb.append(ind(1)).append("protected void ").append(parentTypeName).append(".") - .append(writeMethod()).append("() {\n"); - if (loggingEnabledForWrites) { - sb.append(ind(2)).append("System.out.println(\"[Write] ").append(getToken().getName()) - .append(" = \" + ") - .append("get").append(getToken().getName()).append("() + \" -> \" + ") - .append(writeTopic()).append(");\n"); - } - // _mqttUpdater().publish(${writeTopic()}, ${lastValue()}); - sb.append(ind(2)).append(mqttUpdaterAttribute()).append("().publish(") - .append(writeTopic()).append(", ").append(lastValue()).append(");\n"); - sb.append(ind(1)).append("}\n\n"); + public String Ros2Rag.generateAspect(String rootNodeName) { + rootNode = getProgram().resolveTypeDecl(rootNodeName); + return toMustache().generateAspect(); } - void MappingDefinition.generateAspect(StringBuilder sb) { - sb.append(ind(1)).append("protected static ").append(getToType().prettyPrint()) - .append(" ASTNode.").append(methodName()).append("(") - .append(getFromType().prettyPrint()).append(" ").append(getFromVariableName()) - .append(") throws Exception {\n"); - for (String line : getContent().split("\n")) { - if (!line.trim().isEmpty()) { - sb.append(ind(2)).append(line).append("\n"); + public String MRos2Rag.generateAspect() { + StringBuilder sb = new StringBuilder(); + com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() { + @Override + public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) { + return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) { + @Override + protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) { + com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes); + if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) { + throw new com.github.mustachejava.MustacheException(name + " not found in " + tc); + } + return wrapper; + } + }; } - } - sb.append(ind(1)).append("}\n\n"); - } - - void DependencyDefinition.generateAspect(StringBuilder sb) { - String targetParentTypeName = getTarget().containingTypeDecl().getName(); - String sourceParentTypeName = getSource().containingTypeDecl().getName(); - - // dependency method - sb.append(ind(1)).append("public void ").append(targetParentTypeName).append(".") - .append(dependencyMethod()).append("(").append(sourceParentTypeName).append(" source) {\n"); - sb.append(ind(2)).append("add").append(internalRelationPrefix()).append("Source(source);\n"); - sb.append(ind(1)).append("}\n\n"); - } - - void TokenComponent.generateAspect(StringBuilder sb) { - if (getDependencySourceDefinitionList().isEmpty()) { return; } - - String parentTypeName = containingTypeDecl().getName(); - // virtual setter - sb.append(ind(1)).append("public ").append(parentTypeName).append(" ") - .append(parentTypeName).append(".set").append(getName()).append("(") - .append(getJavaTypeUse().prettyPrint()).append(" value) {\n"); - sb.append(ind(2)).append("set").append(internalName()).append("(value);\n"); - - for (DependencyDefinition dependencyDefinition : getDependencySourceDefinitionList()) { - String targetParentTypeName = dependencyDefinition.getTarget().containingTypeDecl().getName(); - sb.append(ind(2)).append("for (").append(targetParentTypeName).append(" target : get") - .append(dependencyDefinition.internalRelationPrefix()).append("TargetList()) {\n"); - sb.append(ind(3)).append("if (target.") - .append(dependencyDefinition.targetUpdateDefinition().updateMethod()) - .append("()) {\n"); - sb.append(ind(4)).append("target.") - .append(dependencyDefinition.targetUpdateDefinition().writeMethod()) - .append("();\n"); - sb.append(ind(3)).append("}\n"); - sb.append(ind(2)).append("}\n"); - } - sb.append(ind(2)).append("return this;\n"); - sb.append(ind(1)).append("}\n\n"); - - // virtual getter - sb.append(ind(1)).append("public ").append(getJavaTypeUse().prettyPrint()) - .append(" ").append(parentTypeName).append(".get").append(getName()).append("() {\n"); - sb.append(ind(2)).append("return get").append(internalName()).append("();\n"); - sb.append(ind(1)).append("}\n\n"); + }; + com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory(); + mf.setObjectHandler(roh); + com.github.mustachejava.Mustache m = mf.compile("ros2rag.mustache"); + m.execute(new java.io.PrintWriter(new org.jastadd.ros2rag.compiler.AppendableWriter(sb)), this); + return sb.toString(); } } diff --git a/src/main/java/org/jastadd/ros2rag/compiler/AppendableWriter.java b/src/main/java/org/jastadd/ros2rag/compiler/AppendableWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..97680b18dc4fd1d8af3df75f35221e059355e19e --- /dev/null +++ b/src/main/java/org/jastadd/ros2rag/compiler/AppendableWriter.java @@ -0,0 +1,37 @@ +package org.jastadd.ros2rag.compiler; + +import java.io.IOException; +import java.io.Writer; + +/** + * Writer appending to a StringBuilder. + * + * @author rschoene - Initial contribution + */ +public class AppendableWriter extends Writer { + private final StringBuilder sb; + + public AppendableWriter(StringBuilder sb) { + this.sb = sb; + } + + @Override + public void write(char[] chars, int off, int len) throws IOException { + sb.append(chars, off, len); + } + + @Override + public void write(String str) throws IOException { + sb.append(str); + } + + @Override + public void flush() { + + } + + @Override + public void close() { + + } +} diff --git a/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java b/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java index ee575b9495f53eca9925c6828ba96225734fe31a..8428bf27961070afa6410a349b3412e7bff729e6 100644 --- a/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java +++ b/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java @@ -37,6 +37,8 @@ public class Compiler { } public void run(String[] args) throws CommandLineException, CompilerException { + System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager"); + System.setProperty("mustache.debug", "true"); options = new ArrayList<>(); addOptions(); commandLine = new CommandLine(options); diff --git a/src/main/resources/dependencyDefinition.mustache b/src/main/resources/dependencyDefinition.mustache new file mode 100644 index 0000000000000000000000000000000000000000..b8d74ffe8f8aa5aaa8979682e4442215af1974e8 --- /dev/null +++ b/src/main/resources/dependencyDefinition.mustache @@ -0,0 +1,3 @@ + public void {{targetParentTypeName}}.{{dependencyMethod}}({{sourceParentTypeName}} source) { + add{{internalRelationPrefix}}Source(source); + } diff --git a/src/main/resources/mappingApplication.mustache b/src/main/resources/mappingApplication.mustache new file mode 100644 index 0000000000000000000000000000000000000000..95ce2fe29329d87f880497b37697d50c8c0687be --- /dev/null +++ b/src/main/resources/mappingApplication.mustache @@ -0,0 +1,14 @@ +{{lastDefinitionToType}} {{resultVarPrefix}}{{lastDefinitionName}}; +try { + {{#InnerMappingDefinitions}} + {{^last}}{{ToType}} {{/last}}{{resultVarPrefix}}{{methodName}} = {{methodName}}({{inputVarName}});{{!inputVarName has to be computed beforehand}} + {{/InnerMappingDefinitions}} +} catch (Exception e) { + e.printStackTrace(); + {{preemptiveReturn}} +} +{{^alwaysApply}} +if ({{{condition}}}) { + {{preemptiveReturn}} +} +{{/alwaysApply}} diff --git a/src/main/resources/mappingDefinition.mustache b/src/main/resources/mappingDefinition.mustache new file mode 100644 index 0000000000000000000000000000000000000000..920c5a610b0ce0cff1687122c97b6e18ce63bf7f --- /dev/null +++ b/src/main/resources/mappingDefinition.mustache @@ -0,0 +1,3 @@ + protected static {{toType}} ASTNode.{{methodName}}({{fromType}} {{fromVariableName}}) throws Exception { + {{{content}}}{{!maybe print line by line to get better indentation}} + } diff --git a/src/main/resources/mqtt.mustache b/src/main/resources/mqtt.mustache new file mode 100644 index 0000000000000000000000000000000000000000..2474e16525f6058540c3d73191369f4d2d056c4f --- /dev/null +++ b/src/main/resources/mqtt.mustache @@ -0,0 +1,22 @@ +aspect MQTT { + private MqttUpdater {{rootNodeName}}.{{mqttUpdaterField}} = new MqttUpdater(); + public void {{rootNodeName}}.{{mqttSetHostMethod}}(String host) throws java.io.IOException { + {{mqttUpdaterField}}.setHost(host); + } + public void {{rootNodeName}}.{{mqttSetHostMethod}}(String host, int port) throws java.io.IOException { + {{mqttUpdaterField}}.setHost(host, port); + } + + public boolean {{rootNodeName}}.{{mqttWaitUntilReadyMethod}}(long time, java.util.concurrent.TimeUnit unit) { + return {{mqttUpdaterField}}.waitUntilReady(time, unit); + } + + public void {{rootNodeName}}.{{mqttCloseMethod}}() { + {{mqttUpdaterField}}.close(); + } + + inh MqttUpdater ASTNode.{{mqttUpdaterAttribute}}(); + {{#getRootTypeComponents}} + eq {{rootNodeName}}.get{{name}}().{{mqttUpdaterAttribute}}() = {{mqttUpdaterField}}; + {{/getRootTypeComponents}} +} diff --git a/src/main/resources/readDefinition.mustache b/src/main/resources/readDefinition.mustache new file mode 100644 index 0000000000000000000000000000000000000000..f908ef166db8eb781d7b7d46e7bae1719ea5fb1d --- /dev/null +++ b/src/main/resources/readDefinition.mustache @@ -0,0 +1,9 @@ + public void {{parentTypeName}}.{{connectMethod}}(String topic) { + {{mqttUpdaterAttribute}}().newConnection(topic, message -> { + {{> mappingApplication}} + {{#loggingEnabledForReads}} + System.out.println("[Read] " + topic + " -> {{tokenName}} = " + {{lastResult}});{{!lastResult has to be a new attribute}} + {{/loggingEnabledForReads}} + set{{tokenName}}({{lastResult}}); + }); + } diff --git a/src/main/resources/ros2rag.mustache b/src/main/resources/ros2rag.mustache new file mode 100644 index 0000000000000000000000000000000000000000..dd5e73cf554e35cc20da014b2d83f0db1b88521b --- /dev/null +++ b/src/main/resources/ros2rag.mustache @@ -0,0 +1,22 @@ +{{> mqtt}} +aspect ROS2RAG { + {{#ReadDefinitions}} + {{> readDefinition}} + {{/ReadDefinitions}} + + {{#WriteDefinitions}} + {{> writeDefinition}} + {{/WriteDefinitions}} + + {{#MappingDefinitions}} + {{> mappingDefinition}} + {{/MappingDefinitions}} + + {{#DependencyDefinitions}} + {{> dependencyDefinition}} + {{/DependencyDefinitions}} + + {{#TokenComponents}} + {{> tokenComponent}} + {{/TokenComponents}} +} diff --git a/src/main/resources/tokenComponent.mustache b/src/main/resources/tokenComponent.mustache new file mode 100644 index 0000000000000000000000000000000000000000..a3615c4526e5725bdd49121b16a87a011562f0cd --- /dev/null +++ b/src/main/resources/tokenComponent.mustache @@ -0,0 +1,17 @@ + public {{parentTypeName}} {{parentTypeName}}.set{{name}}({{javaType}} value) { + set{{internalName}}(value); + {{#DependencyDefinitions}} + for ({{targetParentTypeName}} target : get{{internalRelationPrefix}}TargetList()) { + {{#targetUpdateDefinition}} + if (target.{{updateMethod}}()) { + target.{{writeMethod}}(); + } + {{/targetUpdateDefinition}} + } + {{/DependencyDefinitions}} + return this; + } + + public {{javaType}} {{parentTypeName}}.get{{name}}() { + return get{{internalName}}(); + } diff --git a/src/main/resources/writeDefinition.mustache b/src/main/resources/writeDefinition.mustache new file mode 100644 index 0000000000000000000000000000000000000000..3d8aab888ed0a39131f5cf00b20cfa2e9ade9048 --- /dev/null +++ b/src/main/resources/writeDefinition.mustache @@ -0,0 +1,24 @@ + private String {{parentTypeName}}.{{writeTopic}} = null; + private byte[] {{parentTypeName}}.{{lastValue}} = null; + + public void {{parentTypeName}}.{{connectMethod}}(String topic, boolean writeCurrentValue) { + {{writeTopic}} = topic; + {{updateMethod}}(); + if (writeCurrentValue) { + {{writeMethod}}(); + } + } + + protected boolean {{parentTypeName}}.{{updateMethod}}() { + {{tokenResetMethod}}(); + {{> mappingApplication}} + {{lastValue}} = {{lastResult}}; + return true; + } + + protected void {{parentTypeName}}.{{writeMethod}}() { + {{#loggingEnabledForWrites}} + System.out.println("[Write] {{tokenName}} = " + get{{tokenName}}() + " -> " + {{writeTopic}}); + {{/loggingEnabledForWrites}} + {{mqttUpdaterAttribute}}().publish({{writeTopic}}, {{lastValue}}); + }