Tulip  4.6.0
Better Visualization Through Research
library/tulip-core/include/tulip/TLPParser.h
00001 /*
00002  *
00003  * This file is part of Tulip (www.tulip-software.org)
00004  *
00005  * Authors: David Auber and the Tulip development Team
00006  * from LaBRI, University of Bordeaux
00007  *
00008  * Tulip is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU Lesser General Public License
00010  * as published by the Free Software Foundation, either version 3
00011  * of the License, or (at your option) any later version.
00012  *
00013  * Tulip is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00016  * See the GNU General Public License for more details.
00017  *
00018  */
00019 ///@cond DOXYGEN_HIDDEN
00020 
00021 #ifndef DOXYGEN_NOTFOR_DEVEL
00022 
00023 #include <sstream>
00024 #include <string>
00025 #include <list>
00026 
00027 
00028 namespace tlp {
00029 
00030 class PluginProgress;
00031 
00032 struct ParserError {
00033   ParserError(int err=0,int lin=0,int cha=0):errorNumber(err),lineInFile(lin),charInLine(cha) {}
00034   int errorNumber;
00035   int lineInFile;
00036   int charInLine;
00037 };
00038 
00039 struct TLPValue {
00040   std::string str;
00041   long integer;
00042   double real;
00043   bool boolean;
00044   std::pair<long, long> range;
00045 };
00046 
00047 enum TLPToken { BOOLTOKEN,ENDOFSTREAM,STRINGTOKEN,INTTOKEN,DOUBLETOKEN,IDTOKEN,ERRORINFILE,OPENTOKEN,CLOSETOKEN,COMMENTTOKEN,RANGETOKEN};
00048 //=====================================================================================
00049 struct TLPTokenParser {
00050   int curLine;
00051   int curChar;
00052   std::istream &is;
00053   TLPTokenParser(std::istream &i):curLine(0),curChar(0),is(i) {}
00054   TLPToken nextToken(TLPValue &val,int &curPos) {
00055     val.str.erase();
00056     bool endOfStream=false,strGet=false,slashMode=false,started=false,stop=false,strComment=false;
00057     char ch;
00058 
00059     while ( (!stop) && (endOfStream=!(is.get(ch).fail()))) {
00060       curPos++;
00061       curChar++;
00062 
00063       if (strGet)
00064         switch (ch) {
00065         case 13 :
00066         case '\n':
00067           curChar=0;
00068           curLine++;
00069           val.str+=ch;
00070           break;
00071 
00072         case '\t':
00073           val.str+="    ";
00074           break;
00075 
00076         case '\\':
00077 
00078           if (!slashMode) {
00079             slashMode=true;
00080           }
00081           else {
00082             val.str+=ch;
00083             slashMode=false;
00084           }
00085 
00086           break;
00087 
00088         case '"':
00089 
00090           if (!slashMode)  {
00091             return STRINGTOKEN;
00092           }
00093           else {
00094             val.str+=ch;
00095             slashMode=false;
00096           }
00097 
00098           break;
00099 
00100         case 'n':
00101 
00102           if (slashMode)   {
00103             val.str+='\n';
00104             slashMode=false;
00105             break;
00106           }
00107 
00108         default:
00109 
00110           if (!slashMode)
00111             val.str+=ch;
00112 
00113           slashMode=false;
00114           break;
00115         }
00116       else if (strComment)
00117         switch (ch) {
00118         case 13 :
00119         case '\n':
00120           curChar=0;
00121           curLine++;
00122           stop=true;
00123           return COMMENTTOKEN;
00124           break;
00125 
00126         default:
00127           curChar++;
00128           val.str+=ch;
00129           break;
00130         }
00131       else
00132         switch (ch) {
00133         case ' ':
00134         case '\t':
00135 
00136           if (started) stop=true;
00137 
00138           break;
00139 
00140         case 13 :
00141         case '\n':
00142           curChar=0;
00143           curLine++;
00144 
00145           if (started) stop=true;
00146 
00147           break;
00148 
00149         case '(':
00150 
00151           if (!started) return OPENTOKEN;
00152           else {
00153             --curPos,--curChar;
00154             is.unget();
00155             stop=true;
00156           }
00157 
00158           break;
00159 
00160         case ')':
00161 
00162           if (!started) return CLOSETOKEN;
00163           else {
00164             --curPos,--curChar;
00165             is.unget();
00166             stop=true;
00167           }
00168 
00169           break;
00170 
00171         case '"':
00172           strGet=true;
00173 
00174           if (started) {
00175             --curPos,--curChar;
00176             is.unget();
00177             stop=true;
00178           }
00179           else started=true;
00180 
00181           break;
00182 
00183         case ';':
00184           strComment=true;
00185 
00186           if (started) {
00187             --curPos,--curChar;
00188             is.unget();
00189             stop=true;
00190           }
00191           else started=true;
00192 
00193           break;
00194 
00195         default :
00196           val.str+=ch;
00197           started=true;
00198           break;
00199         }
00200     }
00201 
00202     if (!started && !endOfStream) return ENDOFSTREAM;
00203 
00204     char *endPtr=NULL;
00205     const char *cstr = val.str.c_str();
00206     errno = 0;
00207     long resultl= strtol(cstr, &endPtr, 10);
00208 
00209     if (errno == ERANGE)
00210       return ERRORINFILE;
00211 
00212     unsigned long strlength = val.str.length();
00213 
00214     if (endPtr==(cstr+ strlength)) {
00215       val.integer=resultl;
00216       return INTTOKEN;
00217     }
00218 
00219     // check for a range
00220     if (endPtr > cstr && (cstr + strlength) > (endPtr + 2)) {
00221       val.range.first = resultl;
00222 
00223       if ((endPtr[0] == '.') && (endPtr[1] == '.')) {
00224         char* beginPtr = endPtr + 2;
00225         errno = 0;
00226         resultl = strtol(beginPtr, &endPtr, 10);
00227 
00228         if (errno == ERANGE)
00229           return ERRORINFILE;
00230 
00231         if (endPtr == (cstr + strlength)) {
00232           if (resultl < val.range.first)
00233             return ERRORINFILE;
00234 
00235           val.range.second = resultl;
00236           return RANGETOKEN;
00237         }
00238       }
00239     }
00240 
00241     endPtr=NULL;
00242 
00243     double resultd=strtod(cstr, &endPtr);
00244 
00245     if (errno == ERANGE)
00246       return ERRORINFILE;
00247 
00248     if (endPtr==(cstr + strlength)) {
00249       val.real=resultd;
00250       return DOUBLETOKEN;
00251     }
00252 
00253     if (strcasecmp(cstr, "true")==0) {
00254       val.boolean=true;
00255       return BOOLTOKEN;
00256     }
00257 
00258     if (strcasecmp(cstr, "false")==0) {
00259       val.boolean=false;
00260       return BOOLTOKEN;
00261     }
00262 
00263     if (started) return STRINGTOKEN;
00264 
00265     return ERRORINFILE;
00266   }
00267 };
00268 //=====================================================================================
00269 struct TLPBuilder {
00270   virtual ~TLPBuilder() {}
00271   virtual bool addBool(const bool)=0;
00272   virtual bool addInt(const int)=0;
00273   virtual bool addRange(int, int)=0;
00274   virtual bool addDouble(const double)=0;
00275   virtual bool addString(const std::string &)=0;
00276   virtual bool addStruct(const std::string&,TLPBuilder*&)=0;
00277   virtual bool close() =0;
00278   virtual bool canRead() {
00279     return false;
00280   }
00281   virtual bool read(std::istream&) {
00282     return false;
00283   }
00284 };
00285 
00286 struct TLPTrue:public TLPBuilder {
00287   bool addBool(const bool) {
00288     return true;
00289   }
00290   bool addInt(const int) {
00291     return true;
00292   }
00293   bool addRange(int, int) {
00294     return true;
00295   }
00296   bool addDouble(const double) {
00297     return true;
00298   }
00299   bool addString(const std::string &) {
00300     return true;
00301   }
00302   bool addStruct(const std::string& /*structName*/, TLPBuilder*&newBuilder) {
00303     newBuilder=new TLPTrue();
00304     return true;
00305   }
00306   bool close() {
00307     return true;
00308   }
00309 };
00310 
00311 struct TLPFalse:public TLPBuilder {
00312   bool addBool(const bool) {
00313     return false;
00314   }
00315   bool addInt(const int) {
00316     return false;
00317   }
00318   bool addRange(int, int) {
00319     return false;
00320   }
00321   bool addDouble(const double) {
00322     return false;
00323   }
00324   bool addString(const std::string &) {
00325     return false;
00326   }
00327   bool addStruct(const std::string& /*structName*/, TLPBuilder*&newBuilder) {
00328     newBuilder=new TLPFalse();
00329     return false;
00330   }
00331   bool close() {
00332     return true;
00333   }
00334 };
00335 //=====================================================================================
00336 template <bool displayComment>
00337 struct TLPParser {
00338   std::list<TLPBuilder *> builderStack;
00339   std::istream &inputStream;
00340   TLPTokenParser *tokenParser;
00341   PluginProgress *pluginProgress;
00342   int fileSize,curPos;
00343 
00344   TLPParser(std::istream &inputStream,TLPBuilder *builder,PluginProgress *pluginProgress,int size): inputStream(inputStream),
00345     pluginProgress(pluginProgress),
00346     fileSize(size),
00347     curPos(0) {
00348     builderStack.push_front(builder);
00349   }
00350 
00351   ~TLPParser() {
00352     while (!builderStack.empty()) {
00353       TLPBuilder *builder = builderStack.front();
00354       builderStack.pop_front();
00355 
00356       if (builderStack.empty() || builder != builderStack.front())
00357         delete builder;
00358     }
00359   }
00360 
00361   bool formatError() {
00362     std::stringstream ess;
00363     ess << "Error when parsing char " << tokenParser->curChar
00364         << " at line " << tokenParser->curLine + 1;
00365 
00366     if (errno)
00367       ess << std::endl << strerror(errno);
00368 
00369     pluginProgress->setError(ess.str());
00370     return false;
00371   }
00372 
00373   bool parse() {
00374     TLPTokenParser tParser(inputStream);
00375     tokenParser= &tParser;
00376     TLPToken currentToken;
00377     TLPValue currentValue;
00378 
00379     while ((currentToken=tokenParser->nextToken(currentValue,curPos))!=ENDOFSTREAM) {
00380       if (curPos%2000==1) if (pluginProgress->progress(curPos,fileSize)!=TLP_CONTINUE)
00381           return pluginProgress->state()!=TLP_CANCEL;
00382 
00383       switch (currentToken) {
00384       case OPENTOKEN:
00385         currentToken=tokenParser->nextToken(currentValue,curPos);
00386 
00387         if (currentToken!=STRINGTOKEN)
00388           return formatError();//we can throw an exeption
00389 
00390         TLPBuilder *newBuilder;
00391 
00392         if (builderStack.front()->addStruct(currentValue.str, newBuilder)) {
00393           builderStack.push_front(newBuilder);
00394 
00395           if (newBuilder->canRead())
00396             if (!newBuilder->read(inputStream))
00397               return formatError();
00398         }
00399         else
00400           return formatError();
00401 
00402         break;
00403 
00404       case BOOLTOKEN:
00405 
00406         if (!builderStack.front()->addBool(currentValue.boolean))
00407           return formatError();
00408 
00409         break;
00410 
00411       case INTTOKEN:
00412 
00413         if (!builderStack.front()->addInt(currentValue.integer))
00414           return formatError();
00415 
00416         break;
00417 
00418       case RANGETOKEN:
00419 
00420         if (!builderStack.front()->addRange(currentValue.range.first,
00421                                             currentValue.range.second))
00422           return formatError();
00423 
00424         break;
00425 
00426       case DOUBLETOKEN:
00427 
00428         if (!builderStack.front()->addDouble(currentValue.real))
00429           return formatError();
00430 
00431         break;
00432 
00433       case STRINGTOKEN:
00434 
00435         if (!builderStack.front()->addString(currentValue.str))
00436           return formatError();
00437 
00438         break;
00439 
00440       case CLOSETOKEN:
00441 
00442         if (builderStack.front()->close()) {
00443           TLPBuilder * builder = builderStack.front();
00444           builderStack.pop_front();
00445 
00446           if (builder != builderStack.front())
00447             delete builder;
00448         }
00449         else
00450           return formatError();
00451 
00452         break;
00453 
00454       case ERRORINFILE:
00455         return formatError();
00456 
00457       case ENDOFSTREAM:
00458         return true;
00459 
00460       case COMMENTTOKEN:
00461 
00462         if (displayComment) tlp::debug() << "Comment line:" << tokenParser->curLine << "->" <<  currentValue.str;
00463 
00464         break;
00465 
00466       default:
00467         break;
00468       }
00469     }
00470 
00471     if (pluginProgress)
00472       pluginProgress->progress(fileSize,fileSize);
00473 
00474     return true;
00475   }
00476 };
00477 
00478 }
00479 //=====================================================================================
00480 
00481 #endif // DOXYGEN_NOTFOR_DEVEL
00482 ///@endcond
 All Classes Files Functions Variables Enumerations Enumerator Properties