pelib  2.0.0
include/pelib/AlgebraDataParser.hpp
Go to the documentation of this file.
00001 /*
00002  Copyright 2015 Nicolas Melot
00003 
00004  This file is part of Pelib.
00005 
00006  Pelib is free software: you can redistribute it and/or modify
00007  it under the terms of the GNU General Public License as published by
00008  the Free Software Foundation, either version 3 of the License, or
00009  (at your option) any later version.
00010 
00011  Pelib is distributed in the hope that it will be useful,
00012  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  GNU General Public License for more details.
00015 
00016  You should have received a copy of the GNU General Public License
00017  along with Pelib. If not, see <http://www.gnu.org/licenses/>.
00018 */
00019 
00020 
00021 #include <iostream>
00022 #include <cstdlib>
00023 #include <fstream>
00024 #include <sstream>
00025 #include <string>
00026 #include <boost/regex.hpp>
00027 #include <iomanip>
00028 #include <boost/algorithm/string.hpp>
00029 
00030 #include <pelib/ParseException.hpp>
00031 #include <pelib/NoDecimalFloatException.hpp>
00032 #include <pelib/Algebra.hpp>
00033 #include <pelib/AlgebraData.hpp>
00034 #include <pelib/DataParser.hpp>
00035 
00036 #ifndef PELIB_ALGEBRADATAPARSER
00037 #define PELIB_ALGEBRADATAPARSER
00038 
00039 namespace pelib
00040 {
00042         class AlgebraDataParser : public DataParser
00043         {
00044                 public:
00046                         template <class Target>
00047                         static
00048                         Target
00049                         convert(std::string element, bool strict = 0)
00050                         {
00051                                 Target out;
00052                                 bool infinity, positive;
00053 
00054                                 if(is_integer(typeid(out)) || is_decimal(typeid(out)))
00055                                 {
00056                                         boost::algorithm::to_lower(element);
00057                                         if(element.compare("+inf") == 0 || element.compare("inf") == 0)
00058                                         {
00059                                                 out = std::numeric_limits<Target>::max();
00060                                                 infinity = true;
00061                                                 positive = true;
00062                                         }
00063                                         else
00064                                         {
00065                                                 if(element.compare("-inf") == 0)
00066                                                 {
00067                                                         out = std::numeric_limits<Target>::min();
00068                                                         infinity = true;
00069                                                         positive = false;
00070                                                 }
00071                                                 else
00072                                                 {
00073                                                         std::istringstream converter(element);
00074                                                         converter >> out;
00075                                                         infinity = false;
00076 
00077                                                         if(converter.fail())
00078                                                         {
00079                                                                 throw ParseException(std::string("Couln't convert literal \"").append(element).append("\" into type \"").append(typeid(out).name()).append("\"."));
00080                                                         }
00081 
00082                                                         std::stringstream ss;
00083                                                         ss << out;
00084                                                         double val;
00085                                                         ss >> val;
00086 
00087                                                         positive = val >= 0;
00088                                                 }
00089                                         }
00090                                 }
00091                                 else
00092                                 {
00093                                         std::istringstream converter(element);
00094                                         converter >> out;
00095 
00096                                         if(converter.fail())
00097                                         {
00098                                                 throw ParseException(std::string("Couln't convert literal \"").append(element).append("\" into type \"").append(typeid(out).name()).append("\"."));
00099                                         }
00100                                 
00101                                         return out;     
00102                                 }
00103 
00104 #if TRACE
00105                                 std::cerr << "String element: \"" << element << "\"" << std::endl;
00106                                 std::cerr << "Strict checking: \"" << strict << "\"" << std::endl;
00107                                 std::cerr << "Let us proceed with optional checking" << std::endl;
00108 #endif
00109                                 if(strict)
00110                                 {
00111 #if TRACE
00112                                         std::cerr << "OK so we have to check" << std::endl;
00113 #endif
00114                                         // We asked a floating-point conversion, but provided a integer
00115                                         //if(strcmp(typeid(out).name(), "f") == 0 || strcmp(typeid(out).name(), "d") == 0)
00116                                         if(is_decimal(typeid(out)) && ! is_integer(typeid(out)))
00117                                         {
00118 #if TRACE
00119                                                 std::cerr << "So we are asking for a decimal conversion" << std::endl;
00120 #endif
00121                                                 // let's try to parse against a fixed-point value
00122                                                 //match("[-+]?\\d+\\.\\d+", element);
00123                                                 boost::cmatch match;
00124                                                 if(!boost::regex_match(element.c_str(), match, boost::regex("[-+]?\\d+\\.\\d+")))
00125                                                 {
00126 #if TRACE
00127                                                         // This should bin in the else clause
00128                                                         //std::cerr << "\"" << element << "\" passed the fixed-point format matching" << std::endl;
00129 #endif
00130                                                         // OK so it doesn't parse a fixed-point notation
00131                                                         // Then I suppose it was a scientific notation; let's see if it indeed denotes a decimal digit
00132                                                         double val;
00133                                                         std::stringstream ss;
00134                                                         ss << out;
00135                                                         ss >> val;
00136                                                         long long int int_test = (long long int)floor(val);
00137                                                         //std::istringstream converter(element);
00138                                                         //converter >> int_test;
00139 
00140 #if TRACE
00141                                                         std::cerr << "\"" << element << "\" is not at fixed-point format, may be scientific notation" << std::endl;
00142 #endif
00143 
00144                                                         if(int_test == val && !infinity)
00145                                                         {
00146                                                                 // Integer-converted and floating-point were equal, then it was an integer, you fool
00147 #if TRACE
00148                                                                 std::cerr << "The decimal part of \"" << element << "\" is nul, therefore we have an integer" << std::endl;
00149 #endif
00150                                                                 throw NoDecimalFloatException(std::string("Asked a decimal conversion, but \"").append(element).append("\" is integer."), val);
00151                                                         }
00152 
00153                                                         if(infinity && std::numeric_limits<Target>::has_infinity)
00154                                                         {
00155                                                                 ss.str("");
00156                                                                 ss.clear(); // Clear state flags.
00157                                                                 ss << std::numeric_limits<Target>::infinity();
00158                                                                 ss >> val;
00159 
00160                                                                 ss.str("");
00161                                                                 ss.clear();
00162                                                                 ss << (positive ? val : -1 * val);
00163                                                                 ss >> out;
00164                                                         }
00165 #if TRACE
00166                                                         std::cerr << "Passed the scientific format matching" << std::endl;
00167 #endif
00168                                                 }
00169 #if TRACE
00170                                                 std::cerr << "OK I valid this conversion" << std::endl;
00171 #endif
00172                                         }
00173                                                 
00174                                         // We asked an integer conversion, but provided a float
00175                                         //if(strcmp(typeid(out).name(), "f") != 0 && strcmp(typeid(out).name(), "d") != 0)
00176                                         if(! is_decimal(typeid(out)) && is_integer(typeid(out)))
00177                                         {
00178 #if TRACE
00179                                                 std::cerr << "So we are asking for a integer conversion" << std::endl;
00180 #endif
00181                                                 // let's try to parse against a fixed-point value
00182                                                 //match("[+-]?\\d+\\.\\d+", element);
00183                                                 boost::cmatch match;
00184                                                 if(!boost::regex_match(element.c_str(), match, boost::regex("[+-]?\\d+\\.\\d+")))
00185                                                 {
00186 #if TRACE
00187                                                         std::cerr << "It is not fixed-point format, good" << std::endl;
00188 #endif
00189                                                         // Then I suppose it was a scientific notation; let's see if it indeed denotes an integer digit
00190                                                         if(!infinity)
00191                                                         {
00192                                                                 double int_test;
00193                                                                 std::istringstream converter(element);
00194                                                                 converter >> int_test;
00195 
00196                                                                 long long int val;
00197                                                                 std::stringstream ss;
00198                                                                 ss << out;
00199                                                                 ss >> val;
00200 
00201                                                                 if(int_test != val)
00202                                                                 {
00203 #if TRACE
00204                                                                         std::cerr << "But this is not an integer anyway" << std::endl;
00205 #endif
00206                                                                         // Integer-converted and floating-point were equal, then it was an integer, you fool
00207                                                                         throw ParseException(std::string("Asked an integer conversion, but \"").append(element).append("\" is decimal."));
00208                                                                 }
00209 #if TRACE
00210                                                                 std::cerr << "OK I valid this conversion" << std::endl;
00211 #endif
00212                                                                 return out;
00213                                                         }
00214                                                         else
00215                                                         {
00216                                                                 if(infinity && std::numeric_limits<Target>::has_infinity)
00217                                                                 {
00218                                                                         long long int val;
00219                                                                         std::stringstream ss;
00220                                                                         ss << std::numeric_limits<Target>::infinity();
00221                                                                         ss >> val;
00222         
00223                                                                         ss.str("");
00224                                                                         ss.clear();
00225                                                                         ss << (positive ? val : -1 * val);
00226                                                                         ss >> out;
00227         
00228                                                                         return out;
00229                                                                 }
00230                                                                 else
00231                                                                 {
00232                                                                         return out;
00233                                                                 }
00234                                                         }
00235                                                 }
00236 #if TRACE
00237                                                 std::cerr << "But element was fixed-point format" << std::endl;
00238 #endif
00239                                                 throw ParseException(std::string("Asked an integer conversion, but \"").append(element).append("\" is decimal."));
00240                                         }
00241                                 }
00242                                 else
00243                                 {
00244                                         if(infinity && std::numeric_limits<Target>::has_infinity)
00245                                         {
00246                                                 std::stringstream ss;
00247                                         
00248                                                 if(is_decimal(typeid(out)))
00249                                                 {
00250                                                         long double val;
00251                                                         ss << std::numeric_limits<Target>::infinity();
00252                                                         ss >> val;
00253                                                         ss.str("");
00254                                                         ss.clear();
00255                                                         ss << (positive ? val : -1 * val);
00256                                                 }
00257                                                 else
00258                                                 {
00259                                                         long long int val;
00260                                                         ss << std::numeric_limits<Target>::infinity();
00261                                                         ss >> val;
00262                                                         ss.str("");
00263                                                         ss.clear();
00264                                                         ss << (positive ? val : -1 * val);
00265                                                 }
00266 
00267                                                 ss >> out;
00268                                                 return out;
00269                                         }
00270                                                 else
00271                                         {
00272                                                 return out;
00273                                         }
00274                                 }
00275 
00276                                 return out;
00277                         }
00278 
00279                         // This is causing too much problem, probably due to a bug in boost::regex, even when inlining
00280                         // Inline manually instead
00281 #if 0
00282 
00283                         static inline
00284                         boost::cmatch
00285                         match(std::string regex, std::string str);
00286 #endif
00287 
00289                         virtual
00290                         std::string
00291                         getDetailedPattern() = 0;
00292 
00294                         virtual
00295                         std::string
00296                         getGlobalPattern() = 0;
00297 
00299                         virtual
00300                         AlgebraData*
00301                         parse(std::istream &in) = 0;
00302                 protected:
00303                         static bool is_decimal(const std::type_info &var)
00304                         {
00305                                 return (std::string(var.name()).compare(std::string(typeid(float).name())) == 0) ||
00306                                         (std::string(var.name()).compare(std::string(typeid(double).name())) == 0) ||
00307                                         (std::string(var.name()).compare(std::string(typeid(long double).name())) == 0);
00308                         }
00309 
00310                         static bool is_integer(const std::type_info &var)
00311                         {
00312                                 /*
00313                                         List obtained by running the following C program
00314                                         #include <iostream>
00315                                         #include <cstdlib>
00316                                         #include <typeinfo>
00317 
00318                                         using namespace std;
00319 
00320                                         #define show(type) cout << #type << " " << typeid(type).name() << endl;
00321 
00322                                         int
00323                                         main(int argc, char **argv)
00324                                         {
00325                                                 show(int);
00326                                                 show(unsigned int);
00327                                                 show(long int);
00328                                                 show(long unsigned int);
00329                                                 show(long long int);
00330                                                 show(long long unsigned int);
00331                                                 cout << endl;
00332                                                 show(size_t);
00333                                                 show(ptrdiff_t);
00334                                                 show(signed size_t);
00335                                                 show(unsigned ptrdiff_t);
00336                                                 show(long size_t);
00337                                                 show(long long size_t);
00338                                                 show(long ptrdiff_t);
00339                                                 show(long long ptrdiff_t);
00340                                                 cout << endl;
00341                                                 show(float);
00342                                                 show(double);
00343                                                 show(long double);
00344                                                 return EXIT_SUCCESS;
00345                                         }
00346 
00347                                         ./test | rev | sort -k 1 | uniq -w 1 | cut -f 1 -d ' ' --complement | rev
00348                                 */
00349 
00350                                 return (std::string(var.name()).compare(std::string(typeid(int).name())) == 0) ||
00351                                         (std::string(var.name()).compare(std::string(typeid(unsigned int).name())) == 0) ||
00352                                         (std::string(var.name()).compare(std::string(typeid(long size_t).name())) == 0) ||
00353                                         (std::string(var.name()).compare(std::string(typeid(size_t).name())) == 0) ||
00354                                         (std::string(var.name()).compare(std::string(typeid(long long size_t).name())) == 0) ||
00355                                         (std::string(var.name()).compare(std::string(typeid(long long unsigned int).name())) == 0);
00356                         }                       
00357                 private:
00358                         // Nothing private
00359         };
00360 }
00361 
00362 #endif