Tulip  5.2.0
Large graphs analysis and drawing
TLPParser.h
1 /*
2  *
3  * This file is part of Tulip (http://tulip.labri.fr)
4  *
5  * Authors: David Auber and the Tulip development Team
6  * from LaBRI, University of Bordeaux
7  *
8  * Tulip is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation, either version 3
11  * of the License, or (at your option) any later version.
12  *
13  * Tulip is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU General Public License for more details.
17  *
18  */
19 ///@cond DOXYGEN_HIDDEN
20 
21 #ifndef TLPPARSER_H
22 #define TLPPARSER_H
23 
24 #include <sstream>
25 #include <string>
26 #include <list>
27 
28 namespace tlp {
29 
30 class PluginProgress;
31 
32 struct ParserError {
33  ParserError(int err = 0, int lin = 0, int cha = 0)
34  : errorNumber(err), lineInFile(lin), charInLine(cha) {}
35  int errorNumber;
36  int lineInFile;
37  int charInLine;
38 };
39 
40 struct TLPValue {
41  std::string str;
42  long integer;
43  double real;
44  bool boolean;
45  std::pair<long, long> range;
46 };
47 
48 enum TLPToken {
49  BOOLTOKEN,
50  ENDOFSTREAM,
51  STRINGTOKEN,
52  INTTOKEN,
53  DOUBLETOKEN,
54  IDTOKEN,
55  ERRORINFILE,
56  OPENTOKEN,
57  CLOSETOKEN,
58  COMMENTTOKEN,
59  RANGETOKEN
60 };
61 //=====================================================================================
62 struct TLPTokenParser {
63  int curLine;
64  std::istream &is;
65  TLPTokenParser(std::istream &i) : curLine(0), is(i) {}
66 
67  bool newLine(char ch, int &curPos) {
68  if (ch != '\n') {
69  is.get(ch);
70 
71  if (ch != '\n') {
72  is.unget();
73  return false;
74  }
75 
76  ++curPos;
77  }
78 
79  ++curLine;
80  return true;
81  }
82 
83  TLPToken nextToken(TLPValue &val, int &curPos) {
84  val.str.erase();
85  bool endOfStream = false, strGet = false, slashMode = false, started = false, stop = false,
86  strComment = false;
87  char ch;
88 
89  while ((!stop) && (endOfStream = !(is.get(ch).fail()))) {
90  ++curPos;
91 
92  if (strGet)
93  switch (ch) {
94  case 13:
95  case '\n':
96  if (!newLine(ch, curPos))
97  break;
98 
99  val.str += ch;
100  break;
101 
102  case '\t':
103  val.str += " ";
104  break;
105 
106  case '\\':
107 
108  if (!slashMode) {
109  slashMode = true;
110  } else {
111  val.str += ch;
112  slashMode = false;
113  }
114 
115  break;
116 
117  case '"':
118 
119  if (!slashMode) {
120  return STRINGTOKEN;
121  } else {
122  val.str += ch;
123  slashMode = false;
124  }
125 
126  break;
127 
128  case 'n':
129  if (slashMode) {
130  val.str += '\n';
131  slashMode = false;
132  break;
133  }
134 
135  default:
136  if (!slashMode)
137  val.str += ch;
138 
139  slashMode = false;
140  break;
141  }
142  else if (strComment)
143  switch (ch) {
144  case 13:
145  case '\n':
146  if (!newLine(ch, curPos))
147  break;
148 
149  stop = true;
150  return COMMENTTOKEN;
151  break;
152 
153  default:
154  val.str += ch;
155  break;
156  }
157  else
158  switch (ch) {
159  case ' ':
160  case '\t':
161  if (started)
162  stop = true;
163 
164  break;
165 
166  case 13:
167  case '\n':
168  if (!newLine(ch, curPos))
169  break;
170 
171  if (started)
172  stop = true;
173 
174  break;
175 
176  case '(':
177  if (!started)
178  return OPENTOKEN;
179  else {
180  --curPos;
181  is.unget();
182  stop = true;
183  }
184 
185  break;
186 
187  case ')':
188  if (!started)
189  return CLOSETOKEN;
190  else {
191  --curPos;
192  is.unget();
193  stop = true;
194  }
195 
196  break;
197 
198  case '"':
199  strGet = true;
200 
201  if (started) {
202  --curPos;
203  is.unget();
204  stop = true;
205  } else
206  started = true;
207 
208  break;
209 
210  case ';':
211  strComment = true;
212 
213  if (started) {
214  --curPos;
215  is.unget();
216  stop = true;
217  } else
218  started = true;
219 
220  break;
221 
222  default:
223  val.str += ch;
224  started = true;
225  break;
226  }
227  }
228 
229  if (!started && !endOfStream)
230  return ENDOFSTREAM;
231 
232  char *endPtr = nullptr;
233  const char *cstr = val.str.c_str();
234  errno = 0;
235  long resultl = strtol(cstr, &endPtr, 10);
236 
237  if (errno == ERANGE)
238  return ERRORINFILE;
239 
240  unsigned long strlength = val.str.length();
241 
242  if (endPtr == (cstr + strlength)) {
243  val.integer = resultl;
244  return INTTOKEN;
245  }
246 
247  // check for a range
248  if (endPtr > cstr && (cstr + strlength) > (endPtr + 2)) {
249  val.range.first = resultl;
250 
251  if ((endPtr[0] == '.') && (endPtr[1] == '.')) {
252  char *beginPtr = endPtr + 2;
253  errno = 0;
254  resultl = strtol(beginPtr, &endPtr, 10);
255 
256  if (errno == ERANGE)
257  return ERRORINFILE;
258 
259  if (endPtr == (cstr + strlength)) {
260  if (resultl < val.range.first)
261  return ERRORINFILE;
262 
263  val.range.second = resultl;
264  return RANGETOKEN;
265  }
266  }
267  }
268 
269  endPtr = nullptr;
270 
271  double resultd = strtod(cstr, &endPtr);
272 
273  if (errno == ERANGE)
274  return ERRORINFILE;
275 
276  if (endPtr == (cstr + strlength)) {
277  val.real = resultd;
278  return DOUBLETOKEN;
279  }
280 
281  if (strcasecmp(cstr, "true") == 0) {
282  val.boolean = true;
283  return BOOLTOKEN;
284  }
285 
286  if (strcasecmp(cstr, "false") == 0) {
287  val.boolean = false;
288  return BOOLTOKEN;
289  }
290 
291  if (started)
292  return STRINGTOKEN;
293 
294  return ERRORINFILE;
295  }
296 };
297 //=====================================================================================
298 struct TLPBuilder {
299  virtual ~TLPBuilder() {}
300  virtual bool addBool(const bool) = 0;
301  virtual bool addInt(const int) = 0;
302  virtual bool addRange(int, int) = 0;
303  virtual bool addDouble(const double) = 0;
304  virtual bool addString(const std::string &) = 0;
305  virtual bool addStruct(const std::string &, TLPBuilder *&) = 0;
306  virtual bool close() = 0;
307  virtual bool canRead() {
308  return false;
309  }
310  virtual bool read(std::istream &) {
311  return false;
312  }
313 };
314 
315 struct TLPTrue : public TLPBuilder {
316  bool addBool(const bool) override {
317  return true;
318  }
319  bool addInt(const int) override {
320  return true;
321  }
322  bool addRange(int, int) override {
323  return true;
324  }
325  bool addDouble(const double) override {
326  return true;
327  }
328  bool addString(const std::string &) override {
329  return true;
330  }
331  bool addStruct(const std::string & /*structName*/, TLPBuilder *&newBuilder) override {
332  newBuilder = new TLPTrue();
333  return true;
334  }
335  bool close() override {
336  return true;
337  }
338 };
339 
340 struct TLPFalse : public TLPBuilder {
341  bool addBool(const bool) override {
342  return false;
343  }
344  bool addInt(const int) override {
345  return false;
346  }
347  bool addRange(int, int) override {
348  return false;
349  }
350  bool addDouble(const double) override {
351  return false;
352  }
353  bool addString(const std::string &) override {
354  return false;
355  }
356  bool addStruct(const std::string & /*structName*/, TLPBuilder *&newBuilder) override {
357  newBuilder = new TLPFalse();
358  return false;
359  }
360  bool close() override {
361  return true;
362  }
363 };
364 //=====================================================================================
365 template <bool displayComment>
366 struct TLPParser {
367  std::list<TLPBuilder *> builderStack;
368  std::istream &inputStream;
369  TLPTokenParser *tokenParser;
370  PluginProgress *pluginProgress;
371  int fileSize, curPos;
372 
373  TLPParser(std::istream &inputStream, TLPBuilder *builder, PluginProgress *pluginProgress,
374  int size)
375  : inputStream(inputStream), pluginProgress(pluginProgress), fileSize(size), curPos(0) {
376  builderStack.push_front(builder);
377  }
378 
379  ~TLPParser() {
380  while (!builderStack.empty()) {
381  TLPBuilder *builder = builderStack.front();
382  builderStack.pop_front();
383 
384  if (builderStack.empty() || builder != builderStack.front())
385  delete builder;
386  }
387  }
388 
389  bool formatError(const std::string &value) {
390  std::stringstream ess;
391  ess << "Error when parsing '" << value.c_str() << "' at line " << tokenParser->curLine + 1;
392 
393  if (errno)
394  ess << std::endl << strerror(errno);
395 
396  pluginProgress->setError(ess.str());
397  return false;
398  }
399 
400  bool parse() {
401  TLPTokenParser tParser(inputStream);
402  tokenParser = &tParser;
403  TLPToken currentToken;
404  TLPValue currentValue;
405 
406  while ((currentToken = tokenParser->nextToken(currentValue, curPos)) != ENDOFSTREAM) {
407  if (curPos % 2000 == 1)
408  if (pluginProgress->progress(curPos, fileSize) != TLP_CONTINUE)
409  return pluginProgress->state() != TLP_CANCEL;
410 
411  switch (currentToken) {
412  case OPENTOKEN:
413  currentToken = tokenParser->nextToken(currentValue, curPos);
414 
415  if (currentToken != STRINGTOKEN)
416  return formatError(currentValue.str); // we can throw an exeption
417 
418  TLPBuilder *newBuilder;
419 
420  if (builderStack.front()->addStruct(currentValue.str, newBuilder)) {
421  builderStack.push_front(newBuilder);
422 
423  if (newBuilder->canRead())
424  if (!newBuilder->read(inputStream))
425  return formatError(currentValue.str);
426  } else
427  return formatError(currentValue.str);
428 
429  break;
430 
431  case BOOLTOKEN:
432 
433  if (!builderStack.front()->addBool(currentValue.boolean))
434  return formatError(currentValue.str);
435 
436  break;
437 
438  case INTTOKEN:
439 
440  if (!builderStack.front()->addInt(currentValue.integer))
441  return formatError(currentValue.str);
442 
443  break;
444 
445  case RANGETOKEN:
446 
447  if (!builderStack.front()->addRange(currentValue.range.first, currentValue.range.second))
448  return formatError(currentValue.str);
449 
450  break;
451 
452  case DOUBLETOKEN:
453 
454  if (!builderStack.front()->addDouble(currentValue.real))
455  return formatError(currentValue.str);
456 
457  break;
458 
459  case STRINGTOKEN:
460 
461  if (!builderStack.front()->addString(currentValue.str))
462  return formatError(currentValue.str);
463 
464  break;
465 
466  case CLOSETOKEN:
467 
468  if (builderStack.front()->close()) {
469  TLPBuilder *builder = builderStack.front();
470  builderStack.pop_front();
471 
472  if (builder != builderStack.front())
473  delete builder;
474  } else
475  return formatError(currentValue.str);
476 
477  break;
478 
479  case ERRORINFILE:
480  return formatError(currentValue.str);
481 
482  case ENDOFSTREAM:
483  return true;
484 
485  case COMMENTTOKEN:
486 
487  if (displayComment)
488  tlp::debug() << "Comment line:" << tokenParser->curLine << "->" << currentValue.str;
489 
490  break;
491 
492  default:
493  break;
494  }
495  }
496 
497  if (pluginProgress)
498  pluginProgress->progress(fileSize, fileSize);
499 
500  return true;
501  }
502 };
503 }
504 //=====================================================================================
505 #endif // TLPPARSER_H
506 
507 ///@endcond