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         if (i < values.size()-1) {                                      \
00189           cvm::fatal_error ("Error: values from string \""+             \
00190                             data+"\" are less than expected ("+         \
00191                             cvm::to_str (values.size())+").\n");        \
00192         }                                                               \
00193       }                                                                 \
00194                                                                         \
00195       if (parse_mode != parse_silent) {                                 \
00196         cvm::log ("# "+std::string (key)+" = "+                         \
00197                   cvm::to_str (values)+"\n");                           \
00198       }                                                                 \
00199                                                                         \
00200     } else {                                                            \
00201                                                                         \
00202       if (b_found_any)                                                  \
00203         cvm::fatal_error ("Error: improper or missing values for \""+   \
00204                           std::string (key)+"\".\n");                   \
00205                                                                         \
00206       for (size_t i = 0; i < values.size(); i++)                        \
00207         values[i] = def_values[ (i > def_values.size()) ? 0 : i ];      \
00208                                                                         \
00209       if (parse_mode != parse_silent) {                                 \
00210         cvm::log ("# "+std::string (key)+" = "+                         \
00211                   cvm::to_str (def_values)+" [default]\n");             \
00212       }                                                                 \
00213     }                                                                   \
00214                                                                         \
00215     return b_found_any;                                                 \
00216   }
00217 
00218 
00219 // single-value keyword parsers
00220 
00221 _get_keyval_scalar_ (int);
00222 _get_keyval_scalar_ (size_t);
00223 _get_keyval_scalar_string_ (std::string);
00224 _get_keyval_scalar_ (cvm::real);
00225 _get_keyval_scalar_ (cvm::rvector);
00226 _get_keyval_scalar_ (cvm::quaternion);
00227 _get_keyval_scalar_ (colvarvalue);
00228 
00229 
00230 // multiple-value keyword parsers
00231 
00232 _get_keyval_vector_ (int);
00233 _get_keyval_vector_ (size_t);
00234 _get_keyval_vector_ (std::string);
00235 _get_keyval_vector_ (cvm::real);
00236 _get_keyval_vector_ (cvm::rvector);
00237 _get_keyval_vector_ (cvm::quaternion);
00238 _get_keyval_vector_ (colvarvalue);
00239 
00240 
00241 
00242 bool colvarparse::get_keyval (std::string const &conf,        
00243                               char const *key,                
00244                               bool &value,                    
00245                               bool const &def_value,          
00246                               Parse_Mode const parse_mode)    
00247 {
00248   std::string data;
00249   bool b_found = false, b_found_any = false;
00250   size_t save_pos = 0, found_count = 0;
00251 
00252   do {
00253     std::string data_this = "";
00254     b_found = key_lookup (conf, key, data_this, save_pos);
00255     if (b_found) {
00256       if (!b_found_any)
00257         b_found_any = true;
00258       found_count++;
00259       data = data_this;
00260     }
00261   } while (b_found);
00262 
00263   if (found_count > 1)
00264     cvm::log ("Warning: found more than one instance of \""+
00265               std::string (key)+"\".\n");
00266 
00267   if (data.size()) {
00268     std::istringstream is (data);
00269     if ( (data == std::string ("on")) ||
00270          (data == std::string ("yes")) ||
00271          (data == std::string ("true")) ) {
00272       value = true;
00273     } else if ( (data == std::string ("off")) ||
00274               (data == std::string ("no")) ||
00275               (data == std::string ("false")) ) {
00276       value = false;
00277     } else
00278       cvm::fatal_error ("Error: boolean values only are allowed "  
00279                         "for \""+std::string (key)+"\".\n");
00280     if (parse_mode != parse_silent) {
00281       cvm::log ("# "+std::string (key)+" = "+
00282                 (value ? "on" : "off")+"\n");
00283     }
00284   } else {
00285 
00286     if (b_found_any) {
00287       if (parse_mode != parse_silent) {
00288         cvm::log ("# "+std::string (key)+" = on\n");
00289       }
00290       value = true;
00291     } else {
00292       value = def_value;
00293       if (parse_mode != parse_silent) {
00294         cvm::log ("# "+std::string (key)+" = "+
00295                   (def_value ? "on" : "off")+" [default]\n");
00296       }
00297     }
00298   }
00299 
00300   return b_found_any;
00301 }
00302 
00303 
00304 void colvarparse::add_keyword (char const *key)
00305 {
00306   for (std::list<std::string>::iterator ki = allowed_keywords.begin();
00307        ki != allowed_keywords.end(); ki++) {
00308     if (to_lower_cppstr (std::string (key)) == *ki)
00309       return;
00310   }
00311   // not found in the list
00312   //   if (cvm::debug())
00313   //     cvm::log ("Registering a new keyword, \""+std::string (key)+"\".\n");
00314   allowed_keywords.push_back (to_lower_cppstr (std::string (key)));
00315 }
00316 
00317 
00318 void colvarparse::strip_values (std::string &conf)
00319 {
00320   size_t offset = 0;
00321   data_begin_pos.sort();
00322   data_end_pos.sort();
00323 
00324   std::list<size_t>::iterator data_begin = data_begin_pos.begin();
00325   std::list<size_t>::iterator data_end   = data_end_pos.begin();
00326 
00327   for ( ; (data_begin != data_begin_pos.end()) &&
00328           (data_end   != data_end_pos.end()) ;
00329         data_begin++, data_end++) {
00330 
00331     //     std::cerr << "data_begin, data_end "
00332     //               << *data_begin << ", " << *data_end
00333     //               << "\n";
00334 
00335     size_t const nchars = *data_end-*data_begin;
00336 
00337     //     std::cerr << "conf[data_begin:data_end] = \""
00338     //               << std::string (conf, *data_begin - offset, nchars)
00339     //               << "\"\n";
00340 
00341     conf.erase (*data_begin - offset, nchars);
00342     offset += nchars;
00343 
00344     //     std::cerr << ("Stripped config = \"\n"+conf+"\"\n");
00345 
00346   }
00347 }
00348 
00349 
00350 void colvarparse::check_keywords (std::string &conf, char const *key)
00351 {
00352   //   if (cvm::debug())
00353   //     cvm::log ("Configuration string for \""+std::string (key)+
00354   //               ": \"\n"+conf+"\".\n");
00355 
00356   strip_values (conf);
00357   // after stripping, the config string has either empty lines, or
00358   // lines beginning with a keyword
00359 
00360   std::string line;
00361   std::istringstream is (conf);
00362   while (std::getline (is, line)) {
00363     if (line.size() == 0)
00364       continue;
00365     if (line.find_first_not_of (white_space) ==
00366         std::string::npos)
00367       continue;
00368 
00369     std::string uk;
00370     std::istringstream line_is (line);
00371     line_is >> uk;
00372     if (cvm::debug())
00373       cvm::log ("Checking the validity of \""+uk+"\".\n");
00374     uk = to_lower_cppstr (uk);
00375 
00376     bool found_keyword = false;
00377     for (std::list<std::string>::iterator ki = allowed_keywords.begin();
00378          ki != allowed_keywords.end(); ki++) {
00379       if (uk == *ki) {
00380         found_keyword = true;
00381         break;
00382       }
00383     }
00384     if (!found_keyword)
00385       cvm::fatal_error ("Error: keyword \""+uk+"\" is not supported, "
00386                         "or not recognized in this context.\n");
00387   }
00388 }
00389 
00390 
00391 std::istream & colvarparse::getline_nocomments (std::istream &is,
00392                                                 std::string &line,
00393                                                 char const delim)
00394 {
00395   std::getline (is, line, delim);
00396   size_t const comment = line.find ('#');
00397   if (comment != std::string::npos) {
00398     line.erase (comment);
00399   }
00400   return is;
00401 }
00402 
00403 
00404 bool colvarparse::key_lookup (std::string const &conf,
00405                               char const *key_in,
00406                               std::string &data,
00407                               size_t &save_pos)
00408 {
00409   // add this keyword to the register (in its camelCase version)
00410   add_keyword (key_in);
00411 
00412   // use the lowercase version from now on
00413   std::string const key (to_lower_cppstr (key_in));
00414 
00415   // "conf_lower" is only used to lookup the keyword, but its value
00416   // will be read from "conf", in order not to mess up file names
00417   std::string const conf_lower (to_lower_cppstr (conf));
00418 
00419   // by default, there is no value, unless we found one
00420   data = "";
00421 
00422   // when the function is invoked without save_pos, ensure that we
00423   // start from zero
00424   colvarparse::dummy_pos = 0;
00425 
00426   // start from the first occurrence of key
00427   size_t pos = conf_lower.find (key, save_pos); 
00428   
00429   // iterate over all instances until it finds the isolated keyword
00430   while (true) {
00431 
00432     if (pos == std::string::npos) {
00433       // no valid instance of the keyword has been found
00434       return false;
00435     }
00436 
00437     bool b_isolated_left = true, b_isolated_right = true;
00438 
00439     if (pos > 0) {
00440       if ( std::string ("\n"+white_space+
00441                         "}").find (conf[pos-1]) ==
00442            std::string::npos ) {
00443         // none of the valid delimiting characters is on the left of key 
00444         b_isolated_left = false;
00445       }
00446     }
00447 
00448     if (pos < conf.size()-key.size()-1) {
00449       if ( std::string ("\n"+white_space+
00450                         "{").find (conf[pos+key.size()]) ==
00451            std::string::npos ) {
00452         // none of the valid delimiting characters is on the right of key 
00453         b_isolated_right = false;
00454       }
00455     }
00456 
00457     // check that there are matching braces between here and the end of conf
00458     bool const b_not_within_block = brace_check (conf, pos);
00459 
00460     bool const b_isolated = (b_isolated_left && b_isolated_right &&
00461                              b_not_within_block);
00462     
00463     if (b_isolated) {
00464       // found it
00465       break;
00466     } else {
00467       // try the next occurrence of key
00468       pos = conf_lower.find (key, pos+key.size());
00469     }
00470   }
00471 
00472   // check it is not between quotes
00473   //   if ( (conf.find_last_of  ("\"",
00474   //                             conf.find_last_of  (white_space, pos)) !=
00475   //         std::string::npos) &&
00476   //        (conf.find_first_of ("\"",
00477   //                             conf.find_first_of (white_space, pos)) !=
00478   //         std::string::npos) )
00479   //     return false;
00480 
00481 
00482   // save the pointer for a future call (when iterating over multiple
00483   // valid instances of the same keyword)
00484   save_pos = pos + key.size();
00485 
00486   // get the remainder of the line
00487   size_t pl = conf.rfind ("\n", pos);
00488   size_t line_begin = (pl == std::string::npos) ? 0 : pos;
00489   size_t nl = conf.find  ("\n", pos);
00490   size_t line_end = (nl == std::string::npos) ? conf.size() : nl;
00491   std::string line (conf, line_begin, (line_end-line_begin)); 
00492 
00493   size_t data_begin = (to_lower_cppstr (line)).find (key) + key.size();
00494   data_begin = line.find_first_not_of (white_space, data_begin+1);
00495 
00496   //   size_t data_begin_absolute = data_begin + line_begin;
00497   //   size_t data_end_absolute   = data_begin;
00498 
00499   if (data_begin != std::string::npos) {
00500 
00501     size_t data_end = line.find_last_not_of (white_space) + 1;
00502     data_end = (data_end == std::string::npos) ? line.size() : data_end;
00503     //     data_end_absolute = data_end + line_begin;
00504 
00505     if (line.find ('{', data_begin) != std::string::npos) {
00506 
00507       size_t brace_count = 1;
00508       size_t brace = line.find ('{', data_begin);  // start from the first opening brace
00509 
00510       while (brace_count > 0) {
00511 
00512         // find the matching closing brace
00513         brace = line.find_first_of ("{}", brace+1);
00514         while (brace == std::string::npos) {
00515           // add a new line
00516           if (line_end >= conf.size()) {
00517             cvm::fatal_error ("Parse error: reached the end while "
00518                               "looking for closing brace; until now "
00519                               "the following was parsed: \"\n"+
00520                               line+"\".\n");
00521             return false;
00522           }
00523           size_t const old_end = line.size();
00524           //           data_end_absolute += old_end+1;
00525 
00526           line_begin = line_end;
00527           nl = conf.find ('\n', line_begin+1);
00528           if (nl == std::string::npos) 
00529             line_end = conf.size();
00530           else 
00531             line_end = nl;
00532           line.append (conf, line_begin, (line_end-line_begin));
00533 
00534           brace = line.find_first_of ("{}", old_end);
00535         }
00536 
00537         if (line[brace] == '{') brace_count++;
00538         if (line[brace] == '}') brace_count--;
00539       }
00540 
00541       // set data_begin afterthe opening brace
00542       data_begin = line.find_first_of ('{', data_begin) + 1;
00543       data_begin = line.find_first_not_of (white_space,
00544                                            data_begin);
00545       // set data_end before the closing brace
00546       data_end = brace;
00547       data_end = line.find_last_not_of (white_space+"}",
00548                                         data_end) + 1;
00549       //       data_end_absolute = line_end;
00550 
00551       if (data_end > line.size())
00552         data_end = line.size();
00553     }
00554 
00555     data.append (line, data_begin, (data_end-data_begin));
00556 
00557     if (data.size() && save_delimiters) {
00558       data_begin_pos.push_back (conf.find (data, pos+key.size()));
00559       data_end_pos.push_back   (data_begin_pos.back()+data.size());
00560       //       std::cerr << "key = " << key << ", data = \""
00561       //                 << data << "\", data_begin, data_end = "
00562       //                 << data_begin_pos.back() << ", " << data_end_pos.back()
00563       //                 << "\n";
00564     }
00565   }
00566   
00567   save_pos = line_end;
00568 
00569   return true;
00570 }
00571 
00572 
00573 std::istream & operator>> (std::istream &is, colvarparse::read_block const &rb)
00574 {
00575   size_t start_pos = is.tellg();
00576   std::string read_key, next;
00577 
00578   if ( !(is >> read_key) || !(read_key == rb.key) ||
00579        !(is >> next) ) {
00580     // the requested keyword has not been found, or it is not possible
00581     // to read data after it
00582     is.clear();
00583     is.seekg (start_pos, std::ios::beg);
00584     is.setstate (std::ios::failbit);
00585     return is;
00586   }
00587 
00588   if (next != "{") {
00589     (*rb.data) = next;
00590     return is;
00591   }
00592 
00593   size_t brace_count = 1;
00594   std::string line;
00595   while (colvarparse::getline_nocomments (is, line)) {
00596     size_t br = 0, br_old;
00597     while ( (br = line.find_first_of ("{}", br)) != std::string::npos) {
00598       if (line[br] == '{') brace_count++;
00599       if (line[br] == '}') brace_count--;
00600       br_old = br;
00601       br++;
00602     }
00603     if (brace_count) (*rb.data).append (line + "\n");
00604     else {
00605       (*rb.data).append (line, 0, br_old);
00606       break;
00607     }
00608   }
00609   if (brace_count)  {
00610     // end-of-file reached
00611     // restore initial position
00612     is.clear();
00613     is.seekg (start_pos, std::ios::beg);
00614     is.setstate (std::ios::failbit);
00615   }
00616   return is;
00617 }
00618 
00619 
00620 bool colvarparse::brace_check (std::string const &conf,
00621                                size_t const start_pos)
00622 {
00623   size_t brace_count = 0;
00624   size_t brace = start_pos;
00625   while ( (brace = conf.find_first_of ("{}", brace)) != std::string::npos) {
00626     if (conf[brace] == '{') brace_count++;
00627     if (conf[brace] == '}') brace_count--;
00628     brace++;
00629   }
00630 
00631   if (brace_count != 0)
00632     return false;
00633   else
00634     return true;
00635 }
00636 
00637 
00638 // Emacs
00639 // Local Variables:
00640 // mode: C++
00641 // End:

Generated on Tue Nov 24 04:07:42 2009 for NAMD by  doxygen 1.3.9.1