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