9a70a426ae5a734152ba2e047858cccf23867725
[petrinet.git] / src / main / java / de / yasc / example / petrinet / PetrinetParser.java
1 package de.yasc.example.petrinet;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.InputStreamReader;
7 import java.util.ArrayList;
8
9 public final class PetrinetParser implements AutoCloseable {
10
11     private final BufferedReader reader;
12
13     public PetrinetParser(InputStream stream) {
14         reader = new BufferedReader(new InputStreamReader(stream));
15     }
16
17     private static class Line {
18
19         static enum Type {
20             PLACES, TRANSITION, MARKING, COMMENT, UNKNOWN;
21         }
22
23         Type type;
24         Integer val0;
25         Integer val1[];
26         Integer val2[];
27
28         private Integer parsePositiveInt(String token) throws IOException {
29             try {
30                 var val = Integer.valueOf(token);
31                 if (val < 0) {
32                     throw new IOException("invalid input - '" + token + "' is not positive");
33                 }
34                 return val;
35             } catch (NumberFormatException ex) {
36                 throw new IOException("invalid input - '" + token + "' is not an integer");
37             }
38         }
39
40         private void parsePlaces(String tokens[]) throws IOException {
41             if (tokens.length != 2) {
42                 throw new IOException("invalid input - syntax of places is 'p <#n>'");
43             }
44             val0 = parsePositiveInt(tokens[1]);
45         }
46
47         private void parseTransition(String tokens[]) throws IOException {
48             if (tokens.length < 4) {
49                 throw new IOException("invalid input - syntax of transition is 't <pre_1> ... <pre_m> / <post_1> ... <post_n>'");
50             }
51             var vals = new ArrayList<>();
52             int i = 1;
53             for (; i < tokens.length; i++) {
54                 if (tokens[i].equals("/")) {
55                     i++;
56                     break;
57                 }
58                 vals.add(parsePositiveInt(tokens[i]));
59             }
60             val1 = vals.toArray(new Integer[0]);
61             if (i == tokens.length) {
62                 throw new IOException("invalid input - transition has no postset");
63             }
64             if (val1.length == 0) {
65                 throw new IOException("invalid input - transition has no preset");
66             }
67             vals.clear();
68             for (; i < tokens.length; i++) {
69                 vals.add(parsePositiveInt(tokens[i]));
70             }
71             val2 = vals.toArray(new Integer[0]);
72         }
73
74         private void parseMarking(String tokens[]) throws IOException {
75             if (tokens.length < 2) {
76                 throw new IOException("invalid input - syntax of marking is 'm <place_1> ... <place_n>");
77             }
78             var vals = new ArrayList<>();
79             int i = 1;
80             for (; i < tokens.length; i++) {
81                 vals.add(parsePositiveInt(tokens[i]));
82             }
83             val1 = vals.toArray(new Integer[0]);
84         }
85
86         Line(String line) throws IOException {
87             if (line.isBlank()) {
88                 type = Type.COMMENT;
89             } else {
90                 var tokens = line.split("\\s");
91                 assert tokens.length > 0;
92                 switch (tokens[0]) {
93                     case "p":
94                         type = Type.PLACES;
95                         parsePlaces(tokens);
96                         break;
97                     case "t":
98                         type = Type.TRANSITION;
99                         parseTransition(tokens);
100                         break;
101                     case "m":
102                         type = Type.MARKING;
103                         parseMarking(tokens);
104                         break;
105                     case "#":
106                         type = Type.COMMENT;
107                         break;
108                     default:
109                         type = Type.UNKNOWN;
110                 }
111             }
112         }
113     }
114
115     public PetriNet read() throws IOException {
116         PetriNet result = null;
117
118         String input;
119         int line = 0;
120         while ((input = reader.readLine()) != null) {
121             line++;
122             try {
123                 var parsedLine = new Line(input);
124                 switch (parsedLine.type) {
125                     case COMMENT: break;
126                     case PLACES:
127                         if (result == null) {
128                             result = new PetriNet(parsedLine.val0);
129                         } else {
130                             throw new IOException("places command must occur once");
131                         }
132                         break;
133                     case TRANSITION:
134                         if (result == null)
135                             throw new IOException("the first command must be 'p' to define the places");
136                         result.addTransistion(parsedLine.val1, parsedLine.val2);
137                         break;
138                     case MARKING:
139                         if (result == null)
140                             throw new IOException("the first command must be 'p' to define the places");
141                         result.addMarking(parsedLine.val1);
142                         break;
143                     default:
144                         throw new IOException("unknown command");
145                 }
146             } catch (IndexOutOfBoundsException ex) {
147                 throw new IOException(String.format("a place with the specified index does not exist (line %d)", line), ex);
148             } catch (IOException ex) {
149                 throw new IOException(ex.getMessage() + String.format(" (line %d)", line), ex);
150             }
151         }
152
153         return result;
154     }
155
156     @Override
157     public void close() throws Exception {
158         reader.close();
159     }
160 }