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