From 1265bfbb3866a3bd1c966be6b7b744f96e3344fd Mon Sep 17 00:00:00 2001 From: rschoene <rene.schoene@tu-dresden.de> Date: Wed, 22 Apr 2020 20:15:29 +0200 Subject: [PATCH] Base: Begin with parser for ros2rag specification. - changed compiler to accept two additional required parameters "inputGrammar" and "inputRos2Rag" - update ros2rag grammar according to latest draft - begin with parser (missing resolving amongst others) - parsing in test kind of fails because of leading comment --- ros2rag.base/build.gradle | 3 +- ros2rag.base/src/main/jastadd/Preamble.parser | 1 + .../jastadd/{RelAst.flex => Ros2Rag.flex} | 10 +++ ros2rag.base/src/main/jastadd/Ros2Rag.parser | 69 +++++++++++++++++++ ros2rag.base/src/main/jastadd/Ros2Rag.relast | 21 +++--- .../src/main/jastadd/backend/Aspect.jadd | 4 +- .../jastadd/ros2rag/compiler/Compiler.java | 64 +++++++++++------ .../jastadd/ros2rag/compiler/SimpleMain.java | 2 +- .../jastadd/ros2rag/tests/RosToRagTest.java | 11 +-- ros2rag.base/src/test/resources/Example.jadd | 47 +++++++++++++ .../src/test/resources/Example.relast | 13 ++++ .../src/test/resources/Example.ros2rag | 20 ++++++ .../src/test/resources/MinimalExample.relast | 14 ---- .../src/main/jastadd/Example.ros2rag | 4 +- 14 files changed, 230 insertions(+), 53 deletions(-) rename ros2rag.base/src/main/jastadd/{RelAst.flex => Ros2Rag.flex} (82%) create mode 100644 ros2rag.base/src/main/jastadd/Ros2Rag.parser create mode 100644 ros2rag.base/src/test/resources/Example.jadd create mode 100644 ros2rag.base/src/test/resources/Example.relast create mode 100644 ros2rag.base/src/test/resources/Example.ros2rag delete mode 100644 ros2rag.base/src/test/resources/MinimalExample.relast diff --git a/ros2rag.base/build.gradle b/ros2rag.base/build.gradle index f9afaaf..64d5ca7 100644 --- a/ros2rag.base/build.gradle +++ b/ros2rag.base/build.gradle @@ -109,12 +109,13 @@ jastadd { } scanner { - include "src/main/jastadd/RelAst.flex" + include "src/main/jastadd/Ros2Rag.flex" } parser { include "src/main/jastadd/Preamble.parser" include "src/main/jastadd/RelAst.parser" + include "src/main/jastadd/Ros2Rag.parser" } } } diff --git a/ros2rag.base/src/main/jastadd/Preamble.parser b/ros2rag.base/src/main/jastadd/Preamble.parser index 386489c..43fa51e 100644 --- a/ros2rag.base/src/main/jastadd/Preamble.parser +++ b/ros2rag.base/src/main/jastadd/Preamble.parser @@ -4,3 +4,4 @@ import org.jastadd.ros2rag.ast.*; :}; %goal goal; +%goal ros2rag; diff --git a/ros2rag.base/src/main/jastadd/RelAst.flex b/ros2rag.base/src/main/jastadd/Ros2Rag.flex similarity index 82% rename from ros2rag.base/src/main/jastadd/RelAst.flex rename to ros2rag.base/src/main/jastadd/Ros2Rag.flex index b0a3fd2..9ef2f3e 100644 --- a/ros2rag.base/src/main/jastadd/RelAst.flex +++ b/ros2rag.base/src/main/jastadd/Ros2Rag.flex @@ -49,6 +49,14 @@ ID = [a-zA-Z$_][a-zA-Z0-9$_]* "abstract" { return sym(Terminals.ABSTRACT); } "rel" { return sym(Terminals.RELATION); } +"read" { return sym(Terminals.READ); } +"write" { return sym(Terminals.WRITE); } +"using" { return sym(Terminals.USING); } +"canDependOn" { return sym(Terminals.CAN_DEPEND_ON); } +"maps" { return sym(Terminals.MAPS); } +"to" { return sym(Terminals.TO); } +"as" { return sym(Terminals.AS); } + ";" { return sym(Terminals.SCOL); } ":" { return sym(Terminals.COL); } "::=" { return sym(Terminals.ASSIGN); } @@ -59,6 +67,8 @@ ID = [a-zA-Z$_][a-zA-Z0-9$_]* ">" { return sym(Terminals.GT); } "[" { return sym(Terminals.LBRACKET); } "]" { return sym(Terminals.RBRACKET); } +"{" { return sym(Terminals.LB_CURLY); } +"}" { return sym(Terminals.RB_CURLY); } "/" { return sym(Terminals.SLASH); } "?" { return sym(Terminals.QUESTION_MARK); } "->" { return sym(Terminals.RIGHT); } diff --git a/ros2rag.base/src/main/jastadd/Ros2Rag.parser b/ros2rag.base/src/main/jastadd/Ros2Rag.parser new file mode 100644 index 0000000..44f2761 --- /dev/null +++ b/ros2rag.base/src/main/jastadd/Ros2Rag.parser @@ -0,0 +1,69 @@ +Ros2Rag ros2rag + = update_definition.d ros2rag.r {: r.getUpdateDefinitionList().insertChild(d, 0); return r; :} + | dependency_definition.d ros2rag.r {: r.getDependencyDefinitionList().insertChild(d, 0); return r; :} + | mapping_definition.d ros2rag.r {: r.getMappingDefinitionList().insertChild(d, 0); return r; :} + | {: return new Ros2Rag(); :} +; + +// read Joint.CurrentPosition using LinkStateToIntPosition ; +// write RobotArm._AppropriateSpeed using CreateSpeedMessage ; +UpdateDefinition update_definition + = READ ID.type_name DOT ID.token_name SCOL + {: + ReadFromMqttDefinition result = new ReadFromMqttDefinition(); + result.setToken(TokenComponent.createRef(type_name + "." + token_name)); + return result; + :} + | READ ID.type_name DOT ID.token_name USING ID.mapping_def SCOL + {: + ReadFromMqttDefinition result = new ReadFromMqttDefinition(); + result.setToken(TokenComponent.createRef(type_name + "." + token_name)); + result.setMapping(MappingDefinition.createRef(mapping_def)); + return result; + :} + | WRITE ID.type_name DOT ID.token_name SCOL + {: + WriteToMqttDefinition result = new WriteToMqttDefinition(); + result.setToken(TokenComponent.createRef(type_name + "." + token_name)); + return result; + :} + | WRITE ID.type_name DOT ID.token_name USING ID.mapping_def SCOL + {: + WriteToMqttDefinition result = new WriteToMqttDefinition(); + result.setToken(TokenComponent.createRef(type_name + "." + token_name)); + result.setMapping(MappingDefinition.createRef(mapping_def)); + return result; + :} +; + +// RobotArm._AppropriateSpeed canDependOn Joint.CurrentPosition as dependency1 ; +DependencyDefinition dependency_definition + = ID.target_type DOT ID.target_token CAN_DEPEND_ON ID.source_type DOT ID.source_token AS ID.id SCOL + {: + DependencyDefinition result = new DependencyDefinition(); + result.setSource(TokenComponent.createRef(source_type + "." + source_token)); + result.setTarget(TokenComponent.createRef(target_type + "." + target_token)); + result.setID(id); + return result; + :} +; + +//LinkStateToIntPosition maps protobuf panda.Linkstate.PandaLinkState x to IntPosition y { +// panda.Linkstate.PandaLinkState.Position p = x.getPos(); +// y = IntPosition.of((int) p.getPositionX(), (int) p.getPositionY(), (int) p.getPositionZ()); +//} +MappingDefinition mapping_definition + = ID.id MAPS java_type_use.from_type ID.from_variable TO java_type_use.to_type ID.to_variable LB_CURLY mapping_def_content.content RB_CURLY + {: + MappingDefinition result = new MappingDefinition(); + result.setID(id); + result.setFrom(from_type); + result.setFromVariable(from_variable); + result.setTo(to_type); + result.setToVariable(to_variable); + result.setContent(content); + return result; + :} +; + +String mapping_def_content = ID ; diff --git a/ros2rag.base/src/main/jastadd/Ros2Rag.relast b/ros2rag.base/src/main/jastadd/Ros2Rag.relast index 75c8df8..6d13bd1 100644 --- a/ros2rag.base/src/main/jastadd/Ros2Rag.relast +++ b/ros2rag.base/src/main/jastadd/Ros2Rag.relast @@ -1,13 +1,18 @@ -Ros2Rag ::= MappingDefinition* SyncDefinition* Program; +Ros2Rag ::= UpdateDefinition* DependencyDefinition* MappingDefinition* Program; -abstract SyncDefinition ::= <AlwaysApply:Boolean> ; +abstract UpdateDefinition ::= <AlwaysApply:Boolean> ; -rel SyncDefinition.Mapping? -> MappingDefinition; +rel UpdateDefinition.Mapping? -> MappingDefinition; -abstract TokenSyncDefinition : SyncDefinition; -rel TokenSyncDefinition.token -> TokenComponent; +abstract TokenUpdateDefinition : UpdateDefinition; +rel TokenUpdateDefinition.Token -> TokenComponent; -ReadFromMqttDefinition : TokenSyncDefinition; -WriteToMqttDefinition : TokenSyncDefinition; +ReadFromMqttDefinition : TokenUpdateDefinition; +WriteToMqttDefinition : TokenUpdateDefinition; -MappingDefinition ::= <ID> From:JavaTypeUse To:JavaTypeUse <Content> ; +// example: RobotArm._AppropriateSpeed canDependOn Joint.CurrentPosition as dependency1 +DependencyDefinition ::= <ID> ; +rel DependencyDefinition.Source -> TokenComponent ; +rel DependencyDefinition.Target -> TokenComponent ; + +MappingDefinition ::= <ID> From:JavaTypeUse <FromVariable> To:JavaTypeUse <ToVariable> <Content> ; diff --git a/ros2rag.base/src/main/jastadd/backend/Aspect.jadd b/ros2rag.base/src/main/jastadd/backend/Aspect.jadd index c63ec13..d403bbc 100644 --- a/ros2rag.base/src/main/jastadd/backend/Aspect.jadd +++ b/ros2rag.base/src/main/jastadd/backend/Aspect.jadd @@ -31,14 +31,14 @@ aspect Aspect { public void Ros2Rag.generateAspect(StringBuilder sb) { sb.append("aspect ROS2RAG {\n"); - for (SyncDefinition def : getSyncDefinitionList()) { + for (UpdateDefinition def : getUpdateDefinitionList()) { def.generateAspect(sb); } sb.append("}\n"); } - abstract void SyncDefinition.generateAspect(StringBuilder sb); + abstract void UpdateDefinition.generateAspect(StringBuilder sb); // @Override // void UpdateDefinition.generateAspect(StringBuilder sb) { // // TODO 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 1ba4fbb..fac820c 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 @@ -10,12 +10,16 @@ import org.jastadd.ros2rag.parser.RelAstParser; import org.jastadd.ros2rag.scanner.RelAstScanner; import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; public class Compiler { private StringOption optionOutputDir; + private StringOption optionInputGrammar; + private StringOption optionInputRos2Rag; private ArrayList<Option<?>> options; private CommandLine commandLine; @@ -31,21 +35,28 @@ public class Compiler { if (optionOutputDir.isSet()) { outputDir = optionOutputDir.getValue(); } else { - System.out.println("No output grammar name is set. Assuming output dir '" + outputDir + "'."); + System.out.println("No output output dir is set. Assuming '" + outputDir + "'."); } printMessage("Running ROS2RAG Preprocessor"); - if (commandLine.getArguments().size() < 1) { - error("specify at least one input file"); + if (!optionInputGrammar.isSet()) { + error("specify an input grammar"); } - List<String> filenames = commandLine.getArguments(); - Program p = parseProgram(filenames); + if (!optionInputRos2Rag.isSet()) { + error("specify the ros2rag definition file"); + } - writeToFile(outputDir + "/Grammar.ast", p.generateAbstractGrammar()); - writeToFile(outputDir + "/ROS2RAG.jadd", p.generateAspect()); + List<String> otherArgs = commandLine.getArguments(); + if (!otherArgs.isEmpty()) { + printMessage("Superfluous arguments will be ignored: " + otherArgs); + } + Ros2Rag ros2Rag = parseProgram(optionInputGrammar.getValue(), optionInputRos2Rag.getValue()); + printMessage("Writing output files"); + writeToFile(outputDir + "/Grammar.relast", ros2Rag.getProgram().generateAbstractGrammar()); + writeToFile(outputDir + "/ROS2RAG.jadd", ros2Rag.generateAspect()); } @@ -75,6 +86,8 @@ public class Compiler { private void addOptions() { optionOutputDir = addOption(new StringOption("outputDir", "target directory for the generated files.")); + optionInputGrammar = addOption(new StringOption("inputGrammar", "base grammar.")); + optionInputRos2Rag = addOption(new StringOption("inputRos2Rag", "ros2rag definition file.")); } private <OptionType extends Option<?>> OptionType addOption(OptionType option) { @@ -82,23 +95,32 @@ public class Compiler { return option; } - private Program parseProgram(List<String> fileNames) { - + private Ros2Rag parseProgram(String inputGrammarFileName, String inputRos2RagFileName) { Program program = new Program(); + Ros2Rag ros2Rag; + + try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputGrammarFileName))) { + RelAstScanner scanner = new RelAstScanner(reader); + RelAstParser parser = new RelAstParser(); + GrammarFile inputGrammar = (GrammarFile) parser.parse(scanner); + program.addGrammarFile(inputGrammar); + } catch (IOException | Parser.Exception e) { + System.err.println(e.getMessage()); + System.exit(1); + } - for (String fileName : fileNames) { - FileReader reader = null; - try { - reader = new FileReader(fileName); - } catch (FileNotFoundException e) { - System.err.println(e.getMessage()); - System.exit(1); - } - - parse(program, reader, fileName); + try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputRos2RagFileName))) { + RelAstScanner scanner = new RelAstScanner(reader); + RelAstParser parser = new RelAstParser(); + ros2Rag = (Ros2Rag) parser.parse(scanner, RelAstParser.AltGoals.ros2rag); + ros2Rag.setProgram(program); + } catch (IOException | Parser.Exception e) { + System.err.println(e.getMessage()); + System.exit(2); + return null; // poor compiler does not know System.exit() } - program.treeResolveAll(); - return program; + + return ros2Rag; } private void parse(Program program, Reader reader, String file) { diff --git a/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/SimpleMain.java b/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/SimpleMain.java index e27a97e..7d528ac 100644 --- a/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/SimpleMain.java +++ b/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/SimpleMain.java @@ -64,7 +64,7 @@ public class SimpleMain { readFromMqttDefinition.setAlwaysApply(false); readFromMqttDefinition.setToken(TokenComponent.createRef("Joint.CurrentPosition")); readFromMqttDefinition.setMapping(mappingDefinition); - model.addSyncDefinition(readFromMqttDefinition); + model.addUpdateDefinition(readFromMqttDefinition); model.treeResolveAll(); diff --git a/ros2rag.base/src/test/java/org/jastadd/ros2rag/tests/RosToRagTest.java b/ros2rag.base/src/test/java/org/jastadd/ros2rag/tests/RosToRagTest.java index 52e1a4b..19ad7e5 100644 --- a/ros2rag.base/src/test/java/org/jastadd/ros2rag/tests/RosToRagTest.java +++ b/ros2rag.base/src/test/java/org/jastadd/ros2rag/tests/RosToRagTest.java @@ -11,10 +11,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class RosToRagTest { - void transform(String inputFile, String outputDir) throws CommandLine.CommandLineException { + void transform(String inputGrammar, String inputRos2Rag, String outputDir) throws CommandLine.CommandLineException { System.out.println(Paths.get(".").toAbsolutePath()); - assertTrue(Paths.get(inputFile).toFile().exists(), "input file does not exist"); + assertTrue(Paths.get(inputGrammar).toFile().exists(), "input grammar does not exist"); File outputDirFile = Paths.get(outputDir).toFile(); if (outputDirFile.exists()) { @@ -28,7 +28,8 @@ public class RosToRagTest { String[] args = { "--outputDir=" + outputDir, - inputFile + "--inputGrammar=" + inputGrammar, + "--inputRos2Rag=" + inputRos2Rag }; new Compiler(args); @@ -36,6 +37,8 @@ public class RosToRagTest { @Test void transformMinimalExample() throws CommandLine.CommandLineException { - transform("src/test/resources/MinimalExample.relast", "src/test/resources/out"); + transform("src/test/resources/Example.relast", + "src/test/resources/Example.ros2rag", + "src/test/resources/out"); } } diff --git a/ros2rag.base/src/test/resources/Example.jadd b/ros2rag.base/src/test/resources/Example.jadd new file mode 100644 index 0000000..1fd2549 --- /dev/null +++ b/ros2rag.base/src/test/resources/Example.jadd @@ -0,0 +1,47 @@ +aspect GrammarTypes { + public class IntPosition { + private final int x, y, z; + + private IntPosition(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public static IntPosition of(int x, int y, int z) { + return new IntPosition(x, y, z); + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getZ() { + return z; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + IntPosition that = (IntPosition) o; + return x == that.x && + y == that.y && + z == that.z; + } + + @Override + public int hashCode() { + return java.util.Objects.hash(x, y, z); + } + + @Override + public String toString() { + return "(" + x + ", " + y + ", " + z + ")"; + } + } +} diff --git a/ros2rag.base/src/test/resources/Example.relast b/ros2rag.base/src/test/resources/Example.relast new file mode 100644 index 0000000..f4b2aa0 --- /dev/null +++ b/ros2rag.base/src/test/resources/Example.relast @@ -0,0 +1,13 @@ +Model ::= RobotArm ZoneModel ; + +ZoneModel ::= <Size:IntPosition> SafetyZone:Zone* ; + +Zone ::= Coordinate* ; + +RobotArm ::= Joint* EndEffector <_AttributeTestSource:int> /<_AppropriateSpeed:double>/ ; // normally this would be: <AttributeTestSource:int> ; + +Joint ::= <Name> <_CurrentPosition:IntPosition> ; // normally this would be: <CurrentPosition:IntPosition> + +EndEffector : Joint; + +Coordinate ::= <Position:IntPosition> ; diff --git a/ros2rag.base/src/test/resources/Example.ros2rag b/ros2rag.base/src/test/resources/Example.ros2rag new file mode 100644 index 0000000..26702ca --- /dev/null +++ b/ros2rag.base/src/test/resources/Example.ros2rag @@ -0,0 +1,20 @@ +/* Version 2020-04-17 */ +// --- update definitions --- +read Joint.CurrentPosition using LinkStateToIntPosition ; +write RobotArm._AppropriateSpeed using CreateSpeedMessage ; + +// --- dependency definitions --- +RobotArm._AppropriateSpeed canDependOn Joint.CurrentPosition as dependency1 ; +RobotArm._AppropriateSpeed canDependOn RobotArm._AttributeTestSource as dependency2 ; + +// --- mapping definitions --- +LinkStateToIntPosition: map protobuf panda.Linkstate.PandaLinkState x to IntPosition y { + panda.Linkstate.PandaLinkState.Position p = x.getPos(); + y = IntPosition.of((int) p.getPositionX(), (int) p.getPositionY(), (int) p.getPositionZ()); +} + +CreateSpeedMessage: map double x to protobuf config.Robotconfig.RobotConfig y { + y = config.Robotconfig.RobotConfig.newBuilder() + .setSpeed(x) + .build(); +} diff --git a/ros2rag.base/src/test/resources/MinimalExample.relast b/ros2rag.base/src/test/resources/MinimalExample.relast deleted file mode 100644 index 7152bd1..0000000 --- a/ros2rag.base/src/test/resources/MinimalExample.relast +++ /dev/null @@ -1,14 +0,0 @@ -Model ::= RobotArm ZoneModel ; - -ZoneModel ::= Size:Position SafetyZone:Zone*; - -Zone ::= Position*; - -RobotArm ::= Joint* EndEffector /<ShouldUseLowSpeed:Boolean>/ ; - -Joint ::= <Name> ; -rel Joint.CurrentPosition -> Position ; - -EndEffector : Joint; - -Position ::= <x:int> <y:int> <z:int> ; diff --git a/ros2rag.example/src/main/jastadd/Example.ros2rag b/ros2rag.example/src/main/jastadd/Example.ros2rag index f6b49f2..e65c535 100644 --- a/ros2rag.example/src/main/jastadd/Example.ros2rag +++ b/ros2rag.example/src/main/jastadd/Example.ros2rag @@ -10,12 +10,12 @@ RobotArm._AppropriateSpeed canDependOn Joint.CurrentPosition as dependency1 ; RobotArm._AppropriateSpeed canDependOn RobotArm._AttributeTestSource as dependency2 ; // --- mapping definitions --- -LinkStateToIntPosition: map protobuf panda.Linkstate.PandaLinkState x to IntPosition y { +LinkStateToIntPosition maps protobuf panda.Linkstate.PandaLinkState x to IntPosition y { panda.Linkstate.PandaLinkState.Position p = x.getPos(); y = IntPosition.of((int) p.getPositionX(), (int) p.getPositionY(), (int) p.getPositionZ()); } -CreateSpeedMessage: map double x to protobuf config.Robotconfig.RobotConfig y { +CreateSpeedMessage maps double x to protobuf config.Robotconfig.RobotConfig y { y = config.Robotconfig.RobotConfig.newBuilder() .setSpeed(x) .build(); -- GitLab