package org.scribble.model.endpoint; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.scribble.model.endpoint.actions.EAccept; import org.scribble.model.endpoint.actions.EConnect; import org.scribble.model.endpoint.actions.EDisconnect; import org.scribble.model.endpoint.actions.EAction; import org.scribble.model.endpoint.actions.EReceive; import org.scribble.model.endpoint.actions.ESend; import org.scribble.model.endpoint.actions.EWrapClient; import org.scribble.model.endpoint.actions.EWrapServer; import org.scribble.sesstype.Payload; import org.scribble.sesstype.name.DataType; import org.scribble.sesstype.name.MessageId; import org.scribble.sesstype.name.MessageSigName; import org.scribble.sesstype.name.Op; import org.scribble.sesstype.name.Role; public class AutParser { public AutParser() { } public EGraph parse(String aut) { //Map<Integer, Map<String, Integer>> edges = new HashMap<>(); Map<Integer, List<String>> as = new HashMap<>(); Map<Integer, List<Integer>> succs = new HashMap<>(); int init = -1; try { BufferedReader br = new BufferedReader(new StringReader(aut)); String line = br.readLine(); if (line == null || !line.startsWith("des (") || !line.endsWith(")")) { throw new RuntimeException("Unexpected first line: " + line); } String[] first = line.substring("des (".length(), line.length() - 1).split(","); if (first.length != 3) { throw new RuntimeException("Unexpected first line: " + line); } init = Integer.parseInt(first[0]); //int trans = Integer.parseInt(first[1]); //int states = Integer.parseInt(first[2]); while ((line = br.readLine()) != null) { if (!line.startsWith("(") || !line.endsWith(")")) { throw new RuntimeException("Unexpected line: " + line); } //String[] read = line.substring(1, line.length()-1).split(","); String[] read = new String[] { line.substring(1, line.indexOf(',')), line.substring(line.indexOf(',')+1, line.lastIndexOf(',')), line.substring(line.lastIndexOf(',')+1, line.length()-1) }; int s = Integer.parseInt(read[0]); String a = read[1].substring(1, read[1].length()-1); int succ = Integer.parseInt(read[2]); //Map<String, Integer> tmp = edges.get(s); List<String> tmp1 = as.get(s); List<Integer> tmp2 = succs.get(s); if (tmp1 == null) { //tmp = new HashMap<>(); //edges.put(s, tmp); tmp1 = new LinkedList<>(); as.put(s, tmp1); tmp2 = new LinkedList<>(); succs.put(s, tmp2); } //tmp.put(a, succ); tmp1.add(a); tmp2.add(succ); } } catch (IOException e) { throw new RuntimeException(e); } //Set<Integer> allSuccs = edges.values().stream().flatMap((j) -> j.values().stream()).collect(Collectors.toSet()); Set<Integer> allSuccs = succs.values().stream().flatMap((j) -> j.stream()).collect(Collectors.toSet()); int term = -1; //Set<Integer> terms = allSuccs.stream().filter((j) -> !edges.containsKey(j)).collect(Collectors.toSet()); Set<Integer> terms = allSuccs.stream().filter((j) -> !succs.containsKey(j)).collect(Collectors.toSet()); if (terms.size() > 0) { term = terms.iterator().next(); } EGraphBuilderUtil builder = new EGraphBuilderUtil(); builder.reset(); Map<Integer, EState> map = new HashMap<>(); map.put(init, builder.getEntry()); if (term != -1) { map.put(term, builder.getExit()); } map.put(init, builder.getEntry()); //for (int i : edges.keySet()) for (int i : as.keySet()) { if (i != init && i != term) { map.put(i, builder.newState(Collections.emptySet())); } } //for (int i : succs) for (int i : succs.keySet()) { if (!map.containsKey(i) && i != init && i != term) { map.put(i, builder.newState(Collections.emptySet())); } } //for (int i : edges.keySet()) for (int i : as.keySet()) { EState s = map.get(i); //Map<String, Integer> tmp = edges.get(i); List<String> tmp1 = as.get(i); List<Integer> tmp2 = succs.get(i); //if (tmp != null) if (tmp1 != null) { //for (String a : tmp.keySet()) Iterator<Integer> is = tmp2.iterator(); for (String a : tmp1) { int succ = is.next(); //builder.addEdge(s, parseIOAction(a), map.get(tmp.get(a))); builder.addEdge(s, parseIOAction(a), map.get(succ)); } } } //return builder.finalise(); return new EGraph(builder.getEntry(), builder.getExit()); } // Cf. getCommSymbol of IOActions // FIXME: simply do a match for getCommSymbol? private static EAction parseIOAction(String a) { String peer; String action; String msg; // Could be an Op or a MessageSigName (affects API generation) String[] pay = null; /*int i = a.indexOf("!"); i = (i == -1) ? a.indexOf("?") : i; int j = i+1; String tmp = a.substring(j, j+1); if (tmp.equals("!") || tmp.equals("?")) { j++; } action = a.substring(i, j);*/ int i, j; if ((i = a.indexOf("!")) != -1) { j = i+1; if (a.charAt(j) == '!') { j++; if (a.charAt(i-1) == '(') { if (a.charAt(j+1) != ')') { throw new RuntimeException("Shouldn't get in here: " + a); } i--; j++; } } } else if ((i = a.indexOf("?")) != -1) { j = i+1; if (a.charAt(j) == '?') { j++; if (a.charAt(i-1) == '(') { if (a.charAt(j+1) != ')') { throw new RuntimeException("Shouldn't get in here: " + a); } i--; j++; } } } else if ((i = a.indexOf('/')) != -1) { if (a.charAt(i-1) != '-' || a.charAt(i+1) != '-') { throw new RuntimeException("Shouldn't get in here: " + a); } j = i+2; i--; } else { throw new RuntimeException("[TODO] aut parsing not supported for: " + a); } action = a.substring(i, j); peer = a.substring(0, i); int k = a.indexOf("("); msg = a.substring(j, k); String p = a.substring(k+1, a.length()-1); if (!p.isEmpty()) { pay = p.split(","); } switch (action) { case "!": { Payload payload = (pay != null) ? new Payload(Arrays.asList(pay).stream().map((pe) -> new DataType(pe)).collect(Collectors.toList())) : Payload.EMPTY_PAYLOAD; return new ESend(new Role(peer), getMessageIdHack(msg), payload); // FIXME: how about MessageSigNames? -- currently OK, treated as empty payload (cf. ModelAction) } case "?": { Payload payload = (pay != null) ? new Payload(Arrays.asList(pay).stream().map((pe) -> new DataType(pe)).collect(Collectors.toList())) : Payload.EMPTY_PAYLOAD; return new EReceive(new Role(peer), getMessageIdHack(msg), payload); // FIXME: how about MessageSigNames?) } case "!!": { //return new Connect(new Role(peer)); Payload payload = (pay != null) ? new Payload(Arrays.asList(pay).stream().map((pe) -> new DataType(pe)).collect(Collectors.toList())) : Payload.EMPTY_PAYLOAD; return new EConnect(new Role(peer), getMessageIdHack(msg), payload); } case "??": { //return new Accept(new Role(peer)); Payload payload = (pay != null) ? new Payload(Arrays.asList(pay).stream().map((pe) -> new DataType(pe)).collect(Collectors.toList())) : Payload.EMPTY_PAYLOAD; return new EAccept(new Role(peer), getMessageIdHack(msg), payload); } case "(!!)": { return new EWrapClient(new Role(peer)); } case "(??)": { return new EWrapServer(new Role(peer)); } case "-/-": { return new EDisconnect(new Role(peer)); } default: { throw new RuntimeException("[TODO] aut parsing not supported for: " + msg); } } } // Cf. ModelState.toAut, ModelAction.toStringWithMessageIdHack private static MessageId<?> getMessageIdHack(String msg) { return (msg.startsWith("^")) ? new MessageSigName(msg.substring(1)) : new Op(msg); } }