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