diff --git a/build.gradle b/build.gradle index 9e0bde27ffc2908022aa1a0148b6dc8acec2be35..dec769678645445543fb08103107e15ec46d2ca4 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,7 @@ buildscript { dependencies { implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8' implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8' + implementation 'com.github.spullara.mustache.java:compiler:0.9.6' // api 'org.jastadd:jastadd:2.3.4' runtime 'org.jastadd:jastadd:2.3.4' api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' @@ -62,6 +63,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", @@ -73,6 +75,7 @@ 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/MustacheNodes.relast") outputs.files file("./src/gen/jastadd/Ros2Rag.ast"), file("src/gen/jastadd/Ros2Rag.jadd"), file("src/gen/jastadd/Ros2RagRefResolver.jadd"), diff --git a/src/main/jastadd/MustacheNodes.relast b/src/main/jastadd/MustacheNodes.relast new file mode 100644 index 0000000000000000000000000000000000000000..30277272d713c062307b3679dff7db75b71b91ef --- /dev/null +++ b/src/main/jastadd/MustacheNodes.relast @@ -0,0 +1,2 @@ +TypeComponentMustache ; +rel TypeComponentMustache.TypeComponent -> TypeComponent ; diff --git a/src/main/jastadd/Navigation.jrag b/src/main/jastadd/Navigation.jrag index f56d99825c376248ce8f9a2dae1388ed3898ea64..e034804ba6f4151c744e7fb495c17f483be7e685 100644 --- a/src/main/jastadd/Navigation.jrag +++ b/src/main/jastadd/Navigation.jrag @@ -3,7 +3,7 @@ aspect Navigation { // --- program --- eq Ros2Rag.getChild().program() = getProgram(); - // --- ros2rag + // --- ros2rag --- inh Ros2Rag ASTNode.ros2rag(); eq Ros2Rag.getChild().ros2rag() = this; 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 e698e405ee6d392d2405b15fe0869432b9b18a59..81e2dabe41761ef3e1b3058adb2809be90f6ac1d 100644 --- a/src/main/jastadd/backend/Generation.jadd +++ b/src/main/jastadd/backend/Generation.jadd @@ -22,10 +22,10 @@ aspect GenerationUtils { } 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 +39,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,54 +46,53 @@ 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(); - } + // naming copy attributes + // --- mqttUpdaterAttribute --- + inh String UpdateDefinition.mqttUpdaterAttribute(); + inh String MappingDefinition.mqttUpdaterAttribute(); + inh String DependencyDefinition.mqttUpdaterAttribute(); + eq Ros2Rag.getChild().mqttUpdaterAttribute() = mqttUpdaterAttribute(); - 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"); + // --- mqttUpdaterField --- + eq Ros2Rag.getChild().mqttUpdaterField() = mqttUpdaterField(); - // 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"); + // --- rootNodeName --- + syn String ASTNode.rootNodeName() = rootNode.getName(); - // 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"); + // mustache specific nodes + syn nta TypeComponentMustache TypeComponent.mustache() { + TypeComponentMustache result = new TypeComponentMustache(); + result.setTypeComponent(this); + return result; + } + syn String TypeComponentMustache.name() = getTypeComponent().getName(); + inh String TypeComponentMustache.mqttUpdaterAttribute(); + inh String TypeComponentMustache.mqttUpdaterField(); - // mqttUpdater - sb.append(ind(1)).append("inh MqttUpdater ASTNode.").append(mqttUpdaterAttribute()).append("();\n"); + // mustache specific attributes + syn java.util.List<TypeComponentMustache> Ros2Rag.rootTypeChildren() { + java.util.List<TypeComponentMustache> result = new java.util.ArrayList<>(); 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"); + if (child.isTypeComponent()){ + result.add(child.asTypeComponent().mustache()); } } - sb.append("}\n\n"); + return result; + } + + public String Ros2Rag.generateAspect(String rootNodeName) { + StringBuilder sb = new StringBuilder(); + rootNode = getProgram().resolveTypeDecl(rootNodeName); + generateMqttAspect(sb); + generateGrammarExtension(sb); + return sb.toString(); + } + + public void Ros2Rag.generateMqttAspect(StringBuilder sb) { + String rootNodeName = rootNode.getName(); + com.github.mustachejava.MustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory(); + com.github.mustachejava.Mustache m = mf.compile("mqtt.mustache"); + m.execute(new java.io.PrintWriter(new org.jastadd.ros2rag.compiler.AppendableWriter(sb)), this); } public void Ros2Rag.generateGrammarExtension(StringBuilder sb) { 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/resources/mqtt.mustache b/src/main/resources/mqtt.mustache new file mode 100644 index 0000000000000000000000000000000000000000..33e3f4ee001d889575c264f617f37d04dc574a97 --- /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}}(); + {{#rootTypeChildren}} + eq {{rootNodeName}}.get{{name}}().{{mqttUpdaterAttribute}}() = {{mqttUpdaterField}}; + {{/rootTypeChildren}} +}