package de.yasc.example.petrinet; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; public final class PetrinetParser implements AutoCloseable { private final BufferedReader reader; public PetrinetParser(InputStream stream) { reader = new BufferedReader(new InputStreamReader(stream)); } private static class Line { static enum Type { PLACES, TRANSITION, MARKING, COMMENT, UNKNOWN; } Type type; Integer val0; Integer val1[]; Integer val2[]; private Integer parsePositiveInt(String token) throws IOException { try { var val = Integer.valueOf(token); if (val < 0) { throw new IOException("invalid input - '" + token + "' is not positive"); } return val; } catch (NumberFormatException ex) { throw new IOException("invalid input - '" + token + "' is not an integer"); } } private void parsePlaces(String tokens[]) throws IOException { if (tokens.length != 2) { throw new IOException("invalid input - syntax of places is 'p <#n>'"); } val0 = parsePositiveInt(tokens[1]); } private void parseTransition(String tokens[]) throws IOException { if (tokens.length < 4) { throw new IOException("invalid input - syntax of transition is 't ... / ... '"); } var vals = new ArrayList<>(); int i = 1; for (; i < tokens.length; i++) { if (tokens[i].equals("/")) { i++; break; } vals.add(parsePositiveInt(tokens[i])); } val1 = vals.toArray(new Integer[0]); if (i == tokens.length) { throw new IOException("invalid input - transition has no postset"); } if (val1.length == 0) { throw new IOException("invalid input - transition has no preset"); } vals.clear(); for (; i < tokens.length; i++) { vals.add(parsePositiveInt(tokens[i])); } val2 = vals.toArray(new Integer[0]); } private void parseMarking(String tokens[]) throws IOException { if (tokens.length < 2) { throw new IOException("invalid input - syntax of marking is 'm ... "); } var vals = new ArrayList<>(); int i = 1; for (; i < tokens.length; i++) { vals.add(parsePositiveInt(tokens[i])); } val1 = vals.toArray(new Integer[0]); } Line(String line) throws IOException { if (line.isBlank()) { type = Type.COMMENT; } else { var tokens = line.split("\\s"); assert tokens.length > 0; switch (tokens[0]) { case "p": type = Type.PLACES; parsePlaces(tokens); break; case "t": type = Type.TRANSITION; parseTransition(tokens); break; case "m": type = Type.MARKING; parseMarking(tokens); break; case "#": type = Type.COMMENT; break; default: type = Type.UNKNOWN; } } } } public Petrinet read() throws IOException { Petrinet result = null; String input; int line = 0; while ((input = reader.readLine()) != null) { line++; try { var parsedLine = new Line(input); switch (parsedLine.type) { case COMMENT: break; case PLACES: if (result == null) { result = new Petrinet(parsedLine.val0); } else { throw new IOException("places command must occur once"); } break; case TRANSITION: if (result == null) throw new IOException("the first command must be 'p' to define the places"); result.addTransistion(parsedLine.val1, parsedLine.val2); break; case MARKING: if (result == null) throw new IOException("the first command must be 'p' to define the places"); result.addMarking(parsedLine.val1); break; default: throw new IOException("unknown command"); } } catch (IndexOutOfBoundsException ex) { throw new IOException(String.format("a place with the specified index does not exist (line %d)", line), ex); } catch (IOException ex) { throw new IOException(ex.getMessage() + String.format(" (line %d)", line), ex); } } return result; } @Override public void close() throws IOException { reader.close(); } }