From cfdd9ff32441ba428b25361dfdfad868b2223f3b Mon Sep 17 00:00:00 2001 From: rschoene <rene.schoene@tu-dresden.de> Date: Fri, 29 Nov 2019 19:13:24 +0100 Subject: [PATCH] Split statemachine in task and solution. --- settings.gradle | 3 +- statemachine.drast/build.gradle | 4 +- .../inf/st/statemachine/DrAstRunner.java | 2 +- .../.gitignore | 0 .../build.gradle | 0 .../src/main/jastadd/Analysis.jrag | 0 .../src/main/jastadd/ConnectedComponents.jrag | 0 .../src/main/jastadd/NameAnalysis.jrag | 0 .../src/main/jastadd/Navigation.jrag | 0 .../src/main/jastadd/Printing.jrag | 0 .../src/main/jastadd/SMtoDotG.jrag | 0 .../main/jastadd/StateMachineParser.parser | 0 .../src/main/jastadd/StateMachineScanner.flex | 0 .../src/main/jastadd/Util.jadd | 0 .../src/main/jastadd/Util.jrag | 0 .../tudresden/inf/st/statemachine/Main.java | 0 .../inf/st/statemachine/ParserUtils.java | 0 .../src/main/resources/StateMachine.ecore | 0 .../src/main/resources/log4j2.xml | 0 .../inf/st/statemachine/AnalysisTest.java | 0 .../inf/st/statemachine/ParserTest.java | 0 .../src/test/resources/empty.sm | 0 .../src/test/resources/machine_one.sm | 0 .../src/test/resources/telephone.sm | 0 statemachine.task/.gitignore | 7 + statemachine.task/build.gradle | 143 ++++++++++++++++++ .../src/main/jastadd/Analysis.jrag | 77 ++++++++++ .../src/main/jastadd/ConnectedComponents.jrag | 67 ++++++++ .../src/main/jastadd/NameAnalysis.jrag | 25 +++ .../src/main/jastadd/Navigation.jrag | 47 ++++++ .../src/main/jastadd/Printing.jrag | 12 ++ .../src/main/jastadd/SMtoDotG.jrag | 18 +++ .../main/jastadd/StateMachineParser.parser | 65 ++++++++ .../src/main/jastadd/StateMachineScanner.flex | 46 ++++++ statemachine.task/src/main/jastadd/Util.jadd | 39 +++++ statemachine.task/src/main/jastadd/Util.jrag | 3 + .../tudresden/inf/st/statemachine/Main.java | 96 ++++++++++++ .../inf/st/statemachine/ParserUtils.java | 27 ++++ .../src/main/resources/StateMachine.ecore | 27 ++++ .../src/main/resources/log4j2.xml | 22 +++ .../inf/st/statemachine/AnalysisTest.java | 116 ++++++++++++++ .../inf/st/statemachine/ParserTest.java | 85 +++++++++++ statemachine.task/src/test/resources/empty.sm | 7 + .../src/test/resources/machine_one.sm | 6 + .../src/test/resources/telephone.sm | 25 +++ 45 files changed, 965 insertions(+), 4 deletions(-) rename {statemachine.base => statemachine.solution}/.gitignore (100%) rename {statemachine.base => statemachine.solution}/build.gradle (100%) rename {statemachine.base => statemachine.solution}/src/main/jastadd/Analysis.jrag (100%) rename {statemachine.base => statemachine.solution}/src/main/jastadd/ConnectedComponents.jrag (100%) rename {statemachine.base => statemachine.solution}/src/main/jastadd/NameAnalysis.jrag (100%) rename {statemachine.base => statemachine.solution}/src/main/jastadd/Navigation.jrag (100%) rename {statemachine.base => statemachine.solution}/src/main/jastadd/Printing.jrag (100%) rename {statemachine.base => statemachine.solution}/src/main/jastadd/SMtoDotG.jrag (100%) rename {statemachine.base => statemachine.solution}/src/main/jastadd/StateMachineParser.parser (100%) rename {statemachine.base => statemachine.solution}/src/main/jastadd/StateMachineScanner.flex (100%) rename {statemachine.base => statemachine.solution}/src/main/jastadd/Util.jadd (100%) rename {statemachine.base => statemachine.solution}/src/main/jastadd/Util.jrag (100%) rename {statemachine.base => statemachine.solution}/src/main/java/de/tudresden/inf/st/statemachine/Main.java (100%) rename {statemachine.base => statemachine.solution}/src/main/java/de/tudresden/inf/st/statemachine/ParserUtils.java (100%) rename {statemachine.base => statemachine.solution}/src/main/resources/StateMachine.ecore (100%) rename {statemachine.base => statemachine.solution}/src/main/resources/log4j2.xml (100%) rename {statemachine.base => statemachine.solution}/src/test/java/de/tudresden/inf/st/statemachine/AnalysisTest.java (100%) rename {statemachine.base => statemachine.solution}/src/test/java/de/tudresden/inf/st/statemachine/ParserTest.java (100%) rename {statemachine.base => statemachine.solution}/src/test/resources/empty.sm (100%) rename {statemachine.base => statemachine.solution}/src/test/resources/machine_one.sm (100%) rename {statemachine.base => statemachine.solution}/src/test/resources/telephone.sm (100%) create mode 100644 statemachine.task/.gitignore create mode 100644 statemachine.task/build.gradle create mode 100644 statemachine.task/src/main/jastadd/Analysis.jrag create mode 100644 statemachine.task/src/main/jastadd/ConnectedComponents.jrag create mode 100644 statemachine.task/src/main/jastadd/NameAnalysis.jrag create mode 100644 statemachine.task/src/main/jastadd/Navigation.jrag create mode 100644 statemachine.task/src/main/jastadd/Printing.jrag create mode 100644 statemachine.task/src/main/jastadd/SMtoDotG.jrag create mode 100644 statemachine.task/src/main/jastadd/StateMachineParser.parser create mode 100644 statemachine.task/src/main/jastadd/StateMachineScanner.flex create mode 100644 statemachine.task/src/main/jastadd/Util.jadd create mode 100644 statemachine.task/src/main/jastadd/Util.jrag create mode 100644 statemachine.task/src/main/java/de/tudresden/inf/st/statemachine/Main.java create mode 100644 statemachine.task/src/main/java/de/tudresden/inf/st/statemachine/ParserUtils.java create mode 100644 statemachine.task/src/main/resources/StateMachine.ecore create mode 100644 statemachine.task/src/main/resources/log4j2.xml create mode 100644 statemachine.task/src/test/java/de/tudresden/inf/st/statemachine/AnalysisTest.java create mode 100644 statemachine.task/src/test/java/de/tudresden/inf/st/statemachine/ParserTest.java create mode 100644 statemachine.task/src/test/resources/empty.sm create mode 100644 statemachine.task/src/test/resources/machine_one.sm create mode 100644 statemachine.task/src/test/resources/telephone.sm diff --git a/settings.gradle b/settings.gradle index 03d6f56..70ecfb0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,6 @@ rootProject.name = 'ifm-refactoring-jastadd' -include 'statemachine.base' +include 'statemachine.task' +include 'statemachine.solution' include 'statemachine.drast' include 'ragdoc-view' diff --git a/statemachine.drast/build.gradle b/statemachine.drast/build.gradle index fcf3218..ee6ac3e 100644 --- a/statemachine.drast/build.gradle +++ b/statemachine.drast/build.gradle @@ -10,7 +10,7 @@ sourceCompatibility = 1.8 repositories.mavenCentral() dependencies { - implementation project(':statemachine.base') + implementation project(':statemachine.solution') implementation files('../libs/DrAST-1.2.2.jar') implementation fileTree(dir: "${System.properties['java.home']}", include: '**/jfxrt.jar') } @@ -21,7 +21,7 @@ run { mainClassName = 'de.tudresden.inf.st.statemachine.DrAstRunner' standardInput = System.in } -run.dependsOn ':statemachine.base:jar' +run.dependsOn ':statemachine.solution:jar' run.doFirst { environment 'JAVA_TOOL_OPTIONS', '-Dlog4j2.disableJmx=true' } task jarDrAst(type: Jar, dependsOn: ':statemachine.base:jar') { diff --git a/statemachine.drast/src/main/java/de/tudresden/inf/st/statemachine/DrAstRunner.java b/statemachine.drast/src/main/java/de/tudresden/inf/st/statemachine/DrAstRunner.java index fef307c..ecab210 100644 --- a/statemachine.drast/src/main/java/de/tudresden/inf/st/statemachine/DrAstRunner.java +++ b/statemachine.drast/src/main/java/de/tudresden/inf/st/statemachine/DrAstRunner.java @@ -37,7 +37,7 @@ public class DrAstRunner extends DrASTGUI { private static void openView() { guiHasBeenCreated = true; - DrASTSettings.put(DrASTSettings.PREV_JAR, "../statemachine.base/build/libs/statemachine.base-0.1.jar"); + DrASTSettings.put(DrASTSettings.PREV_JAR, "../statemachine.solution/build/libs/statemachine.solution-0.1.jar"); DrASTSettings.put(DrASTSettings.PREV_TAIL_ARGS, "-Dlog4j2.disable.jmx=true"); launch(); con.onApplicationClose(); diff --git a/statemachine.base/.gitignore b/statemachine.solution/.gitignore similarity index 100% rename from statemachine.base/.gitignore rename to statemachine.solution/.gitignore diff --git a/statemachine.base/build.gradle b/statemachine.solution/build.gradle similarity index 100% rename from statemachine.base/build.gradle rename to statemachine.solution/build.gradle diff --git a/statemachine.base/src/main/jastadd/Analysis.jrag b/statemachine.solution/src/main/jastadd/Analysis.jrag similarity index 100% rename from statemachine.base/src/main/jastadd/Analysis.jrag rename to statemachine.solution/src/main/jastadd/Analysis.jrag diff --git a/statemachine.base/src/main/jastadd/ConnectedComponents.jrag b/statemachine.solution/src/main/jastadd/ConnectedComponents.jrag similarity index 100% rename from statemachine.base/src/main/jastadd/ConnectedComponents.jrag rename to statemachine.solution/src/main/jastadd/ConnectedComponents.jrag diff --git a/statemachine.base/src/main/jastadd/NameAnalysis.jrag b/statemachine.solution/src/main/jastadd/NameAnalysis.jrag similarity index 100% rename from statemachine.base/src/main/jastadd/NameAnalysis.jrag rename to statemachine.solution/src/main/jastadd/NameAnalysis.jrag diff --git a/statemachine.base/src/main/jastadd/Navigation.jrag b/statemachine.solution/src/main/jastadd/Navigation.jrag similarity index 100% rename from statemachine.base/src/main/jastadd/Navigation.jrag rename to statemachine.solution/src/main/jastadd/Navigation.jrag diff --git a/statemachine.base/src/main/jastadd/Printing.jrag b/statemachine.solution/src/main/jastadd/Printing.jrag similarity index 100% rename from statemachine.base/src/main/jastadd/Printing.jrag rename to statemachine.solution/src/main/jastadd/Printing.jrag diff --git a/statemachine.base/src/main/jastadd/SMtoDotG.jrag b/statemachine.solution/src/main/jastadd/SMtoDotG.jrag similarity index 100% rename from statemachine.base/src/main/jastadd/SMtoDotG.jrag rename to statemachine.solution/src/main/jastadd/SMtoDotG.jrag diff --git a/statemachine.base/src/main/jastadd/StateMachineParser.parser b/statemachine.solution/src/main/jastadd/StateMachineParser.parser similarity index 100% rename from statemachine.base/src/main/jastadd/StateMachineParser.parser rename to statemachine.solution/src/main/jastadd/StateMachineParser.parser diff --git a/statemachine.base/src/main/jastadd/StateMachineScanner.flex b/statemachine.solution/src/main/jastadd/StateMachineScanner.flex similarity index 100% rename from statemachine.base/src/main/jastadd/StateMachineScanner.flex rename to statemachine.solution/src/main/jastadd/StateMachineScanner.flex diff --git a/statemachine.base/src/main/jastadd/Util.jadd b/statemachine.solution/src/main/jastadd/Util.jadd similarity index 100% rename from statemachine.base/src/main/jastadd/Util.jadd rename to statemachine.solution/src/main/jastadd/Util.jadd diff --git a/statemachine.base/src/main/jastadd/Util.jrag b/statemachine.solution/src/main/jastadd/Util.jrag similarity index 100% rename from statemachine.base/src/main/jastadd/Util.jrag rename to statemachine.solution/src/main/jastadd/Util.jrag diff --git a/statemachine.base/src/main/java/de/tudresden/inf/st/statemachine/Main.java b/statemachine.solution/src/main/java/de/tudresden/inf/st/statemachine/Main.java similarity index 100% rename from statemachine.base/src/main/java/de/tudresden/inf/st/statemachine/Main.java rename to statemachine.solution/src/main/java/de/tudresden/inf/st/statemachine/Main.java diff --git a/statemachine.base/src/main/java/de/tudresden/inf/st/statemachine/ParserUtils.java b/statemachine.solution/src/main/java/de/tudresden/inf/st/statemachine/ParserUtils.java similarity index 100% rename from statemachine.base/src/main/java/de/tudresden/inf/st/statemachine/ParserUtils.java rename to statemachine.solution/src/main/java/de/tudresden/inf/st/statemachine/ParserUtils.java diff --git a/statemachine.base/src/main/resources/StateMachine.ecore b/statemachine.solution/src/main/resources/StateMachine.ecore similarity index 100% rename from statemachine.base/src/main/resources/StateMachine.ecore rename to statemachine.solution/src/main/resources/StateMachine.ecore diff --git a/statemachine.base/src/main/resources/log4j2.xml b/statemachine.solution/src/main/resources/log4j2.xml similarity index 100% rename from statemachine.base/src/main/resources/log4j2.xml rename to statemachine.solution/src/main/resources/log4j2.xml diff --git a/statemachine.base/src/test/java/de/tudresden/inf/st/statemachine/AnalysisTest.java b/statemachine.solution/src/test/java/de/tudresden/inf/st/statemachine/AnalysisTest.java similarity index 100% rename from statemachine.base/src/test/java/de/tudresden/inf/st/statemachine/AnalysisTest.java rename to statemachine.solution/src/test/java/de/tudresden/inf/st/statemachine/AnalysisTest.java diff --git a/statemachine.base/src/test/java/de/tudresden/inf/st/statemachine/ParserTest.java b/statemachine.solution/src/test/java/de/tudresden/inf/st/statemachine/ParserTest.java similarity index 100% rename from statemachine.base/src/test/java/de/tudresden/inf/st/statemachine/ParserTest.java rename to statemachine.solution/src/test/java/de/tudresden/inf/st/statemachine/ParserTest.java diff --git a/statemachine.base/src/test/resources/empty.sm b/statemachine.solution/src/test/resources/empty.sm similarity index 100% rename from statemachine.base/src/test/resources/empty.sm rename to statemachine.solution/src/test/resources/empty.sm diff --git a/statemachine.base/src/test/resources/machine_one.sm b/statemachine.solution/src/test/resources/machine_one.sm similarity index 100% rename from statemachine.base/src/test/resources/machine_one.sm rename to statemachine.solution/src/test/resources/machine_one.sm diff --git a/statemachine.base/src/test/resources/telephone.sm b/statemachine.solution/src/test/resources/telephone.sm similarity index 100% rename from statemachine.base/src/test/resources/telephone.sm rename to statemachine.solution/src/test/resources/telephone.sm diff --git a/statemachine.task/.gitignore b/statemachine.task/.gitignore new file mode 100644 index 0000000..19ad8f8 --- /dev/null +++ b/statemachine.task/.gitignore @@ -0,0 +1,7 @@ +build/ +src/gen/ +src/gen-res/ +.test-tmp +/bin/ +logs/ +/doc/ diff --git a/statemachine.task/build.gradle b/statemachine.task/build.gradle new file mode 100644 index 0000000..fc5b063 --- /dev/null +++ b/statemachine.task/build.gradle @@ -0,0 +1,143 @@ +// General configuration (plugins, settings, dependencies) +group 'de.tudresden.inf.st' +version '0.1' + +apply plugin: 'java' +apply plugin: 'jastadd' +apply plugin: 'application' +apply plugin: "idea" + +sourceCompatibility = 1.8 + +repositories.mavenCentral() + +buildscript { + repositories.mavenLocal() + repositories.mavenCentral() + dependencies { + classpath group: 'org.jastadd', name: 'jastaddgradle', version: '1.13.3' + } +} + +idea.module.generatedSourceDirs += file('src/gen/java') + +configurations { + ragdoc +} + +sourceSets.main.java.srcDir "src/gen/java" +jar.manifest.attributes('Main-Class': 'de.tudresden.inf.st.statemachine.Main') + +dependencies { + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "${jackson_version}" + implementation group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' + + jastadd2 "org.jastadd:jastadd:2.3.4" + ragdoc files('../libs/rd-builder.jar') +} + +// Default run configuration +run { + mainClassName = 'de.tudresden.inf.st.statemachine.Main' + standardInput = System.in +} + +// Generated files +def ecoreFile = "./src/main/resources/StateMachine.ecore" +def relastFile = "./src/gen/jastadd/StateMachine.relast" + +// First phase: Ecore -> RelAst +task ecoreToRelast(type: JavaExec) { + group = 'Build' + main = "-jar" + + + doFirst { + delete "src/gen/jastadd" + mkdir "src/gen/jastadd" + } + + args "../libs/ecore2relast-0.1.jar", ecoreFile, relastFile + + inputs.files file(ecoreFile) + outputs.files file(relastFile) +} + +// Second phase: RelAst -> JastAdd +task relastToJastAdd(type: JavaExec) { + group = 'Build' + main = "-jar" + + args "../libs/relast.jar", + "--grammarName=./src/gen/jastadd/StateMachine", + "--useJastAddNames", + "--listClass=ArrayList", + "--jastAddList=JastAddList", + "--resolverHelper", + "--file", + relastFile + + inputs.files relastFile + outputs.files file("./src/gen/jastadd/StateMachine.ast"), file("./src/gen/jastadd/StateMachine.jadd") +} + +// Third phase: JastAdd -> Java (using JastAdd Gradle plugin) +jastadd { + configureModuleBuild() + modules { + module("statemachine") { + + java { + basedir "src/" + include "main/**/*.java" + include "gen/**/*.java" + } + + jastadd { + basedir "src/" + include "main/jastadd/**/*.ast" + include "main/jastadd/**/*.jadd" + include "main/jastadd/**/*.jrag" + include "gen/jastadd/**/*.ast" + include "gen/jastadd/**/*.jadd" + include "gen/jastadd/**/*.jrag" + } + + scanner { + include "src/main/jastadd/StateMachineScanner.flex" + } + + parser { + include "src/main/jastadd/StateMachineParser.parser" + } + } + } + + cleanGen.doFirst { + delete "src/gen/java/de" + delete "src/gen-res/BuildInfo.properties" + } + + module = "statemachine" + + astPackage = 'de.tudresden.inf.st.statemachine.jastadd.model' + + parser.name = 'StateMachineParser' + + genDir = 'src/gen/java' + + buildInfoDir = 'src/gen-res' + + scanner.genDir = "src/gen/java/de/tudresden/inf/st/statemachine/jastadd/scanner" + parser.genDir = "src/gen/java/de/tudresden/inf/st/statemachine/jastadd/parser" + +// default options are: '--rewrite=cnta', '--safeLazy', '--visitCheck=false', '--cacheCycle=false' + extraJastAddOptions = ['--List=JastAddList', '--incremental=param'] +} + +// Workflow configuration for phases +relastToJastAdd.dependsOn ecoreToRelast +generateAst.dependsOn relastToJastAdd + +//// always run jastadd +//jastadd.outputs.upToDateWhen {false} diff --git a/statemachine.task/src/main/jastadd/Analysis.jrag b/statemachine.task/src/main/jastadd/Analysis.jrag new file mode 100644 index 0000000..6bbcf6a --- /dev/null +++ b/statemachine.task/src/main/jastadd/Analysis.jrag @@ -0,0 +1,77 @@ +aspect Analysis { + syn Set<State> State.reachable() circular [new HashSet<State>()] { + Set<State> result = new HashSet<>(); + result.addAll(successors()); + for (State s : successors()) { + result.addAll(s.reachable()); + } + return result; + } + + public void StateMachine.printSomeAnalysis() { + Set<Set<State>> sccs = this.SCC(); + System.out.print("SCCs found: "); + for (Set<State> scc : sccs) { + System.out.print(scc + " "); + } + System.out.println(); + + for (State s : this.states()) { + System.out.println(s + ": successors() = " + s.successors() + ", reachable() = " + s.reachable()); + } + + for (State finalState : this.getFinalList()) { + System.out.println("initial state "+ this.getInitial() + " to " + finalState + " in " + + this.getInitial().minDistTo(finalState) + " step(s)"); + } + } + + syn int State.minDistTo(State other) circular [-1] { + if (this == other) { + return 0; + } + int result = -1; + for (Transition t : this.getOutgoingList()) { + int dist = t.getTo().minDistTo(other); + int delta = t.getLabel().isEmpty() ? 0 : 1; + if (dist != -1 && (result == -1 || result > dist + delta)) { + result = dist + delta; + } + } + return result; + } + + syn boolean Transition.isEpsilon() = getLabel().isEmpty(); + + coll Set<Transition> StateMachine.epsilonTransitions() [new HashSet<>()]; + Transition contributes this when isEpsilon() to StateMachine.epsilonTransitions(); + + public void StateMachine.removeEpsilonTransition(Transition t) { + if (t.isEpsilon()) { + State oldFrom = t.getFrom(); + State oldTo = t.getTo(); + if (oldFrom != oldTo) { + State combined = new State(); + combined.setLabel(oldFrom.getLabel() + "+" + oldTo.getLabel()); + if (oldFrom.isInitial() || oldTo.isInitial()) { + this.setInitial(combined); + } + if (oldFrom.isFinal() || oldTo.isFinal()) { + this.addFinal(combined); + } + new ArrayList<>(oldFrom.getIncomingList()).forEach(trans -> trans.setTo(combined)); + new ArrayList<>(oldFrom.getOutgoingList()).forEach(trans -> trans.setFrom(combined)); + new ArrayList<>(oldTo.getIncomingList()).forEach(trans -> trans.setTo(combined)); + new ArrayList<>(oldTo.getOutgoingList()).forEach(trans -> trans.setFrom(combined)); + this.removeFinal(oldFrom); + this.removeFinal(oldTo); + oldFrom.removeSelf(); + oldTo.removeSelf(); + this.addElement(combined); + } + t.removeSelf(); + } else { + System.err.println("Won't remove non-epsilon transition " + t.getLabel()); + } + } +} diff --git a/statemachine.task/src/main/jastadd/ConnectedComponents.jrag b/statemachine.task/src/main/jastadd/ConnectedComponents.jrag new file mode 100644 index 0000000..406e5f5 --- /dev/null +++ b/statemachine.task/src/main/jastadd/ConnectedComponents.jrag @@ -0,0 +1,67 @@ +aspect ConnectedComponents { +// syn Set<State> State.successors() circular [new HashSet<>()] { +// Set<State> result = new HashSet<>(); +// for (Transition t : getOutgoingList()) { +// State s = t.getTo(); +// result.add(s); +// result.addAll(s.successors()); +// } +// return result; +// } + +// syn Set<State> State.predecessors() circular [new HashSet<>()] { +// Set<State> result = new HashSet<>(); +// for (Transition t : getIncomingList()) { +// State s = t.getFrom(); +// result.add(s); +// result.addAll(s.predecessors()); +// } +// return result; +// } + +// syn boolean State.hasCycle() = successors().contains(this); + +// syn Set<State> State.SCC() { +// Set<State> result = new HashSet<>(successors()); +// result.retainAll(predecessors()); +// return result; +// } + +// coll HashSet<Set<State>> StateMachine.SCC() with add root StateMachine; +// State contributes SCC() when SCC().size() > 0 to StateMachine.SCC(); + + /** + * Kosaraju's algorithm + */ + syn Set<Set<State>> StateMachine.SCC() { + Map<State, Set> visited = new HashMap<>(); + LinkedList<State> locked = new LinkedList<>(); + + for (State n : states()) + if (!visited.containsKey(n)) + n.visit(visited, locked); // forward search + + for (State n : locked) + if (visited.get(n) == null) + n.assign(visited, new HashSet()); // backward search + + return new HashSet(visited.values()); + } + + void State.visit(Map<State, Set> visited, LinkedList<State> locked) { + visited.put(this, null); + for (Transition t : getOutgoingList()) + if (!visited.containsKey(t.getTo())) + t.getTo().visit(visited, locked); + locked.addFirst(this); + } + + void State.assign(Map<State, Set> visited, Set root) { + root.add(this); + visited.put(this, root); + for (Transition t : getIncomingList()) + if (visited.get(t.getFrom()) == null) + t.getFrom().assign(visited, root); + } + +} diff --git a/statemachine.task/src/main/jastadd/NameAnalysis.jrag b/statemachine.task/src/main/jastadd/NameAnalysis.jrag new file mode 100644 index 0000000..a381d9c --- /dev/null +++ b/statemachine.task/src/main/jastadd/NameAnalysis.jrag @@ -0,0 +1,25 @@ +aspect NameAnalysis { + syn State StateMachine.resolveState(String id) { + for (State s : states()) { + if (s.getLabel().equals(id)) { + return s; + } + } + return null; + } + + syn Transition StateMachine.resolveTransition(String id) { + for (Transition t : transitions()) { + if (t.getLabel().equals(id)) { + return t; + } + } + return null; + } + + eq StateMachine.globallyResolveStateByToken(String id) = resolveState(id); + eq Element.globallyResolveStateByToken(String id) = containingStateMachine().resolveState(id); + + eq StateMachine.globallyResolveTransitionByToken(String id) = resolveTransition(id); + eq Element.globallyResolveTransitionByToken(String id) = containingStateMachine().resolveTransition(id); +} diff --git a/statemachine.task/src/main/jastadd/Navigation.jrag b/statemachine.task/src/main/jastadd/Navigation.jrag new file mode 100644 index 0000000..f06ecb8 --- /dev/null +++ b/statemachine.task/src/main/jastadd/Navigation.jrag @@ -0,0 +1,47 @@ +aspect Navigation { + + /** Check, whether an this element is a state */ + syn boolean Element.isState() = false; + eq State.isState() = true; + + /** View this element as a state */ + syn State Element.asState() = null; + eq State.asState() = this; + + /** Check, whether an this element is a transition */ + syn boolean Element.isTransition() = false; + eq Transition.isTransition() = true; + + /** View this element as a transition */ + syn Transition Element.asTransition() = null; + eq Transition.asTransition() = this; + + /** Get all states */ + syn List<State> StateMachine.states() { + List<State> states = new ArrayList<>(); + for (Element element: getElementList()) { + if (element.isState()) { + states.add(element.asState()); + } + } + return states; + } + + syn List<Transition> StateMachine.transitions() { + List<Transition> transitions = new ArrayList<>(); + for (Element element: getElementList()) { + if (element.isTransition()) { + transitions.add(element.asTransition()); + } + } + return transitions; + } + + inh StateMachine Element.containingStateMachine(); + eq StateMachine.getElement().containingStateMachine() = this; + + syn boolean State.isInitial() = containingStateMachine().getInitial().equals(this); + syn boolean State.isFinal() = containingStateMachine().getFinalList().contains(this); + + syn Collection<State> State.successors() = getOutgoingList().stream().map(Transition::getTo).collect(Collectors.toList()); +} diff --git a/statemachine.task/src/main/jastadd/Printing.jrag b/statemachine.task/src/main/jastadd/Printing.jrag new file mode 100644 index 0000000..1ac2e38 --- /dev/null +++ b/statemachine.task/src/main/jastadd/Printing.jrag @@ -0,0 +1,12 @@ +aspect Printing { + syn String StateMachine.prettyPrint() { + StringBuilder sb = new StringBuilder(); + states().forEach(s -> sb.append(s.prettyPrint())); + transitions().forEach(t -> sb.append(t.prettyPrint())); + return sb.toString(); + } + + syn String Element.prettyPrint(); + eq State.prettyPrint() = (isInitial() ? "initial " : "") + (isFinal() ? "final " : "") + "state " + getLabel() + ";\n"; + eq Transition.prettyPrint() = "trans " + getFrom().getLabel() + " -> " + getTo().getLabel() + (isEpsilon() ? "" : " : " + getLabel()) + ";\n"; +} diff --git a/statemachine.task/src/main/jastadd/SMtoDotG.jrag b/statemachine.task/src/main/jastadd/SMtoDotG.jrag new file mode 100644 index 0000000..18155d5 --- /dev/null +++ b/statemachine.task/src/main/jastadd/SMtoDotG.jrag @@ -0,0 +1,18 @@ +aspect StateMachinetoDotG { + syn String StateMachine.toDot() { + StringBuilder b = new StringBuilder(); + b.append("strict digraph cycles {\n"); + + for (State from : states()) { + b.append(" ").append(from.getLabel()).append("[label=\"").append(from.getLabel()).append("\"];\n"); + + for (Transition out : from.getOutgoingList()) { + b.append(" ").append(from.getLabel()).append(" -> ").append(out.getTo().getLabel()) + .append("[label=\"").append(out.getLabel()).append("\"];\n"); + } + } + + b.append("}\n"); + return b.toString(); + } +} diff --git a/statemachine.task/src/main/jastadd/StateMachineParser.parser b/statemachine.task/src/main/jastadd/StateMachineParser.parser new file mode 100644 index 0000000..e31987a --- /dev/null +++ b/statemachine.task/src/main/jastadd/StateMachineParser.parser @@ -0,0 +1,65 @@ +%header {: +// this code is inlined before the generated parser +package de.tudresden.inf.st.statemachine.jastadd.parser; +import de.tudresden.inf.st.statemachine.jastadd.model.*; +import java.util.*; +:}; +// Documentation links: http://beaver.sourceforge.net/spec.html and https://bitbucket.org/jastadd/jastaddparser + +%embed {: + // this code is inlined in the generated parser class + State initial; + List<State> finals = new ArrayList<>(); +:}; + +%goal goal; + +// Productions with semantic actions building the JastAdd AST + +statemachine goal = + element_list + {: + StateMachine result = new StateMachine(element_list); + result.setInitial(initial); + finals.forEach( result::addFinal ); + result.treeResolveAll(); + return result; + :} + ; + +JastAddList element_list = + element {: return new JastAddList().add(element); :} + | element_list element {: return element_list.add(element); :} + ; + +Element element = + INITIAL state_body.sb SEMI {: initial = sb; return sb; :} + | FINAL state_body.sb SEMI {: finals.add(sb); return sb; :} + | INITIAL FINAL state_body.sb SEMI {: initial = sb; finals.add(sb); return sb; :} + | state_body.sb SEMI {: return sb; :} + | TRANS NAME.from ARROW NAME.to COLON NAME.label SEMI + {: + Transition result = new Transition(); + result.setLabel(label); + result.setFrom(State.createRef(from)); + result.setTo(State.createRef(to)); + return result; + :} + | TRANS NAME.from ARROW NAME.to SEMI + {: + Transition result = new Transition(); + result.setLabel(""); // epsilon transition + result.setFrom(State.createRef(from)); + result.setTo(State.createRef(to)); + return result; + :} + ; + +State state_body = + STATE NAME.name + {: + State result = new State(); + result.setLabel(name); + return result; + :} + ; diff --git a/statemachine.task/src/main/jastadd/StateMachineScanner.flex b/statemachine.task/src/main/jastadd/StateMachineScanner.flex new file mode 100644 index 0000000..547fb1a --- /dev/null +++ b/statemachine.task/src/main/jastadd/StateMachineScanner.flex @@ -0,0 +1,46 @@ +package de.tudresden.inf.st.statemachine.jastadd.scanner; + +import de.tudresden.inf.st.statemachine.jastadd.parser.StateMachineParser.Terminals; // The terminals are implicitly defined in the parser +%% +// Documentation links: https://www.jflex.de/manual.html and http://beaver.sourceforge.net/scanners.html + +// define the signature for the generated scanner +%public +%final +%class StateMachineScanner +%extends beaver.Scanner + +// the interface between the scanner and the parser is the nextToken() method +%type beaver.Symbol +%function nextToken +%yylexthrow beaver.Scanner.Exception + +// store line and column information in the tokens +%line +%column + +// this code will be inlined in the body of the generated scanner class +%{ + private beaver.Symbol sym(short id) { + return new beaver.Symbol(id, yyline + 1, yycolumn + 1, yylength(), yytext()); + } +%} + +WhiteSpace = [ ] | \t | \f | \n | \r | \r\n +Identifier = [:jletter:][:jletterdigit:]* + +%% + +// discard whitespace information +{WhiteSpace} { } + +// token definitions +"initial" { return sym(Terminals.INITIAL); } +"final" { return sym(Terminals.FINAL); } +"state" { return sym(Terminals.STATE); } +"trans" { return sym(Terminals.TRANS); } +{Identifier} { return sym(Terminals.NAME); } +";" { return sym(Terminals.SEMI); } +":" { return sym(Terminals.COLON); } +"->" { return sym(Terminals.ARROW); } +<<EOF>> { return sym(Terminals.EOF); } diff --git a/statemachine.task/src/main/jastadd/Util.jadd b/statemachine.task/src/main/jastadd/Util.jadd new file mode 100644 index 0000000..517b6a5 --- /dev/null +++ b/statemachine.task/src/main/jastadd/Util.jadd @@ -0,0 +1,39 @@ +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import java.util.*; +import java.util.stream.Collectors; + +aspect Logging { + static Logger ASTNode.logger = LogManager.getLogger(ASTNode.class); +} + +aspect JastAddAPIExtension { + + /** + * removes the object from the AST, i.e. removes the reference from its parent to the object + * + * Please note that any intrinsic non-containment relations to the object are not removed. + * @return true, if the object had a parent. + */ + public boolean ASTNode.removeSelf() { + if (getParent() == null) { + return false; + } else { + for (int childIndex = 0; childIndex < getParent().numChildren(); childIndex++) { + if (getParent().getChild(childIndex) == this) { + getParent().removeChild(childIndex); + return true; + } + } + } + throw new RuntimeException("unable to remove child, because it was not contained in its parent!"); + } + + public String Element.customID() { + return getLabel(); + } + + public String Element.toString() { + return getLabel(); + } +} diff --git a/statemachine.task/src/main/jastadd/Util.jrag b/statemachine.task/src/main/jastadd/Util.jrag new file mode 100644 index 0000000..37592dd --- /dev/null +++ b/statemachine.task/src/main/jastadd/Util.jrag @@ -0,0 +1,3 @@ +aspect Util { + +} diff --git a/statemachine.task/src/main/java/de/tudresden/inf/st/statemachine/Main.java b/statemachine.task/src/main/java/de/tudresden/inf/st/statemachine/Main.java new file mode 100644 index 0000000..d38b8ac --- /dev/null +++ b/statemachine.task/src/main/java/de/tudresden/inf/st/statemachine/Main.java @@ -0,0 +1,96 @@ +package de.tudresden.inf.st.statemachine; + +import beaver.Parser; +import de.tudresden.inf.st.statemachine.jastadd.model.State; +import de.tudresden.inf.st.statemachine.jastadd.model.StateMachine; +import de.tudresden.inf.st.statemachine.jastadd.model.Transition; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Set; + +public class Main { + + @SuppressWarnings("WeakerAccess") + public static Object DrAST_root_node; + + public static void main(String[] args) throws IOException, Parser.Exception { + StateMachine stateMachine; + if (args.length == 0) { + stateMachine = createExample(); + } else { + // load the file given as first argument + stateMachine = ParserUtils.load(Paths.get(args[0])); + } + printHeading("Initial statemachine"); + System.out.println(stateMachine.prettyPrint()); + + stateMachine.printSomeAnalysis(); + + Set<Transition> epsilons = stateMachine.epsilonTransitions(); + + for (Transition eps : epsilons) { + printHeading("Removing epsilon transition " + eps.prettyPrint().trim()); + System.out.println("Minimal distances before:"); + initialToFinalDistances(stateMachine); + stateMachine.removeEpsilonTransition(eps); + System.out.println("Minimal distances after:"); + initialToFinalDistances(stateMachine); + printHeading("StateMachine after"); + System.out.println(stateMachine.prettyPrint()); + } + printHeading("DotGraph"); + System.out.println(stateMachine.toDot()); + DrAST_root_node = stateMachine; + } + + private static void printHeading(String s) { + System.out.println(); + System.out.println("========================================"); + System.out.println("== " + s); + System.out.println("========================================"); + } + + private static StateMachine createExample() { + // manual construction of a simple statemachine + // (S) -- e --> (B) -- 1 --> (E) + // ^ | + // \ / + // `---- 2 ----* + StateMachine stateMachine = new StateMachine(); + State start = new State(); + start.setLabel("S"); + State stateB = new State(); + stateB.setLabel("B"); + State end = new State(); + end.setLabel("E"); + Transition eps = new Transition(); + Transition t2 = new Transition(); + t2.setLabel("2"); + Transition t1 = new Transition(); + t1.setLabel("1"); + eps.setFrom(start); + eps.setTo(stateB); + t2.setFrom(stateB); + t2.setTo(start); + t1.setFrom(stateB); + t1.setTo(end); + stateMachine.addElement(start); + stateMachine.addElement(stateB); + stateMachine.addElement(end); + stateMachine.addElement(eps); + stateMachine.addElement(t2); + stateMachine.addElement(t1); + stateMachine.setInitial(start); + stateMachine.addFinal(end); + return stateMachine; + } + + private static void initialToFinalDistances(StateMachine stateMachine) { + for (State finalState : stateMachine.getFinalList()) { + System.out.println("initial state "+ stateMachine.getInitial() + " to " + finalState + " in " + + stateMachine.getInitial().minDistTo(finalState) + " step(s)"); + } + } + +} diff --git a/statemachine.task/src/main/java/de/tudresden/inf/st/statemachine/ParserUtils.java b/statemachine.task/src/main/java/de/tudresden/inf/st/statemachine/ParserUtils.java new file mode 100644 index 0000000..ef4e501 --- /dev/null +++ b/statemachine.task/src/main/java/de/tudresden/inf/st/statemachine/ParserUtils.java @@ -0,0 +1,27 @@ +package de.tudresden.inf.st.statemachine; + +import beaver.Parser; +import de.tudresden.inf.st.statemachine.jastadd.model.StateMachine; +import de.tudresden.inf.st.statemachine.jastadd.parser.StateMachineParser; +import de.tudresden.inf.st.statemachine.jastadd.scanner.StateMachineScanner; + +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * Utility methods for loading statemachine files. + * + * @author rschoene - Initial contribution + */ +public class ParserUtils { + static StateMachine load(Path path) throws IOException, Parser.Exception { + Reader reader = Files.newBufferedReader(path); + StateMachineScanner scanner = new StateMachineScanner(reader); + StateMachineParser parser = new StateMachineParser(); + StateMachine result = (StateMachine) parser.parse(scanner); + reader.close(); + return result; + } +} diff --git a/statemachine.task/src/main/resources/StateMachine.ecore b/statemachine.task/src/main/resources/StateMachine.ecore new file mode 100644 index 0000000..d2f3afe --- /dev/null +++ b/statemachine.task/src/main/resources/StateMachine.ecore @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="statemachine" nsURI="http://www.example.org/statemachine" nsPrefix="statemachine"> + <eClassifiers xsi:type="ecore:EClass" name="StateMachine"> + <eStructuralFeatures xsi:type="ecore:EReference" name="element" upperBound="-1" + eType="#//Element" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="initial" lowerBound="1" + eType="#//State"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="final" upperBound="-1" + eType="#//State"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Element" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="label" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="State" eSuperTypes="#//Element"> + <eStructuralFeatures xsi:type="ecore:EReference" name="outgoing" upperBound="-1" + eType="#//Transition" eOpposite="#//Transition/from"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="incoming" upperBound="-1" + eType="#//Transition" eOpposite="#//Transition/to"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Transition" eSuperTypes="#//Element"> + <eStructuralFeatures xsi:type="ecore:EReference" name="from" lowerBound="1" eType="#//State" + eOpposite="#//State/outgoing"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="to" lowerBound="1" eType="#//State" + eOpposite="#//State/incoming"/> + </eClassifiers> +</ecore:EPackage> diff --git a/statemachine.task/src/main/resources/log4j2.xml b/statemachine.task/src/main/resources/log4j2.xml new file mode 100644 index 0000000..e4e4e60 --- /dev/null +++ b/statemachine.task/src/main/resources/log4j2.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="Console"> + <PatternLayout pattern="%highlight{%d{HH:mm:ss.SSS} %-5level} %c{1.} - %msg%n"/> + </Console> + <RollingFile name="RollingFile" fileName="logs/statemachine.log" + filePattern="logs/statemachine-%i.log"> + <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"/> + <Policies> + <OnStartupTriggeringPolicy/> + </Policies> + <DefaultRolloverStrategy max="20"/> + </RollingFile> + </Appenders> + <Loggers> + <Root level="info"> + <AppenderRef ref="Console"/> + <AppenderRef ref="RollingFile"/> + </Root> + </Loggers> +</Configuration> diff --git a/statemachine.task/src/test/java/de/tudresden/inf/st/statemachine/AnalysisTest.java b/statemachine.task/src/test/java/de/tudresden/inf/st/statemachine/AnalysisTest.java new file mode 100644 index 0000000..28969a5 --- /dev/null +++ b/statemachine.task/src/test/java/de/tudresden/inf/st/statemachine/AnalysisTest.java @@ -0,0 +1,116 @@ +package de.tudresden.inf.st.statemachine; + +import beaver.Parser; +import de.tudresden.inf.st.statemachine.jastadd.model.State; +import de.tudresden.inf.st.statemachine.jastadd.model.StateMachine; +import de.tudresden.inf.st.statemachine.jastadd.model.Transition; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + +/** + * Testing analysis attributes. + * + * @author rschoene - Initial contribution + */ +public class AnalysisTest { + + @Test + public void test1() throws IOException, Parser.Exception { + StateMachine stateMachine = ParserUtils.load(Paths.get("src", "test", "resources", "machine_one.sm")); + State s = stateMachine.globallyResolveStateByToken("S"); + State a = stateMachine.globallyResolveStateByToken("A"); + State e = stateMachine.globallyResolveStateByToken("E"); + + assertEquals(0, s.minDistTo(s)); + assertEquals(1, s.minDistTo(a)); + assertEquals(2, s.minDistTo(e)); + assertEquals(1, a.minDistTo(e)); + assertEquals(1, a.minDistTo(s)); + assertEquals(0, a.minDistTo(a)); + assertEquals(-1, e.minDistTo(s)); + assertEquals(-1, e.minDistTo(a)); + assertEquals(0, e.minDistTo(e)); + } + + @Test + public void testEmpty() throws IOException, Parser.Exception { + StateMachine stateMachine = ParserUtils.load(Paths.get("src", "test", "resources", "empty.sm")); + + State s = stateMachine.globallyResolveStateByToken("S"); + State a = stateMachine.globallyResolveStateByToken("A"); + State e = stateMachine.globallyResolveStateByToken("E"); + + assertEquals(0, s.minDistTo(s)); + assertEquals(1, s.minDistTo(a)); + assertEquals(2, s.minDistTo(e)); + assertEquals(1, a.minDistTo(e)); + assertEquals(1, a.minDistTo(s)); + assertEquals(0, a.minDistTo(a)); + assertEquals(-1, e.minDistTo(s)); + assertEquals(-1, e.minDistTo(a)); + assertEquals(0, e.minDistTo(e)); + + // change some things + Transition t5 = new Transition(); + t5.setFrom(a); + t5.setTo(e); + stateMachine.addElement(t5); + + System.out.println(stateMachine.prettyPrint()); + + assertEquals(0, s.minDistTo(s)); + assertEquals(1, s.minDistTo(a)); + assertEquals(1, s.minDistTo(e)); + assertEquals(0, a.minDistTo(e)); + assertEquals(1, a.minDistTo(s)); + assertEquals(0, a.minDistTo(a)); + assertEquals(-1, e.minDistTo(s)); + assertEquals(-1, e.minDistTo(a)); + assertEquals(0, e.minDistTo(e)); + + Set<Transition> epsilons = stateMachine.epsilonTransitions(); + assertEquals(2, epsilons.size()); + + for (Transition eps : epsilons) { + removeEpsilonTransition(stateMachine, eps); + } + + State ae = stateMachine.globallyResolveStateByToken("A+E"); + Transition t6 = new Transition(); + t6.setFrom(s); + t6.setTo(ae); + stateMachine.addElement(t6); + + System.out.println(stateMachine.prettyPrint()); + + epsilons = stateMachine.epsilonTransitions(); + assertEquals(1, epsilons.size()); + + for (Transition eps : epsilons) { + removeEpsilonTransition(stateMachine, eps); + } + } + + private void removeEpsilonTransition(StateMachine stateMachine, Transition eps) { + System.out.print("removing epsilon transition " + eps.prettyPrint()); + System.out.println("Minimal distances before:"); + initialToFinalDists(stateMachine); + stateMachine.removeEpsilonTransition(eps); + System.out.println("Minimal distances after:"); + initialToFinalDists(stateMachine); + System.out.println("StateMachine after"); + System.out.println(stateMachine.prettyPrint()); + } + + private void initialToFinalDists(StateMachine stateMachine) { + for (State finalState : stateMachine.getFinalList()) { + System.out.println("initial state "+ stateMachine.getInitial() + " to " + finalState + " in " + + stateMachine.getInitial().minDistTo(finalState) + " step(s)"); + } + } +} diff --git a/statemachine.task/src/test/java/de/tudresden/inf/st/statemachine/ParserTest.java b/statemachine.task/src/test/java/de/tudresden/inf/st/statemachine/ParserTest.java new file mode 100644 index 0000000..16a6344 --- /dev/null +++ b/statemachine.task/src/test/java/de/tudresden/inf/st/statemachine/ParserTest.java @@ -0,0 +1,85 @@ +package de.tudresden.inf.st.statemachine; + +import beaver.Parser; +import de.tudresden.inf.st.statemachine.jastadd.model.State; +import de.tudresden.inf.st.statemachine.jastadd.model.StateMachine; +import de.tudresden.inf.st.statemachine.jastadd.model.Transition; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.collection.IsEmptyCollection.empty; +import static org.junit.Assert.*; + +/** + * Testing the statemachine parser. + * + * @author rschoene - Initial contribution + */ +public class ParserTest { + + @Test + public void test1() throws IOException, Parser.Exception { + StateMachine stateMachine = ParserUtils.load(Paths.get("src", "test", "resources", "machine_one.sm")); + + assertEquals(6, stateMachine.getNumElement()); + assertEquals(3, stateMachine.states().size()); + assertEquals(3, stateMachine.transitions().size()); + + State s = stateMachine.globallyResolveStateByToken("S"); + State a = stateMachine.globallyResolveStateByToken("A"); + State e = stateMachine.globallyResolveStateByToken("E"); + Transition t1 = stateMachine.globallyResolveTransitionByToken("t1"); + Transition t2 = stateMachine.globallyResolveTransitionByToken("t2"); + Transition t3 = stateMachine.globallyResolveTransitionByToken("t3"); + + assertNotNull(s); + assertNotNull(a); + assertNotNull(e); + + assertEquals(s, stateMachine.getInitial()); + assertEquals(1, stateMachine.getFinalList().size()); + assertThat("E is not final", stateMachine.getFinalList(), hasItem(e)); + + assertNotNull(t1); + assertNotNull(t2); + assertNotNull(t3); + + // t1: s -> a + assertEquals(s, t1.getFrom()); + assertEquals(a, t1.getTo()); + // t2: a -> s + assertEquals(a, t2.getFrom()); + assertEquals(s, t2.getTo()); + // t3: a -> e + assertEquals(a, t3.getFrom()); + assertEquals(e, t3.getTo()); + + List<State> outgoingFromS = outgoingConnectedStates(s); + List<State> outgoingFromA = outgoingConnectedStates(a); + assertThat(outgoingFromS, not(empty())); + assertThat(outgoingFromA, not(empty())); + assertEquals(1, outgoingFromS.size()); + assertThat("Transition S -> A missing", outgoingFromS, hasItem(a)); + assertEquals(2, outgoingFromA.size()); + assertThat("Transitions A -> S or A -> E missing", outgoingFromA, hasItems(s, e)); + assertThat(outgoingConnectedStates(e), empty()); + + String printedForm = "initial state S;\n" + + "state A;\n" + + "final state E;\n" + + "trans S -> A : t1;\n" + + "trans A -> S : t2;\n" + + "trans A -> E : t3;\n"; + assertEquals(printedForm, stateMachine.prettyPrint()); + } + + private List<State> outgoingConnectedStates(State s) { + return s.getOutgoingList().stream().map(Transition::getTo).collect(Collectors.toList()); + } + +} diff --git a/statemachine.task/src/test/resources/empty.sm b/statemachine.task/src/test/resources/empty.sm new file mode 100644 index 0000000..1bbcce6 --- /dev/null +++ b/statemachine.task/src/test/resources/empty.sm @@ -0,0 +1,7 @@ +initial state S; +state A; +final state E; +trans S -> A : t1; +trans A -> S : t2; +trans A -> A; +trans A -> E : t3; diff --git a/statemachine.task/src/test/resources/machine_one.sm b/statemachine.task/src/test/resources/machine_one.sm new file mode 100644 index 0000000..897ccba --- /dev/null +++ b/statemachine.task/src/test/resources/machine_one.sm @@ -0,0 +1,6 @@ +initial state S; +state A; +final state E; +trans S -> A : t1; +trans A -> S : t2; +trans A -> E : t3; diff --git a/statemachine.task/src/test/resources/telephone.sm b/statemachine.task/src/test/resources/telephone.sm new file mode 100644 index 0000000..655e5bb --- /dev/null +++ b/statemachine.task/src/test/resources/telephone.sm @@ -0,0 +1,25 @@ +initial state start; +state onHook; +state dialing; +state ringing; +state waitingForConnection; +state communicating; +state onHold; +final state end; + +trans start -> onHook : start ; +trans onHook -> dialing : startDialing ; +trans dialing -> onHook : hangUp ; +trans onHook -> ringing : incomingCall ; +trans ringing -> onHook : otherPartyHangUp ; +trans dialing -> waitingForConnection : completeNumber ; +trans waitingForConnection -> onHook : hangUp ; +trans waitingForConnection -> onHook : timeOut ; +trans waitingForConnection -> communicating : otherPartyPickUp ; +trans ringing -> communicating : pickUp ; +trans communicating -> onHook : hangUp ; +trans communicating -> onHold : putOnHold ; +trans onHold -> communicating : takeOffHold ; +trans communicating -> end : otherPartyHangUp ; +trans onHold -> end : otherPartyHangUp ; +trans onHold -> end : hangUp ; -- GitLab