Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members

colvarparse.C

Go to the documentation of this file.
00001 #include <sstream>
00002 #include <iostream>
00003 
00004 
00005 
00006 #include "colvarmodule.h"
00007 #include "colvarvalue.h"
00008 #include "colvarparse.h"
00009 
00010 
00011 
00012 // space & tab
00013 std::string const colvarparse::white_space = " \t";
00014 
00015 std::string colvarparse::dummy_string = "";
00016 size_t      colvarparse::dummy_pos = 0;
00017 
00018 
00019 // definition of single-value keyword parsers
00020 
00021 #define _get_keyval_scalar_string_(TYPE)                                \
00022                                                                         \
00023   bool colvarparse::get_keyval (std::string const &conf,                \
00024                                 char const *key,                        \
00025                                 TYPE &value,                            \
00026                                 TYPE const &def_value,                  \
00027                                 Parse_Mode const parse_mode)            \
00028   {                                                                     \
00029     std::string data;                                                   \
00030     bool b_found = false, b_found_any = false;                          \
00031     size_t save_pos = 0, found_count = 0;                               \
00032                                                                         \
00033     do {                                                                \
00034       std::string data_this = "";                                       \
00035       b_found = key_lookup (conf, key, data_this, save_pos);            \
00036       if (b_found) {                                                    \
00037         if (!b_found_any)                                               \
00038           b_found_any = true;                                           \
00039         found_count++;                                                  \
00040         data = data_this;                                               \
00041       }                                                                 \
00042     } while (b_found);                                                  \
00043                                                                         \
00044     if (found_count > 1)                                                \
00045       cvm::log ("Warning: found more than one instance of \""+          \
00046                 std::string (key)+"\".\n");                             \
00047                                                                         \
00048     if (data.size()) {                                                  \
00049       std::istringstream is (data);                                     \
00050       TYPE x (def_value);                                               \
00051       if (is >> x)                                                      \
00052         value = x;                                                      \
00053       else                                                              \
00054         cvm::fatal_error ("Error: in parsing \""+                       \
00055                           std::string (key)+"\".\n");                   \
00056       if (parse_mode != parse_silent) {                                 \
00057         cvm::log ("# "+std::string (key)+" = "+                         \
00058                   cvm::to_str (value)+"\n");                            \
00059       }                                                                 \
00060     } else {                                                            \
00061                                                                         \
00062       if (b_found_any)                                                  \
00063         cvm::fatal_error ("Error: improper or missing value "           \
00064                           "for \""+std::string (key)+"\".\n");          \
00065       value = def_value;                                                \
00066       if (parse_mode != parse_silent) {                                 \
00067         cvm::log ("# "+std::string (key)+" = \""+                       \
00068                   cvm::to_str (def_value)+"\" [default]\n");            \
00069       }                                                                 \
00070     }                                                                   \
00071                                                                         \
00072     return b_found_any;                                                 \
00073   }
00074 
00075 
00076 #define _get_keyval_scalar_(TYPE)                                       \
00077                                                                         \
00078   bool colvarparse::get_keyval (std::string const &conf,                \
00079                                 char const *key,                        \
00080                                 TYPE &value,                            \
00081                                 TYPE const &def_value,                  \
00082                                 Parse_Mode const parse_mode)            \
00083   {                                                                     \
00084     std::string data;                                                   \
00085     bool b_found = false, b_found_any = false;                          \
00086     size_t save_pos = 0, found_count = 0;                               \
00087                                                                         \
00088     do {                                                                \
00089       std::string data_this = "";                                       \
00090       b_found = key_lookup (conf, key, data_this, save_pos);            \
00091       if (b_found) {                                                    \
00092         if (!b_found_any)                                               \
00093           b_found_any = true;                                           \
00094         found_count++;                                                  \
00095         data = data_this;                                               \
00096       }                                                                 \
00097     } while (b_found);                                                  \
00098                                                                         \
00099     if (found_count > 1)                                                \
00100       cvm::log ("Warning: found more than one instance of \""+          \
00101                 std::string (key)+"\".\n");                             \
00102                                                                         \
00103     if (data.size()) {                                                  \
00104       std::istringstream is (data);                                     \
00105       TYPE x (def_value);                                               \
00106       if (is >> x)                                                      \
00107         value = x;                                                      \
00108       else                                                              \
00109         cvm::fatal_error ("Error: in parsing \""+                       \
00110                           std::string (key)+"\".\n");                   \
00111       if (parse_mode != parse_silent) {                                 \
00112         cvm::log ("# "+std::string (key)+" = "+                         \
00113                   cvm::to_str (value)+"\n");                            \
00114       }                                                                 \
00115     } else {                                                            \
00116                                                                         \
00117       if (b_found_any)                                                  \
00118         cvm::fatal_error ("Error: improper or missing value "           \
00119                           "for \""+std::string (key)+"\".\n");          \
00120       value = def_value;                                                \
00121       if (parse_mode != parse_silent) {                                 \
00122         cvm::log ("# "+std::string (key)+" = "+                         \
00123                   cvm::to_str (def_value)+" [default]\n");              \
00124       }                                                                 \
00125     }                                                                   \
00126                                                                         \
00127     return b_found_any;                                                 \
00128   }
00129 
00130 
00131 // definition of multiple-value keyword parsers
00132 
00133 #define _get_keyval_vector_(TYPE)                                       \
00134                                                                         \
00135   bool colvarparse::get_keyval (std::string const &conf,                \
00136                                 char const *key,                        \
00137                                 std::vector<TYPE> &values,              \
00138                                 std::vector<TYPE> const &def_values,    \
00139                                 Parse_Mode const parse_mode)            \
00140   {                                                                     \
00141     std::string data;                                                   \
00142     bool b_found = false, b_found_any = false;                          \
00143     size_t save_pos = 0, found_count = 0;                               \
00144                                                                         \
00145     do {                                                                \
00146       std::string data_this = "";                                       \
00147       b_found = key_lookup (conf, key, data_this, save_pos);            \
00148       if (b_found) {                                                    \
00149         if (!b_found_any)                                               \
00150           b_found_any = true;                                           \
00151         found_count++;                                                  \
00152         data = data_this;                                               \
00153       }                                                                 \
00154     } while (b_found);                                                  \
00155                                                                         \
00156     if (found_count > 1)                                                \
00157       cvm::log ("Warning: found more than one instance of \""+          \
00158                 std::string (key)+"\".\n");                             \
00159                                                                         \
00160     if (data.size()) {                                                  \
00161       std::istringstream is (data);                                     \
00162                                                                         \
00163       if (values.size() == 0) {                                         \
00164                                                                         \
00165         std::vector<TYPE> x;                                            \
00166         if (def_values.size())                                          \
00167           x = def_values;                                               \
00168         else                                                            \
00169           x.assign (1, TYPE());                                         \
00170                                                                         \
00171         for (size_t i = 0;                                              \
00172              ( is >> x[ ((i<x.size()) ? i : x.size()-1) ] );            \
00173              i++) {                                                     \
00174           values.push_back (x[ ((i<x.size()) ? i : x.size()-1) ]);      \
00175         }                                                               \
00176                                                                         \
00177       } else {                                                          \
00178                                                                         \
00179         size_t i = 0;                                                   \
00180         for ( ; i < values.size(); i++) {                               \
00181           TYPE x (values[i]);                                           \
00182           if (is >> x)                                                  \
00183             values[i] = x;                                              \
00184           else                                                          \
00185             cvm::fatal_error ("Error: in parsing \""+                   \
00186                               std::string (key)+"\".\n");               \
00187         }                                                               \
00188       }                                                                 \
00189                                                                         \
00190       if (parse_mode != parse_silent) {                                 \
00191         cvm::log ("# "+std::string (key)+" = "+                         \
00192                   cvm::to_str (values)+"\n");                           \
00193       }                                                                 \
00194                                                                         \
00195     } else {                                                            \
00196                                                                         \
00197       if (b_found_any)                                                  \
00198         cvm::fatal_error ("Error: improper or missing values for \""+   \
00199                           std::string (key)+"\".\n");                   \
00200                                                                         \
00201       for (size_t i = 0; i < values.size(); i++)                        \
00202         values[i] = def_values[ (i > def_values.size()) ? 0 : i ];      \
00203                                                                         \
00204       if (parse_mode != parse_silent) {                                 \
00205         cvm::log ("# "+std::string (key)+" = "+                         \
00206                   cvm::to_str (def_values)+" [default]\n");             \
00207       }                                                                 \
00208     }                                                                   \
00209                                                                         \
00210     return b_found_any;                                                 \
00211   }
00212 
00213 
00214 // single-value keyword parsers
00215 
00216 _get_keyval_scalar_ (int);
00217 _get_keyval_scalar_ (size_t);
00218 _get_keyval_scalar_string_ (std::string);
00219 _get_keyval_scalar_ (cvm::real);
00220 _get_keyval_scalar_ (cvm::rvector);
00221 _get_keyval_scalar_ (cvm::quaternion);
00222 _get_keyval_scalar_ (colvarvalue);
00223 
00224 
00225 // multiple-value keyword parsers
00226 
00227 _get_keyval_vector_ (int);
00228 _get_keyval_vector_ (size_t);
00229 _get_keyval_vector_ (std::string);
00230 _get_keyval_vector_ (cvm::real);
00231 _get_keyval_vector_ (cvm::rvector);
00232 _get_keyval_vector_ (cvm::quaternion);
00233 _get_keyval_vector_ (colvarvalue);
00234 
00235 
00236 
00237 bool colvarparse::get_keyval (std::string const &conf,        
00238                               char const *key,                
00239                               bool &value,                    
00240                               bool const &def_value,          
00241                               Parse_Mode const parse_mode)    
00242 {
00243   std::string data;
00244   bool b_found = false, b_found_any = false;
00245   size_t save_pos = 0, found_count = 0;
00246 
00247   do {
00248     std::string data_this = "";
00249     b_found = key_lookup (conf, key, data_this, save_pos);
00250     if (b_found) {
00251       if (!b_found_any)
00252         b_found_any = true;
00253       found_count++;
00254       data = data_this;
00255     }
00256   } while (b_found);
00257 
00258   if (found_count > 1)
00259     cvm::log ("Warning: found more than one instance of \""+
00260               std::string (key)+"\".\n");
00261 
00262   if (data.size()) {
00263     std::istringstream is (data);
00264     if ( (data == std::string ("on")) ||
00265          (data == std::string ("yes")) ||
00266          (data == std::string ("true")) ) {
00267       value = true;
00268     } else if ( (data == std::string ("off")) ||
00269               (data == std::string ("no")) ||
00270               (data == std::string ("false")) ) {
00271       value = false;
00272     } else
00273       cvm::fatal_error ("Error: boolean values only are allowed "  
00274                         "for \""+std::string (key)+"\".\n");
00275     if (parse_mode != parse_silent) {
00276       cvm::log ("# "+std::string (key)+" = "+
00277                 (value ? "on" : "off")+"\n");
00278     }
00279   } else {
00280 
00281     if (b_found_any) {
00282       if (parse_mode != parse_silent) {
00283         cvm::log ("# "+std::string (key)+" = on\n");
00284       }
00285       value = true;
00286     } else {
00287       value = def_value;
00288       if (parse_mode != parse_silent) {
00289         cvm::log ("# "+std::string (key)+" = "+
00290                   (def_value ? "on" : "off")+" [default]\n");
00291       }
00292     }
00293   }
00294 
00295   return b_found_any;
00296 }
00297 
00298 
00299 void colvarparse::add_keyword (char const *key)
00300 {
00301   for (std::list<std::string>::iterator ki = allowed_keywords.begin();
00302        ki != allowed_keywords.end(); ki++) {
00303     if (to_lower_cppstr (std::string (key)) == *ki)
00304       return;
00305   }
00306   // not found in the list
00307   //   if (cvm::debug())
00308   //     cvm::log ("Registering a new keyword, \""+std::string (key)+"\".\n");
00309   allowed_keywords.push_back (to_lower_cppstr (std::string (key)));
00310 }
00311 
00312 
00313 void colvarparse::strip_values (std::string &conf)
00314 {
00315   size_t offset = 0;
00316   data_begin_pos.sort();
00317   data_end_pos.sort();
00318 
00319   std::list<size_t>::iterator data_begin = data_begin_pos.begin();
00320   std::list<size_t>::iterator data_end   = data_end_pos.begin();
00321 
00322   for ( ; (data_begin != data_begin_pos.end()) &&
00323           (data_end   != data_end_pos.end()) ;
00324         data_begin++, data_end++) {
00325 
00326     //     std::cerr << "data_begin, data_end "
00327     //               << *data_begin << ", " << *data_end
00328     //               << "\n";
00329 
00330     size_t const nchars = *data_end-*data_begin;
00331 
00332     //     std::cerr << "conf[data_begin:data_end] = \""
00333     //               << std::string (conf, *data_begin - offset, nchars)
00334     //               << "\"\n";
00335 
00336     conf.erase (*data_begin - offset, nchars);
00337     offset += nchars;
00338 
00339     //     std::cerr << ("Stripped config = \"\n"+conf+"\"\n");
00340 
00341   }
00342 }
00343 
00344 
00345 void colvarparse::check_keywords (std::string &conf, char const *key)
00346 {
00347   if (cvm::debug())
00348     cvm::log ("Configuration string for \""+std::string (key)+
00349               "\": \"\n"+conf+"\".\n");
00350 
00351   strip_values (conf);
00352   // after stripping, the config string has either empty lines, or
00353   // lines beginning with a keyword
00354 
00355   std::string line;
00356   std::istringstream is (conf);
00357   while (std::getline (is, line)) {
00358     if (line.size() == 0)
00359       continue;
00360     if (line.find_first_not_of (white_space) ==
00361         std::string::npos)
00362       continue;
00363 
00364     std::string uk;
00365     std::istringstream line_is (line);
00366     line_is >> uk;
00367     if (cvm::debug())
00368       cvm::log ("Checking the validity of \""+uk+"\" from line:\n" + line);
00369     uk = to_lower_cppstr (uk);
00370 
00371     bool found_keyword = false;
00372     for (std::list<std::string>::iterator ki = allowed_keywords.begin();
00373          ki != allowed_keywords.end(); ki++) {
00374       if (uk == *ki) {
00375         found_keyword = true;
00376         break;
00377       }
00378     }
00379     if (!found_keyword)
00380       cvm::fatal_error ("Error: keyword \""+uk+"\" is not supported, "
00381                         "or not recognized in this context.\n");
00382   }
00383 }
00384 
00385 
00386 std::istream & colvarparse::getline_nocomments (std::istream &is,
00387                                                 std::string &line,
00388                                                 char const delim)
00389 {
00390   std::getline (is, line, delim);
00391   size_t const comment = line.find ('#');
00392   if (comment != std::string::npos) {
00393     line.erase (comment);
00394   }
00395   return is;
00396 }
00397 
00398 
00399 bool colvarparse::key_lookup (std::string const &conf,
00400                               char const *key_in,
00401                               std::string &data,
00402                               size_t &save_pos)
00403 {
00404   // add this keyword to the register (in its camelCase version)
00405   add_keyword (key_in);
00406 
00407   // use the lowercase version from now on
00408   std::string const key (to_lower_cppstr (key_in));
00409 
00410   // "conf_lower" is only used to lookup the keyword, but its value
00411   // will be read from "conf", in order not to mess up file names
00412   std::string const conf_lower (to_lower_cppstr (conf));
00413 
00414   // by default, there is no value, unless we found one
00415   data = "";
00416 
00417   // when the function is invoked without save_pos, ensure that we
00418   // start from zero
00419   colvarparse::dummy_pos = 0;
00420 
00421   // start from the first occurrence of key
00422   size_t pos = conf_lower.find (key, save_pos); 
00423   
00424   // iterate over all instances until it finds the isolated keyword
00425   while (true) {
00426 
00427     if (pos == std::string::npos) {
00428       // no valid instance of the keyword has been found
00429       return false;
00430     }
00431 
00432     bool b_isolated_left = true, b_isolated_right = true;
00433 
00434     if (pos > 0) {
00435       if ( std::string ("\n"+white_space+
00436                         "}").find (conf[pos-1]) ==
00437            std::string::npos ) {
00438         // none of the valid delimiting characters is on the left of key 
00439         b_isolated_left = false;
00440       }
00441     }
00442 
00443     if (pos < conf.size()-key.size()-1) {
00444       if ( std::string ("\n"+white_space+
00445                         "{").find (conf[pos+key.size()]) ==
00446            std::string::npos ) {
00447         // none of the valid delimiting characters is on the right of key 
00448         b_isolated_right = false;
00449       }
00450     }
00451 
00452     // check that there are matching braces between here and the end of conf
00453     bool const b_not_within_block = brace_check (conf, pos);
00454 
00455     bool const b_isolated = (b_isolated_left && b_isolated_right &&
00456                              b_not_within_block);
00457     
00458     if (b_isolated) {
00459       // found it
00460       break;
00461     } else {
00462       // try the next occurrence of key
00463       pos = conf_lower.find (key, pos+key.size());
00464     }
00465   }
00466 
00467   // check it is not between quotes
00468   //   if ( (conf.find_last_of  ("\"",
00469   //                             conf.find_last_of  (white_space, pos)) !=
00470   //         std::string::npos) &&
00471   //        (conf.find_first_of ("\"",
00472   //                             conf.find_first_of (white_space, pos)) !=
00473   //         std::string::npos) )
00474   //     return false;
00475 
00476 
00477   // save the pointer for a future call (when iterating over multiple
00478   // valid instances of the same keyword)
00479   save_pos = pos + key.size();
00480 
00481   // get the remainder of the line
00482   size_t pl = conf.rfind ("\n", pos);
00483   size_t line_begin = (pl == std::string::npos) ? 0 : pos;
00484   size_t nl = conf.find  ("\n", pos);
00485   size_t line_end = (nl == std::string::npos) ? conf.size() : nl;
00486   std::string line (conf, line_begin, (line_end-line_begin)); 
00487 
00488   size_t data_begin = (to_lower_cppstr (line)).find (key) + key.size();
00489   data_begin = line.find_first_not_of (white_space, data_begin+1);
00490 
00491   //   size_t data_begin_absolute = data_begin + line_begin;
00492   //   size_t data_end_absolute   = data_begin;
00493 
00494   if (data_begin != std::string::npos) {
00495 
00496     size_t data_end = line.find_last_not_of (white_space) + 1;
00497     data_end = (data_end == std::string::npos) ? line.size() : data_end;
00498     //     data_end_absolute = data_end + line_begin;
00499 
00500     if (line.find ('{', data_begin) != std::string::npos) {
00501 
00502       size_t brace_count = 1;
00503       size_t brace = line.find ('{', data_begin);  // start from the first opening brace
00504 
00505       while (brace_count > 0) {
00506 
00507         // find the matching closing brace
00508         brace = line.find_first_of ("{}", brace+1);
00509         while (brace == std::string::npos) {
00510           // add a new line
00511           if (line_end >= conf.size()) {
00512             cvm::fatal_error ("Parse error: reached the end while "
00513                               "looking for closing brace; until now "
00514                               "the following was parsed: \"\n"+
00515                               line+"\".\n");
00516             return false;
00517           }
00518           size_t const old_end = line.size();
00519           //           data_end_absolute += old_end+1;
00520 
00521           line_begin = line_end;
00522           nl = conf.find ('\n', line_begin+1);
00523           if (nl == std::string::npos) 
00524             line_end = conf.size();
00525           else 
00526             line_end = nl;
00527           line.append (conf, line_begin, (line_end-line_begin));
00528 
00529           brace = line.find_first_of ("{}", old_end);
00530         }
00531 
00532         if (line[brace] == '{') brace_count++;
00533         if (line[brace] == '}') brace_count--;
00534       }
00535 
00536       // set data_begin afterthe opening brace
00537       data_begin = line.find_first_of ('{', data_begin) + 1;
00538       data_begin = line.find_first_not_of (white_space,
00539                                            data_begin);
00540       // set data_end before the closing brace
00541       data_end = brace;
00542       data_end = line.find_last_not_of (white_space+"}",
00543                                         data_end) + 1;
00544       //       data_end_absolute = line_end;
00545 
00546       if (data_end > line.size())
00547         data_end = line.size();
00548     }
00549 
00550     data.append (line, data_begin, (data_end-data_begin));
00551 
00552     if (data.size() && save_delimiters) {
00553       data_begin_pos.push_back (conf.find (data, pos+key.size()));
00554       data_end_pos.push_back   (data_begin_pos.back()+data.size());
00555       //       std::cerr << "key = " << key << ", data = \""
00556       //                 << data << "\", data_begin, data_end = "
00557       //                 << data_begin_pos.back() << ", " << data_end_pos.back()
00558       //                 << "\n";
00559     }
00560   }
00561   
00562   save_pos = line_end;
00563 
00564   return true;
00565 }
00566 
00567 
00568 std::istream & operator>> (std::istream &is, colvarparse::read_block const &rb)
00569 {
00570   size_t start_pos = is.tellg();
00571   std::string read_key, next;
00572 
00573   if ( !(is >> read_key) || !(read_key == rb.key) ||
00574        !(is >> next) ) {
00575     // the requested keyword has not been found, or it is not possible
00576     // to read data after it
00577     is.clear();
00578     is.seekg (start_pos, std::ios::beg);
00579     is.setstate (std::ios::failbit);
00580     return is;
00581   }
00582 
00583   if (next != "{") {
00584     (*rb.data) = next;
00585     return is;
00586   }
00587 
00588   size_t brace_count = 1;
00589   std::string line;
00590   while (colvarparse::getline_nocomments (is, line)) {
00591     size_t br = 0, br_old;
00592     while ( (br = line.find_first_of ("{}", br)) != std::string::npos) {
00593       if (line[br] == '{') brace_count++;
00594       if (line[br] == '}') brace_count--;
00595       br_old = br;
00596       br++;
00597     }
00598     if (brace_count) (*rb.data).append (line + "\n");
00599     else {
00600       (*rb.data).append (line, 0, br_old);
00601       break;
00602     }
00603   }
00604   if (brace_count)  {
00605     // end-of-file reached
00606     // restore initial position
00607     is.clear();
00608     is.seekg (start_pos, std::ios::beg);
00609     is.setstate (std::ios::failbit);
00610   }
00611   return is;
00612 }
00613 
00614 
00615 bool colvarparse::brace_check (std::string const &conf,
00616                                size_t const start_pos)
00617 {
00618   size_t brace_count = 0;
00619   size_t brace = start_pos;
00620   while ( (brace = conf.find_first_of ("{}", brace)) != std::string::npos) {
00621     if (conf[brace] == '{') brace_count++;
00622     if (conf[brace] == '}') brace_count--;
00623     brace++;
00624   }
00625 
00626   if (brace_count != 0)
00627     return false;
00628   else
00629     return true;
00630 }
00631 
00632 
00633 // Emacs
00634 // Local Variables:
00635 // mode: C++
00636 // End:

Generated on Fri May 25 04:07:13 2012 for NAMD by  doxygen 1.3.9.1