Skip to content
Snippets Groups Projects
Commit cfdd9ff3 authored by René Schöne's avatar René Schöne
Browse files

Split statemachine in task and solution.

parent a3e0a046
No related branches found
No related tags found
No related merge requests found
Showing
with 721 additions and 0 deletions
build/
src/gen/
src/gen-res/
.test-tmp
/bin/
logs/
/doc/
// 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}
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());
}
}
}
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);
}
}
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);
}
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());
}
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";
}
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();
}
}
%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;
:}
;
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); }
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();
}
}
aspect Util {
}
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)");
}
}
}
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;
}
}
<?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>
<?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>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment