From 937cb5b8cb9fe82036d951a834566dfa73501f45 Mon Sep 17 00:00:00 2001
From: rschoene <rene.schoene@tu-dresden.de>
Date: Fri, 29 Nov 2019 18:11:51 +0100
Subject: [PATCH] Add more analyses and the transformation.

---
 statemachine.base/build.gradle                |   3 +-
 .../src/main/jastadd/Analysis.jrag            |  51 ++++++++
 .../src/main/jastadd/Printing.jrag            |   2 +-
 .../main/jastadd/StateMachineParser.parser    |   9 ++
 .../inf/st/statemachine/AnalysisTest.java     | 116 ++++++++++++++++++
 statemachine.base/src/test/resources/empty.sm |   7 ++
 6 files changed, 186 insertions(+), 2 deletions(-)
 create mode 100644 statemachine.base/src/test/java/de/tudresden/inf/st/statemachine/AnalysisTest.java
 create mode 100644 statemachine.base/src/test/resources/empty.sm

diff --git a/statemachine.base/build.gradle b/statemachine.base/build.gradle
index 9657cfb..fc5b063 100644
--- a/statemachine.base/build.gradle
+++ b/statemachine.base/build.gradle
@@ -131,7 +131,8 @@ jastadd {
     scanner.genDir = "src/gen/java/de/tudresden/inf/st/statemachine/jastadd/scanner"
     parser.genDir = "src/gen/java/de/tudresden/inf/st/statemachine/jastadd/parser"
 
-    extraJastAddOptions = ['--List=JastAddList']
+//  default options are: '--rewrite=cnta', '--safeLazy', '--visitCheck=false', '--cacheCycle=false'
+    extraJastAddOptions = ['--List=JastAddList', '--incremental=param']
 }
 
 // Workflow configuration for phases
diff --git a/statemachine.base/src/main/jastadd/Analysis.jrag b/statemachine.base/src/main/jastadd/Analysis.jrag
index fd38416..19eb894 100644
--- a/statemachine.base/src/main/jastadd/Analysis.jrag
+++ b/statemachine.base/src/main/jastadd/Analysis.jrag
@@ -47,4 +47,55 @@ aspect Analysis {
           this.getInitial().reachableWithin(this.states().size()).contains(finalState));
     }
   }
+
+  // --- new ---
+
+  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.base/src/main/jastadd/Printing.jrag b/statemachine.base/src/main/jastadd/Printing.jrag
index 5f511fc..1ac2e38 100644
--- a/statemachine.base/src/main/jastadd/Printing.jrag
+++ b/statemachine.base/src/main/jastadd/Printing.jrag
@@ -8,5 +8,5 @@ aspect Printing {
 
   syn String Element.prettyPrint();
   eq State.prettyPrint() = (isInitial() ? "initial " : "") + (isFinal() ? "final " : "") + "state " + getLabel() + ";\n";
-  eq Transition.prettyPrint() = "trans " + getFrom().getLabel() + " -> " + getTo().getLabel() + " : " + getLabel() + ";\n";
+  eq Transition.prettyPrint() = "trans " + getFrom().getLabel() + " -> " + getTo().getLabel() + (isEpsilon() ? "" : " : " + getLabel()) + ";\n";
 }
diff --git a/statemachine.base/src/main/jastadd/StateMachineParser.parser b/statemachine.base/src/main/jastadd/StateMachineParser.parser
index 361e117..e31987a 100644
--- a/statemachine.base/src/main/jastadd/StateMachineParser.parser
+++ b/statemachine.base/src/main/jastadd/StateMachineParser.parser
@@ -22,6 +22,7 @@ statemachine goal =
       StateMachine result = new StateMachine(element_list);
       result.setInitial(initial);
       finals.forEach( result::addFinal );
+      result.treeResolveAll();
       return result;
     :}
   ;
@@ -44,6 +45,14 @@ Element element =
       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 =
diff --git a/statemachine.base/src/test/java/de/tudresden/inf/st/statemachine/AnalysisTest.java b/statemachine.base/src/test/java/de/tudresden/inf/st/statemachine/AnalysisTest.java
new file mode 100644
index 0000000..28969a5
--- /dev/null
+++ b/statemachine.base/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.base/src/test/resources/empty.sm b/statemachine.base/src/test/resources/empty.sm
new file mode 100644
index 0000000..1bbcce6
--- /dev/null
+++ b/statemachine.base/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;
-- 
GitLab