From ceff6575ce2c7f5292c344eafdcc062a1ea4f2d6 Mon Sep 17 00:00:00 2001 From: rschoene <rene.schoene@tu-dresden.de> Date: Mon, 4 May 2020 21:36:01 +0200 Subject: [PATCH] Begin with real generation. - Compiler: replace existing files if needed - Renamed Aspect.jadd to Generation.jadd and fill it with content - Missing: Generation of dependency relation, and change of token names in grammar --- ros2rag.base/src/main/jastadd/Analysis.jrag | 19 ++ ros2rag.base/src/main/jastadd/Navigation.jrag | 26 ++ ros2rag.base/src/main/jastadd/Ros2Rag.parser | 1 - ros2rag.base/src/main/jastadd/Ros2Rag.relast | 2 +- .../src/main/jastadd/backend/Aspect.jadd | 86 ------- .../src/main/jastadd/backend/Generation.jadd | 232 ++++++++++++++++++ .../jastadd/ros2rag/compiler/Compiler.java | 4 +- .../src/test/resources/Example.relast | 2 +- .../src/test/resources/Example.ros2rag | 6 +- 9 files changed, 285 insertions(+), 93 deletions(-) delete mode 100644 ros2rag.base/src/main/jastadd/backend/Aspect.jadd create mode 100644 ros2rag.base/src/main/jastadd/backend/Generation.jadd diff --git a/ros2rag.base/src/main/jastadd/Analysis.jrag b/ros2rag.base/src/main/jastadd/Analysis.jrag index 118dd23..246645f 100644 --- a/ros2rag.base/src/main/jastadd/Analysis.jrag +++ b/ros2rag.base/src/main/jastadd/Analysis.jrag @@ -1,3 +1,22 @@ aspect Analysis { + // --- isPrimitiveType --- + syn boolean TokenComponent.isPrimitiveType() = getJavaTypeUse().isPrimitiveType(); + syn boolean JavaTypeUse.isPrimitiveType() = false; + eq SimpleJavaTypeUse.isPrimitiveType() { + switch(getName()) { + case "short": + case "int": + case "float": + case "double": + case "char": + case "byte": return true; + default: return false; + } + } + + // --- prettyPrint --- + syn String MappingDefinitionType.prettyPrint(); + eq JavaMappingDefinitionType.prettyPrint() = getType().getName(); + eq JavaArrayMappingDefinitionType.prettyPrint() = getType().getName() + "[]"; } diff --git a/ros2rag.base/src/main/jastadd/Navigation.jrag b/ros2rag.base/src/main/jastadd/Navigation.jrag index 4b233c2..b401112 100644 --- a/ros2rag.base/src/main/jastadd/Navigation.jrag +++ b/ros2rag.base/src/main/jastadd/Navigation.jrag @@ -35,6 +35,12 @@ aspect Navigation { // --- containedFile --- inh GrammarFile ASTNode.containedFile(); eq GrammarFile.getChild().containedFile() = this; + eq Program.getChild().containedFile() = null; + eq Ros2Rag.getChild().containedFile() = null; + + // --- isTypeComponent --- + syn boolean Component.isTypeComponent() = false; + eq TypeComponent.isTypeComponent() = true; // --- isTokenComponent --- syn boolean Component.isTokenComponent() = false; @@ -43,4 +49,24 @@ aspect Navigation { // --- asTokenComponent --- syn TokenComponent Component.asTokenComponent() = null; eq TokenComponent.asTokenComponent() = this; + + // --- isWriteToMqttDefinition --- + syn boolean UpdateDefinition.isWriteToMqttDefinition() = false; + eq WriteToMqttDefinition.isWriteToMqttDefinition() = true; + + // --- asWriteToMqttDefinition --- + syn WriteToMqttDefinition UpdateDefinition.asWriteToMqttDefinition() = null; + eq WriteToMqttDefinition.asWriteToMqttDefinition() = this; + + // --- targetUpdateDefinition --- + syn WriteToMqttDefinition DependencyDefinition.targetUpdateDefinition() { + // resolve definition in here, as we do not need resolveMethod in any other place (yet) + for (UpdateDefinition updateDefinition : ros2rag().getUpdateDefinitionList()) { + if (updateDefinition.isWriteToMqttDefinition() && + updateDefinition.asWriteToMqttDefinition().getToken().equals(this.getTarget())) { + return updateDefinition.asWriteToMqttDefinition(); + } + } + return null; + } } diff --git a/ros2rag.base/src/main/jastadd/Ros2Rag.parser b/ros2rag.base/src/main/jastadd/Ros2Rag.parser index 750e0ce..f2ab264 100644 --- a/ros2rag.base/src/main/jastadd/Ros2Rag.parser +++ b/ros2rag.base/src/main/jastadd/Ros2Rag.parser @@ -8,7 +8,6 @@ Ros2Rag ros2rag %embed {: private Iterable<String> makeMappingDefs(ArrayList<?> raw_mapping_defs) { - java.util.Collections.reverse(raw_mapping_defs); return () -> raw_mapping_defs.stream().map(raw -> ((Symbol) raw).value.toString()).iterator(); } :} ; diff --git a/ros2rag.base/src/main/jastadd/Ros2Rag.relast b/ros2rag.base/src/main/jastadd/Ros2Rag.relast index f8465bb..1669503 100644 --- a/ros2rag.base/src/main/jastadd/Ros2Rag.relast +++ b/ros2rag.base/src/main/jastadd/Ros2Rag.relast @@ -1,6 +1,6 @@ Ros2Rag ::= UpdateDefinition* DependencyDefinition* MappingDefinition* Program; -abstract UpdateDefinition ::= <AlwaysApply:Boolean> ; +abstract UpdateDefinition ::= <AlwaysApply:boolean> ; rel UpdateDefinition.Mapping* -> MappingDefinition; diff --git a/ros2rag.base/src/main/jastadd/backend/Aspect.jadd b/ros2rag.base/src/main/jastadd/backend/Aspect.jadd deleted file mode 100644 index 1cf2fd9..0000000 --- a/ros2rag.base/src/main/jastadd/backend/Aspect.jadd +++ /dev/null @@ -1,86 +0,0 @@ -aspect Aspect { - - public static final String ASTNode.aspectIndent = " "; - - public String Program.generateAspect() { - StringBuilder sb = new StringBuilder(); - generateAspect(sb); - return sb.toString(); - } - - @Deprecated - public void Program.generateAspect(StringBuilder sb) { - - sb.append("aspect ROS2RAG {\n"); - - // TODO generate getters and setters for ROS2RAG terminals (and attributes?) - - sb.append("}\n"); - } - - public String Ros2Rag.generateAspect(String rootNodeName) { - StringBuilder sb = new StringBuilder(); - generateMqttAspect(sb); - generateGrammarExtension(sb); - return sb.toString(); - } - - public void Ros2Rag.generateMqttAspect(StringBuilder sb) { - - } - - // from "[always] read Joint.CurrentPosition using PoseToPosition;" generate method connectTo -// Joint j; -// j.getCurrentPosition().connectTo("/robot/joint2/pos"); - - public void Ros2Rag.generateGrammarExtension(StringBuilder sb) { - sb.append("aspect ros2rag.GrammarExtension {\n"); - - for (UpdateDefinition def : getUpdateDefinitionList()) { - def.generateAspect(sb); - } - - sb.append("}\n"); - } - - abstract void UpdateDefinition.generateAspect(StringBuilder sb); -// @Override -// void UpdateDefinition.generateAspect(StringBuilder sb) { -// // TODO -// } - - // will be "addConnectionJoint_CurrentPosition" in example -/* // see discussion in codimd (InstanceLocation), why this won't work here - Position.connectTo(String topic) { - mqttUpdater().addConnectionJoint_CurrentPosition(this, topic); - } - MqttUpdater.addConnectionJoint_CurrentPosition(Position target, String topic) { - // either - topicActionMap.put(topic, new Action(JOINT_CURRENTPOSITION, target)); - // or - topicForJoint_CurrentPosition.put(topic, target); - } - */ - @Override - void ReadFromMqttDefinition.generateAspect(StringBuilder sb) { - sb.append("public void ").append("type").append(".connectTo(String topic) {\n") - .append(aspectIndent).append("mqttUpdater().addConnection") - .append(getToken().containingTypeDecl().getName()) - .append("_") - .append(getToken().getName()) - .append("(this, topic);\n") - .append("}\n"); - } - - @Override - void WriteToMqttDefinition.generateAspect(StringBuilder sb) { - sb.append("public void ").append("type").append(".connectTo(String topic) {\n") - .append(aspectIndent).append("mqttUpdater().addConnection") - .append(getToken().containingTypeDecl().getName()) - .append("_") - .append(getToken().getName()) - .append("(this, topic);\n") - .append("}\n"); - } - -} diff --git a/ros2rag.base/src/main/jastadd/backend/Generation.jadd b/ros2rag.base/src/main/jastadd/backend/Generation.jadd new file mode 100644 index 0000000..3195a70 --- /dev/null +++ b/ros2rag.base/src/main/jastadd/backend/Generation.jadd @@ -0,0 +1,232 @@ +aspect GenerationUtils { + public static final String ASTNode.aspectIndent = " "; + + public String ASTNode.ind(int n) { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < n; i++) { + s.append(aspectIndent); + } + return s.toString(); + } +} + +aspect AspectGeneration { + + // naming convention attributes + syn String TokenUpdateDefinition.connectMethod() = "connect" + getToken().getName(); + syn String WriteToMqttDefinition.writeTopic() = "_topic_" + getToken().getName(); + syn String WriteToMqttDefinition.lastValue() = "_lastValue" + getToken().getName(); + syn String WriteToMqttDefinition.updateMethod() = "_update_" + getToken().getName(); + syn String WriteToMqttDefinition.writeMethod() = "_writeLastValue_" + getToken().getName(); + syn String WriteToMqttDefinition.tokenResetMethod() = "get" + getToken().getName() + "_reset"; + syn String MappingDefinition.methodName() = "_apply_" + getID(); + syn String DependencyDefinition.dependencyMethod() = "add" + + Character.toUpperCase(getID().charAt(0)) + + getID().substring(1); + syn String DependencyDefinition.internalRelationPrefix() = "_internal_" + getID(); + syn String DependencyDefinition.internalTokenName() = "_internal" + getSource().getName(); + + 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"; + + syn String Ros2Rag.mqttSetHostMethod() = "MqttSetHost"; + 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 static final int ") + .append(rootNodeName).append("._MQTT_DEFAULT_PORT = 1883;\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("MqttSetHost(host, _MQTT_DEFAULT_PORT);\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 void ").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.GrammarExtension {\n"); + + for (UpdateDefinition def : getUpdateDefinitionList()) { + def.generateAspect(sb); + } + for (MappingDefinition def : getMappingDefinitionList()) { + def.generateAspect(sb); + } + for (DependencyDefinition def : getDependencyDefinitionList()) { + def.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; + for (MappingDefinition mappingDefinition : getMappingList()) { + String resultVariableName = resultVariablePrefix + mappingDefinition.methodName(); + sb.append(ind(indent)).append(mappingDefinition.getToType().prettyPrint()).append(" ") + .append(resultVariablePrefix).append(mappingDefinition.methodName()) + .append(" = ").append(mappingDefinition.methodName()).append("(") + .append(inputVariableName).append(");\n"); + inputVariableName = resultVariableName; + } + if (!getAlwaysApply()) { + sb.append(ind(indent)).append("if (get").append(getToken().getName()).append("()"); + if (getToken().isPrimitiveType()) { + sb.append(" == ").append(inputVariableName); + } else { + sb.append(" != null ? get").append(getToken().getName()).append("().equals(") + .append(inputVariableName).append(")").append(" : ") + .append(inputVariableName).append(" == null"); + } + sb.append(") { ").append(preemptiveReturnStatement()).append(" }\n"); + } + return inputVariableName; + } + + 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"); + 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"); + + // 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"); + + // write method + sb.append(ind(1)).append("protected void").append(parentTypeName).append(".") + .append(writeMethod()).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"); + } + + 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(") {\n"); + for (String line : getContent().split("\n")) { + if (!line.trim().isEmpty()) { + sb.append(ind(2)).append(line).append("\n"); + } + } + 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"); + + // virtual setter + sb.append(ind(1)).append("public ").append(sourceParentTypeName).append(" ") + .append(sourceParentTypeName).append(".set").append(getSource().getName()).append("("); + getSource().getJavaTypeUse().generateAbstractGrammar(sb); + sb.append(" value) {\n"); + sb.append(ind(2)).append("set").append(internalTokenName()).append("(value);\n"); + sb.append(ind(2)).append("for (").append(targetParentTypeName).append(" target : get") + .append(internalRelationPrefix()).append("TargetList()) {\n"); + sb.append(ind(3)).append("if (target.").append(targetUpdateDefinition().updateMethod()).append("()) {\n"); + sb.append(ind(4)).append("target.").append(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 "); + getSource().getJavaTypeUse().generateAbstractGrammar(sb); + sb.append(" ").append(sourceParentTypeName).append(".get").append(getSource().getName()).append("() {\n"); + sb.append(ind(2)).append("return get").append(internalTokenName()).append(";\n"); + sb.append(ind(1)).append("}\n\n"); + } +} diff --git a/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java b/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java index 006f1d1..f9b6f5f 100644 --- a/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java +++ b/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java @@ -12,6 +12,7 @@ import org.jastadd.ros2rag.scanner.Ros2RagScanner; import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; @@ -59,7 +60,8 @@ public class Compiler { // copy MqttUpdater into outputDir try { Files.copy(Paths.get("src", "main", "jastadd", "MqttUpdater.java_class"), - Paths.get(outputDir, "MqttUpdater.java")); + Paths.get(outputDir, "MqttUpdater.java"), + StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { throw new CompilerException("Could not copy MqttUpdater.java", e); } diff --git a/ros2rag.base/src/test/resources/Example.relast b/ros2rag.base/src/test/resources/Example.relast index aa17ad8..93168fb 100644 --- a/ros2rag.base/src/test/resources/Example.relast +++ b/ros2rag.base/src/test/resources/Example.relast @@ -4,7 +4,7 @@ ZoneModel ::= <Size:IntPosition> SafetyZone:Zone* ; Zone ::= Coordinate* ; -RobotArm ::= Joint* EndEffector <_AttributeTestSource:int> /<_AppropriateSpeed:double>/ ; // normally this would be: <AttributeTestSource:int> ; +RobotArm ::= Joint* EndEffector <AttributeTestSource:int> /<AppropriateSpeed:double>/ ; // normally this would be: <AttributeTestSource:int> ; Joint ::= <Name> <CurrentPosition:IntPosition> ; // normally this would be: <CurrentPosition:IntPosition> diff --git a/ros2rag.base/src/test/resources/Example.ros2rag b/ros2rag.base/src/test/resources/Example.ros2rag index f17a9be..9168da2 100644 --- a/ros2rag.base/src/test/resources/Example.ros2rag +++ b/ros2rag.base/src/test/resources/Example.ros2rag @@ -1,11 +1,11 @@ /* Version 2020-04-17 */ // --- update definitions --- read Joint.CurrentPosition using ParseLinkState, LinkStateToIntPosition ; -write RobotArm._AppropriateSpeed using CreateSpeedMessage, SerializeRobotConfig ; +write RobotArm.AppropriateSpeed using CreateSpeedMessage, SerializeRobotConfig ; // --- dependency definitions --- -RobotArm._AppropriateSpeed canDependOn Joint.CurrentPosition as dependency1 ; -RobotArm._AppropriateSpeed canDependOn RobotArm._AttributeTestSource as dependency2 ; +RobotArm.AppropriateSpeed canDependOn Joint.CurrentPosition as dependency1 ; +RobotArm.AppropriateSpeed canDependOn RobotArm.AttributeTestSource as dependency2 ; // --- mapping definitions --- ParseLinkState maps byte[] bytes to panda.Linkstate.PandaLinkState {: -- GitLab