Skip to content

Commit

Permalink
added some escape sequences to the SRE parser
Browse files Browse the repository at this point in the history
  • Loading branch information
qw3ry committed Aug 21, 2017
1 parent dce84a6 commit 8c0a29f
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 47 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ hs_err_pid*

# maven build
/target/

# eclipse
.classpath
.project
.settings/
70 changes: 35 additions & 35 deletions src/main/java/de/uni_stuttgart/beehts/model/DTMC.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ public class DTMC {
private Set<Node> finalNodes = new HashSet<>();

/**
* Creates an "empty" DTMC, that is, a DTMC with an initial node that is
* final and has no edges associated. The only String that can be recognized
* by this machine is the empty string.
* Creates an "empty" DTMC, that is, a DTMC with an initial node that is final
* and has no edges associated. The only String that can be recognized by this
* machine is the empty string.
*
* @return an empty DTMC.
*/
Expand Down Expand Up @@ -164,8 +164,8 @@ public Set<Node> getFinalNodes() {
}

/**
* Add a new node to the DTMC. The name may be modified if a node with this
* name does already exist.
* Add a new node to the DTMC. The name may be modified if a node with this name
* does already exist.
*
* @return the newly created node. You may modify the node afterwards, if
* necessary.
Expand Down Expand Up @@ -210,8 +210,7 @@ private Node addNodeNoCheck(Node n) {
/**
* Add a new node to the DTMC and make it final.
*
* @return The created node. You may modify the node afterwards, if
* necessary.
* @return The created node. You may modify the node afterwards, if necessary.
*/
public Node addFinalNode(String name) {
Node n = addNode(name);
Expand Down Expand Up @@ -239,7 +238,7 @@ public void removeNode(Node n) {
removeEdges(getIncomingEdges(n));
removeEdges(getOutgoingEdges(n));
this.finalNodes.remove(n);
this.nodes.remove(n);
this.nodes.getReverseView().remove(n);
freeNodeNames.add(n.index);
}

Expand Down Expand Up @@ -282,10 +281,14 @@ public Edge addEdge(Node from, Node to, String character, double probability) {
* the edge to add.
*/
public void addEdge(Edge e) {
if (!this.nodes.getReverseView().containsKey(e.from) || !this.nodes.getReverseView().containsKey(e.to)
|| this.getEdges().contains(e) || this.getFinalNodes().contains(e.from)) {
throw new IllegalArgumentException();
if (!this.nodes.getReverseView().containsKey(e.from) || !this.nodes.getReverseView().containsKey(e.to)) {
throw new IllegalArgumentException("1 or both of the nodes are not in this DTMC. Add them first.");
} else if (this.getEdges().contains(e)) {
throw new IllegalArgumentException("Edge has already been added.");
} else if (this.getFinalNodes().contains(e.from)) {
throw new IllegalArgumentException("No outgoing edges for final nodes allowed.");
}

edges.add(e);
incoming.get(e.to).add(e);
outgoing.get(e.from).add(e);
Expand Down Expand Up @@ -323,8 +326,8 @@ public void removeEdges(Collection<Edge> edges) {
}

/**
* Replace the inital node by n. Note that the old initial node will still
* exist in the DTMC, but it is not inital anymore.
* Replace the inital node by n. Note that the old initial node will still exist
* in the DTMC, but it is not inital anymore.
*
* @param n
* The node to make initial.
Expand Down Expand Up @@ -383,23 +386,22 @@ public void clearFinalNodes() {

/**
* Adds the nodes and edges of the other DTMC to the this object. Will not
* change the other DTMC. Will not change the initial node. Will add the
* final nodes of other to the final node set. Note that you need to make
* the attached DTMC accessible from the initial node by yourself, if you
* want so.
* change the other DTMC. Will not change the initial node. Will add the final
* nodes of other to the final node set. Note that you need to make the attached
* DTMC accessible from the initial node by yourself, if you want so.
*
* @param other
* the other DTMC.
* @return a map from the original nodes in other to the new nodes in the
* this object.
* @return a map from the original nodes in other to the new nodes in the this
* object.
*/
public Map<Node, Node> attachOther(DTMC other) {
Map<Node, Node> retVal = new HashMap<>();

other.getNodes().stream().forEachOrdered(n -> retVal.put(n, this.addNode(n.name)));
other.getFinalNodes().stream().forEach(n -> this.makeNodeFinal(retVal.get(n)));
other.getEdges().stream()
.forEach(e -> this.addEdge(retVal.get(e.from), retVal.get(e.to), e.character, e.getProbability()));
other.getEdges().stream().forEach(
e -> this.addEdge(retVal.get(e.from), retVal.get(e.to), e.character, e.getProbability()));

return retVal;
}
Expand All @@ -414,8 +416,8 @@ public Map<Node, Node> attachOther(DTMC other) {
*/
public void simplify() {
// strip epsilon edges, wherever possible
Set<Edge> epsilonEdges = this.getEdges().stream().filter(e -> "".equals(e.character))
.collect(Collectors.toSet());
Set<Edge> epsilonEdges = this.getEdges().stream().filter(e -> "".equals(e.character)).collect(
Collectors.toSet());
epsilonEdges.forEach(e1 -> {
if (!this.getFinalNodes().contains(e1.to)) {
this.getOutgoingEdges(e1.to).forEach(e2 -> {
Expand Down Expand Up @@ -452,9 +454,8 @@ public void simplify() {
}

/**
* Create a deep copy of this DTMC. That means that modifications of the
* copy will not propagate back to the original DTMC (and the other way
* round).
* Create a deep copy of this DTMC. That means that modifications of the copy
* will not propagate back to the original DTMC (and the other way round).
*
* @return A deep copy of this DTMC.
*/
Expand Down Expand Up @@ -514,8 +515,7 @@ public static class Edge {
private double probability;

/**
* Constructor. Initializes the final fields and does some sanity
* checks.
* Constructor. Initializes the final fields and does some sanity checks.
*
* @param from
* The start node.
Expand Down Expand Up @@ -573,13 +573,13 @@ public String toString() {
public static class Node {

/**
* The index of this node. You can think of this as an unique name of
* the node. Automatically assigned.
* The index of this node. You can think of this as an unique name of the node.
* Automatically assigned.
*/
public final int index;
/**
* The name of the node. Use this if you need to give names to your
* nodes. This is used by the toString method, if present.
* The name of the node. Use this if you need to give names to your nodes. This
* is used by the toString method, if present.
*/
public String name = null;

Expand All @@ -589,9 +589,9 @@ public String toString() {
}

/**
* Constructor. Assigns an available index to the node and adds it to
* the DTMC it belongs to. You should probably use {@link DTMC#addNode()
* DTMC.addNode} instead.
* Constructor. Assigns an available index to the node and adds it to the DTMC
* it belongs to. You should probably use {@link DTMC#addNode() DTMC.addNode}
* instead.
*/
public Node() {
if (freeNodeNames.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package de.uni_stuttgart.beehts.model.construction;

import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

import de.uni_stuttgart.beehts.model.SRE;
import de.uni_stuttgart.beehts.model.Tuple;
Expand Down Expand Up @@ -68,11 +68,11 @@ public static SRE parse(String s) {
}

private static List<Token> tokenize(String s) {
List<String> tokens = splitAtTokens(s);
tokens.replaceAll(x -> x.trim());
List<Token> list = new ArrayList<>(tokens.size());
tokens.stream().filter(x -> !x.isEmpty()).forEach(x -> list.add(stringToToken(x)));
return list;
return splitAtTokens(s).stream()
.map(x -> x.trim())
.filter(x -> !x.isEmpty())
.map(x -> stringToToken(x))
.collect(Collectors.toList());
}

private static SRE buildSRE(List<Token> tokens) {
Expand Down Expand Up @@ -130,12 +130,13 @@ private static SRE buildSRE(List<Token> tokens, int firstIdx, int lastIdx) {
break;
}
case IDENTIFIER: {
String content = tokens.get(i).content;
if (lookingForDelimiter) {
throw new InputMismatchException(
"Was looking for a delimiter, got an identifier: " + tokens.get(i).content);
throw new InputMismatchException("Was looking for a delimiter, got an identifier: " + content);
}
content = applyEscapeSequences(content);

This comment has been minimized.

Copy link
@qw3ry

qw3ry Aug 21, 2017

Author Owner

Here, the escape sequences are translated into literal characters

int rate = getRate(tokens, i + 1);
sres.add(new Tuple<>(SREBuilder.atomic(tokens.get(i).content), rate));
sres.add(new Tuple<>(SREBuilder.atomic(content), rate));
if (rate >= 0)
i += 3;
lookingForDelimiter = true;
Expand Down Expand Up @@ -220,6 +221,46 @@ else if (tokens.get(ratePosition).type != Token.Type.RATE)
}
}

/**
* Apply the escape sequences supported by the parser.
*
* Supported escape sequences (note that \ is escaped as neccessary in java
* string literals):<br>
* {@code "" == applyEscapeSequences("\\e")}<br>
* {@code " " == applyEscapeSequences("\\s")}<br>
* For other characters after '\', the character itself is placed there:<br>
* {@code "\\" == applyEscapeSequences("\\\\")}<br>
* {@code "x" == applyEscapeSequences("\\x")}
*
* @param content
* The string with the escape sequences.
* @return The same string with the escape sequences replaced by literals.
*/
private static String applyEscapeSequences(String content) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < content.length(); i++) {
if (content.charAt(i) == '\\') {
i++;
if (i >= content.length()) {
throw new IllegalArgumentException();
}
switch (content.charAt(i)) {
case 'e':
break; // empty string - ignore.
case 's':
sb.append(' ');
break;
default:
sb.append(content.charAt(i));
break;
}
} else {
sb.append(content.charAt(i));
}
}
return sb.toString();
}

private static int getRate(List<Token> tokens, int i) {
if (i + 2 < tokens.size() && tokens.get(i).type == Token.Type.BRACKET_OPEN
&& tokens.get(i + 1).type == Token.Type.RATE && tokens.get(i + 2).type == Token.Type.BRACKET_CLOSE) {
Expand Down Expand Up @@ -251,7 +292,7 @@ private static Token stringToToken(String x) {
default:
if (x.matches("\\d+(\\.\\d+)?")) {
return new Token(Token.Type.RATE, x);
} else if (x.matches("[A-Za-z_\\\\]\\w*")) {
} else if (x.matches("(\\\\[A-Za-z0-9]|(\\w|\\\\.)+)")) {

This comment has been minimized.

Copy link
@qw3ry

qw3ry Aug 21, 2017

Author Owner

Now we may allow a more sophisticated RegEx. Without the additional java escapes: \\[A-Za-z0-9] | (\w | \\.) +

return new Token(Token.Type.IDENTIFIER, x);
} else {
throw new InputMismatchException("Could not parse the following string: " + x);
Expand All @@ -277,6 +318,10 @@ private static List<String> splitAtTokens(String s) {
tokens.add(s.charAt(i) + "");
lastIndex = i + 1;
break;
case '\\':
// escape sequence incoming, using next character as literal.
i++;
// treat this as normal text, therefore fallthrough
default:
break;
}
Expand Down
13 changes: 11 additions & 2 deletions src/test/java/de/uni_stuttgart/beehts/TestModels.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.uni_stuttgart.beehts;

import org.junit.Test;
import static org.junit.Assert.*;

import de.uni_stuttgart.beehts.model.DTMC;
import de.uni_stuttgart.beehts.model.DTMCDelta;
Expand All @@ -15,6 +16,14 @@ public class TestModels {
@Test
public void parseSRE() {
SREBuilder.parse("((a:b)*0.2)[1]+c[2]");
assertEquals(SREBuilder.parse("\\e").toString(), "");
assertEquals(SREBuilder.parse("\\s").toString(), " ");
char[] chars = ":.,;-_#'+*~\\}][{?=)(/&%$§\"!^°<>|".toCharArray();
for (char c : chars) {
assertEquals(SREBuilder.parse("\\" + c).toString(), String.valueOf(c));
}
SREBuilder.parse("\\e[1] + ((a:\\::b)*0.2)[1]+\\1[2]");

}

@Test
Expand All @@ -25,12 +34,12 @@ public void parseDTMC() {

// second syntax type
DTMCParser.parse("0 \n 1 2 \n");
DTMCParser.parse("0 \n 1 2 \n 0 1 0.23 a \n 0 2 0.77 b \n 1 2 1 c");
DTMCParser.parse("0 \n 2 \n 0 1 0.23 a \n 0 2 0.77 b \n 1 2 1 c");
}

@Test
public void applyDelta() {
DTMC dtmc = DTMCParser.parse("0 \n 1 2 \n 0 1 0.23 a \n 0 2 0.77 b \n 1 2 1 c");
DTMC dtmc = DTMCParser.parse("0 \n 2 \n 0 1 0.23 a \n 0 2 0.77 b \n 1 2 1 c");
Delta<DTMC> deltaDTMC = DTMCDelta.parse("0 2 b > 0 2 0.5 b\n+0 0 0.27 d", dtmc);
dtmc = deltaDTMC.applyChanges(dtmc);

Expand Down

0 comments on commit 8c0a29f

Please sign in to comment.