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