CMS 3D CMS Logo

PythonFormWriter.cc

Go to the documentation of this file.
00001 #include <iostream>
00002 
00003 #include "FWCore/ParameterSet/src/PythonFormWriter.h"
00004 #include "FWCore/ParameterSet/interface/Nodes.h"
00005 #include "FWCore/ParameterSet/interface/PSetNode.h"
00006 #include "FWCore/ParameterSet/interface/VPSetNode.h"
00007 #include "FWCore/ParameterSet/interface/EntryNode.h"
00008 #include "FWCore/ParameterSet/interface/ImplicitIncludeNode.h"
00009 #include "FWCore/ParameterSet/interface/VEntryNode.h"
00010 #include "FWCore/ParameterSet/interface/WrapperNode.h"
00011 #include "FWCore/ParameterSet/interface/OperatorNode.h"
00012 #include "FWCore/ParameterSet/interface/OperandNode.h"
00013 #include "FWCore/Utilities/interface/DebugMacros.h"
00014 #include "FWCore/Utilities/interface/EDMException.h"
00015 #include "FWCore/ParameterSet/interface/ParseTree.h"
00016 #include "FWCore/ParameterSet/interface/parse.h"
00017 
00018 //
00019 // TODO:
00020 // 
00021 //   We should clear the state at the beginning of write(), so that we
00022 //   don't garble the result of a second use of the 'Writer. Or, we
00023 //   should have the tree walk done during construction, and have
00024 //   'write' just spew the guts. This would mean we need to make a new
00025 //   'Writer for each parse tree we want to process.
00026 //
00027 //   There is too much replication of code in this class; there is
00028 //   need of refactoring.
00029 //
00030 //   We can not deal with a PSet appearing a top level. There is no
00031 //   place in the output format, as currently defined, so save such a
00032 //   thing.
00033 
00034 #define MYDEBUG(n) FDEBUG(n) << "DBG----- "
00035 
00036 namespace edm
00037 {
00038   namespace pset
00039   {
00040 
00041     //------------------------------------------------------------
00042     // Helper functions
00043 
00044     typedef void (*writer_func)(std::ostream&, std::string const&);
00045 
00046     void 
00047     write_string_value(std::ostream& os, std::string const& val)
00048     {
00049       //make sure the quotes we add are different than the ones
00050       // used in val
00051       std::string quotes("'");
00052       if( quotes == val.substr(0,1)) {
00053         quotes = "\"";
00054       }
00055       // "r" means raw, to preserve all escape charactersOB
00056       os <<"r"<<quotes<<val<<quotes;
00057     }
00058 
00059     void 
00060     write_other_value(std::ostream& os, std::string const& val)
00061     {
00062       os << "'" << val << "'";
00063     }
00064 
00065     void
00066     write_trackedness(std::ostream& os, bool trackedval)
00067     {
00068       if (trackedval) 
00069         os << "'tracked'";
00070       else 
00071         os << "'untracked'";
00072     }
00073 
00074     // Cheesy heuristic for recognizing output modules from the class
00075     // name: If the name ends in OutputModule, we guess it is an
00076     // output module.
00077 
00078     bool
00079     looks_like_an_output_module(std::string const& classname)
00080     {
00081       std::string::size_type pos = classname.find("OutputModule");
00082 
00083       // If we didn't find OutputModule, it isn't an output module
00084       if ( pos == std::string::npos ) return false;
00085 
00086       // Now make sure some tricky lad didn't put OutputModule in the
00087       // middle of a class name... 
00088       //  length of OutputModule = 12
00089 
00090       return  (pos + 12) ==  classname.size();
00091     }
00092     
00093 
00094     //------------------------------------------------------------
00095 
00096     PythonFormWriter::PythonFormWriter() :
00097       procname_(),
00098       moduleStack_(),
00099       modules_(),
00100       outputModuleNames_(),
00101       modulesWithSecSources_(),
00102       triggerPaths_(),
00103       endPaths_()
00104     {
00105       std::list<std::string> emptylist;
00106       modules_.insert(make_pair(std::string("es_module"), emptylist));
00107       modules_.insert(make_pair(std::string("es_source"), emptylist));
00108       modules_.insert(make_pair(std::string("es_prefer"), emptylist));
00109       modules_.insert(make_pair(std::string("module"), emptylist));
00110       modules_.insert(make_pair(std::string("source"), emptylist));
00111       modules_.insert(make_pair(std::string("looper"), emptylist));
00112       modules_.insert(make_pair(std::string("sequence"),emptylist));
00113       modules_.insert(make_pair(std::string("path"),emptylist));
00114       modules_.insert(make_pair(std::string("endpath"),emptylist));
00115       modules_.insert(make_pair(std::string("schedule"), emptylist));
00116       modules_.insert(make_pair(std::string("service"),emptylist));
00117       modules_.insert(make_pair(std::string("pset"), emptylist));
00118     }
00119 
00120     PythonFormWriter::~PythonFormWriter()
00121     { }
00122 
00123     void 
00124     PythonFormWriter::visitUsing(const UsingNode&)
00125     { 
00126       MYDEBUG(5) << "Saw a UsingNode\n";
00127     }
00128 
00129     void 
00130     PythonFormWriter::visitString(const StringNode&)
00131     { 
00132       MYDEBUG(5) << "Saw a StringNode\n";
00133     }
00134 
00135 
00136     // Entries withing modules that are not vectors or PSets.
00137     void 
00138     PythonFormWriter::visitEntry(const EntryNode& n)
00139     { 
00140       MYDEBUG(5) << "Saw an EntryNode\n";
00141       std::ostringstream tuple;
00142 
00143       tuple << "'"
00144             << n.name() << "': ('"
00145             << n.type() << "', ";
00146       write_trackedness(tuple, n.isTracked());
00147       tuple << ", ";
00148 
00149       if (n.type() == "string") 
00150         {
00151           write_string_value(tuple, n.value());
00152         }
00153       else 
00154         {
00155           write_other_value(tuple, n.value());
00156         }
00157 
00158       tuple  << ')';
00159       
00160       moduleStack_.top() += tuple.str();
00161     }
00162 
00163 
00164     void
00165     PythonFormWriter::visitVEntry(const VEntryNode& n)
00166     { 
00167       MYDEBUG(5) << "Saw a VEntryNode\n";
00168       std::ostringstream tuple;
00169 
00170       tuple << "'"
00171             << n.name() << "': ('"
00172             << n.type() << "', ";
00173 
00174       write_trackedness(tuple, n.isTracked());
00175       tuple << ", ";
00176 
00177       // Write out contents of the list...
00178       StringList::const_iterator i = n.value()->begin();
00179       StringList::const_iterator e = n.value()->end();
00180 
00181       // Figure out which writer to call, so we don't have to do it
00182       // each time in the loop below.
00183       writer_func writer_to_call = 
00184         ( n.type() == "vstring" )  // not n.type() == "string"!
00185         ? &write_string_value
00186         : &write_other_value;
00187 
00188       tuple << "[ ";
00189       for ( bool first = true; i != e; ++i, first = false)
00190         {
00191           if (!first) tuple << ", ";
00192           writer_to_call(tuple, *i);
00193         }
00194 
00195       tuple << " ])";
00196       
00197       moduleStack_.top() += tuple.str();
00198     }
00199 
00200     void
00201     PythonFormWriter::visitContents(const ContentsNode& n)
00202     { 
00203       MYDEBUG(5) << "Saw a ContentsNode\n";
00204       writeCompositeNode(n);
00205     }
00206 
00207 
00208     void PythonFormWriter::writeCompositeNode(const CompositeNode &n)
00209     {
00210       // If the module stack is not empty, we're working on a PSet
00211       // inside a module (maybe inside something inside of a
00212       // module). Otherwise, we're working on a top-level PSet (not
00213       // currently working), or on the process block itself.
00214       if(moduleStack_.empty() )
00215       {
00216          // we don't want commas between top-level nodes
00217          n.acceptForChildren(*this);
00218       }
00219       else 
00220       {
00221         moduleStack_.top() += "{";
00222         writeCommaSeparated(n);
00223         moduleStack_.top() += "}";
00224       } 
00225     }
00226 
00227     void
00228     PythonFormWriter::writeCommaSeparated(const CompositeNode & n)
00229     {
00230       assert(!moduleStack_.empty());
00231       NodePtrList::const_iterator i = n.nodes()->begin();
00232       NodePtrList::const_iterator e = n.nodes()->end();
00233       for ( bool first = true; i != e; first = false, ++i)
00234       {
00235         if (!first)
00236         {
00237           moduleStack_.top() += ", ";
00238         }
00239         (*i)->accept(*this);
00240       }
00241     }
00242 
00243     void 
00244     PythonFormWriter::visitInclude(const IncludeNode &n)
00245     {
00246       if(moduleStack_.empty() )
00247       {
00248          // we don't want commas between top-level nodes
00249          n.acceptForChildren(*this);
00250       }
00251       else
00252       {
00253         writeCommaSeparated(n);
00254       }
00255     }
00256 
00257 
00258     void
00259     PythonFormWriter:: visitPSet(const PSetNode& n)
00260     { 
00261       MYDEBUG(5) << "Saw a PSetNode\n";
00262       if ( "process" == n.type() )
00263         {
00264           procname_ = n.name();
00265           n.acceptForChildren(*this);
00266 
00267           MYDEBUG(4) << "\nprocess name: " << procname_
00268                      << "\nstack size? " << moduleStack_.size()
00269                      << "\nnumber of module types? " << modules_.size()
00270                      << '\n';
00271         }
00272       else if ( "PSet" == n.type() ) 
00273         {
00274           // We're processing (the contents of) a PSet if we got
00275           // here. The following processing assumes this PSet should
00276           // be written a named parameter.
00277           std::ostringstream out;
00278           out << "'" 
00279               << n.name() 
00280               << "': ('PSet', ";
00281           write_trackedness(out, n.isTracked());
00282           out << ", ";
00283 
00284           bool atTopLevel = (moduleStack_.empty());
00285           if(atTopLevel) 
00286           {
00287             moduleStack_.push(std::string());
00288           }
00289           moduleStack_.top() += out.str();
00290           writeCompositeNode(n);
00291 
00292           // And finish up
00293           //moduleStack_.top() += ")\n";
00294           moduleStack_.top() += ")";
00295 
00296           if(atTopLevel) 
00297           {
00298             modules_["pset"].push_back(moduleStack_.top());
00299             moduleStack_.pop();
00300           }
00301         }
00302       else
00303         {
00304           MYDEBUG(5) << "weird thing: "
00305                      << n.type() << " " << n.name() << '\n';
00306         }
00307 
00308     }
00309 
00310     void
00311     PythonFormWriter::visitVPSet(const VPSetNode& n)
00312     { 
00313       MYDEBUG(5) << "Saw a VPSetNode\n";
00314       std::ostringstream out;
00315       out << "'"
00316           << n.name()
00317           << "': ('VPSet', ";
00318       write_trackedness(out, n.isTracked());
00319       out << ", [";
00320       moduleStack_.top() += out.str();
00321       
00322       writeCommaSeparated(n);
00323 
00324       moduleStack_.top() += "])";
00325     }
00326 
00327     void
00328     PythonFormWriter::visitModule(const ModuleNode& n)
00329     { 
00330       MYDEBUG(5) << "Saw a ModuleNode, name: " 
00331                  << n.name() << '\n';
00332 
00333       std::ostringstream header;
00334 
00335       // We don't want to write the name 'nameless' for unnamed
00336       // es_modules, nor the name 'main_es_input' for unnamed
00337       // es_sources, nor an empty string for the unnamed (main)
00338       // source.
00339       if ( (n.type() == "es_module") ||
00340            (n.type() == "es_source") ||
00341            (n.type() == "es_prefer")   )
00342         {
00343           //es_* items are unique based on 'C++ class' and 'label'
00344           std::string prefix("");
00345           std::string label("");
00346           std::string name("@");
00347           if((n.type() == "es_module" && n.name()!="nameless" ||
00348               n.type() == "es_source" && n.name()!="main_es_input") ||
00349               n.type() == "es_prefer" && n.name()!="nameless")
00350           {
00351              label = n.name();
00352              name += n.name();
00353           }
00354           if(n.type() =="es_prefer") {
00355             prefix = "esprefer_";
00356           }
00357           header <<"'"<< prefix << n.className() <<name<<"': { '@label': ('string','tracked', '" <<label<<"'), ";
00358         }
00359       else if (n.type() == "source" && n.name().empty())
00360         {
00361           // no header to write...
00362         }
00363       else if (n.type() == "looper" && n.name().empty())
00364       {
00365         // no header to write...
00366       }
00367       else if(n.type()=="service") 
00368         {
00369           header<<"'"<<n.className()<<"': {";
00370         }
00371       else if(n.type()=="secsource")
00372         {
00373           // we need to remember all modules with secsources
00374           // we should be inside a module stack now, so the first word
00375           // of the stack should be the top-level module name
00376           assert( moduleStack_.size() > 0);
00377           std::vector<std::string> tokens = tokenize(moduleStack_.top(), ":");
00378           assert(!tokens.empty());
00379           modulesWithSecSources_.push_back(*(tokens.begin()));
00380 
00381           header<<"'"<<n.name() <<"': ('secsource', 'tracked', {";
00382         }
00383       else
00384         {
00385           header << "'" << n.name() << "': {";
00386         }
00387 
00388       header << "'@classname': ('string', 'tracked', '"
00389              << n.className()
00390              << "')";
00391 
00392       // Remember the names of modules that are output modules...  We
00393       // use a cheesy heuristic; see looks_like_an_output_module for
00394       // the details
00395 
00396 //       assert ( looks_like_an_output_module ("AsciiOutputModule") );
00397 //       assert ( looks_like_an_output_module ("PoolOutputModule") );
00398 //       assert ( !looks_like_an_output_module ("NotOutputModuleX") );
00399 //       assert ( !looks_like_an_output_module ("") );
00400 //       assert ( !looks_like_an_output_module ("X") );
00401 
00402       if ( n.type() == "module" && 
00403            looks_like_an_output_module(n.className()) )
00404         {
00405           outputModuleNames_.push_back(n.name());
00406         }
00407 
00408       // secsource is the only kind of module that can exist inside another module
00409       if(n.type() == "secsource") 
00410       {
00411         assert(!moduleStack_.empty());
00412         moduleStack_.top() += header.str();
00413       }
00414       else 
00415       {
00416         moduleStack_.push(header.str());
00417       }
00418 
00419       // We can't just call 'acceptForChildren', beacuse we have to
00420       // take action between children.
00421       //n.acceptForChildren(*this);
00422      
00423       NodePtrList::const_iterator i(n.nodes()->begin());
00424       NodePtrList::const_iterator e(n.nodes()->end());
00425 
00426        for (  ; i!=e; ++i)
00427         {
00428           // If we are processing a 'process' block, moduleStack_ will
00429           // be empty; if we're processing a module, or a pset, it
00430           // won't be. We hope we don't get here otherwise.
00431           //if (!moduleStack_.empty()) moduleStack_.top() += "\n,";
00432           if (!moduleStack_.empty()) moduleStack_.top() += ", ";
00433           (*i)->accept(*this);
00434         }
00435 
00436       moduleStack_.top() += '}'; // add trailer
00437 
00438       std::string section_label = n.type();
00439       // the only module that we expect to see inside
00440       // another module is the secsource
00441       if(section_label == "secsource") 
00442       {
00443         moduleStack_.top() += ')';
00444       } 
00445       else 
00446       {
00447         modules_[section_label].push_back(moduleStack_.top());
00448         moduleStack_.pop();
00449       }
00450     }
00451 
00452 
00453     // sequence, path, endpath, schedule come in 'WrapperNode'
00454     void
00455     PythonFormWriter::visitWrapper(const WrapperNode& n)
00456     {
00457       std::ostringstream header;
00458       header << "'";
00459       if(n.type() != "schedule")
00460       {
00461         header<<n.name()<<"' : '";
00462       }
00463       moduleStack_.push(header.str());
00464       
00465       //processes the node held by the wrapper
00466       n.wrapped()->accept(*this);
00467 
00468       moduleStack_.top()+="'";
00469       modules_[n.type()].push_back(moduleStack_.top());
00470       moduleStack_.pop();
00471       MYDEBUG(5) << "Saw a WrapperNode, name: "
00472                  << n.name() << '\n';
00473       // handle a few special cases
00474       if(n.type() == "path")
00475       {
00476         triggerPaths_.push_back(n.name());
00477       }
00478       else if(n.type() == "endpath")
00479       {
00480         endPaths_.push_back(n.name());
00481       }
00482     }
00483     
00484     void 
00485     PythonFormWriter::visitOperator(const OperatorNode& n)
00486     {
00487       moduleStack_.top()+="(";
00488       n.left()->accept(*this);
00489       moduleStack_.top()+=n.type();
00490       n.right()->accept(*this);
00491       moduleStack_.top()+=")";
00492     }
00493     void 
00494     PythonFormWriter::visitOperand(const OperandNode& n)
00495     {
00496       moduleStack_.top()+=n.name();
00497     }
00498 
00499 
00500     void
00501     PythonFormWriter::write(ParseTree& parsetree, std::ostream& out)
00502     {
00503       // Walk the tree, accumulating state.
00504       parsetree.top()->accept(*this);
00505 
00506       // Now write what we have found
00507       out << "{\n'procname': '"
00508           << procname_
00509           << "'\n";
00510 
00511       out << ", 'main_input': {\n";
00512       {
00513          std::list<std::string> const& input = modules_["source"];
00514          if(input.empty()){
00515             out << "}";
00516          }
00517          else {
00518             out << *(input.begin()) << '\n';
00519          }
00520          // print guts of main input here
00521       }
00522       //NOTE: no extra '}' added since it is added in the previous printing
00523       out << " # end of main_input\n";
00524 
00525       out << ", 'looper': {\n";
00526       {
00527         std::list<std::string> const& input = modules_["looper"];
00528         if(input.empty()){
00529           out << "}";
00530         }
00531         else {
00532           out << *(input.begin()) << '\n';
00533         }
00534         // print guts of main input here
00535       }
00536       //NOTE: no extra '}' added since it is added in the previous printing
00537       out << " # end of looper\n";
00538       
00539       
00540       writeType("pset", out);
00541       writeType("module", out);
00542       writeType("es_module", out);
00543       writeType("es_source", out);
00544       writeType("es_prefer", out);
00545 
00546       out << "# output modules (names)\n";
00547       {
00548         out << ", 'output_modules': [ ";
00549         writeCommaSeparated(outputModuleNames_, true, out);
00550         out << " ]\n" ;
00551       }
00552 
00553       out << "# modules with secsources (names)\n";
00554       {
00555         out << ", 'modules_with_secsources': [ ";
00556         writeCommaSeparated(modulesWithSecSources_, false, out);
00557         out << " ]\n" ;
00558       }
00559 
00560       writeType("sequence", out);
00561       writeType("path", out);
00562       writeType("endpath", out);
00563       writeType("service", out);
00564       doSchedule(out);
00565       
00566       out << '}';
00567     }
00568 
00569     void PythonFormWriter::writeType(const std::string & type, std::ostream & out)
00570     {
00571       // We're making plurals here
00572       out << "# " << type << "s\n";
00573       {
00574         out << ", '" << type << "s': {\n";
00575         writeCommaSeparated(modules_[type], false, out);
00576         out << "} #end of " << type << "s\n";
00577       }
00578     }
00579 
00580     void PythonFormWriter::writeCommaSeparated(const std::list<std::string> & input,
00581                                                bool addQuotes, std::ostream & out)
00582     {
00583       std::list<std::string>::const_iterator i = input.begin();
00584       std::list<std::string>::const_iterator e = input.end();
00585       for ( bool first = true; i!=e; first=false,++i)
00586       {
00587         if (!first) out << ',';
00588         if(addQuotes) out << "'";
00589         out << *i ;
00590         if(addQuotes) out << "'";
00591         out << '\n';
00592       }
00593     }
00594 
00595 
00596     void PythonFormWriter::doSchedule(std::ostream & out)
00597     {
00598       int nSchedules = modules_["schedule"].size();
00599       if(nSchedules > 1)
00600       {
00601         throw edm::Exception(errors::Configuration)
00602           << "More than one schedule defined in this config file";
00603       }
00604       // if one is already defined
00605       else if(nSchedules == 1)
00606       {
00607         writeSchedule(out);
00608       }
00609       // if there's none defined, make one
00610       // so that python won't re-order it for us later.
00611       else 
00612       {
00613         //concatenate triggerPath and endPaths in a comma-delimited list
00614         std::string schedule = "'";
00615         std::list<std::string> parts(triggerPaths_);
00616         parts.insert(parts.end(), endPaths_.begin(), endPaths_.end());
00617         std::list<std::string>::const_iterator i = parts.begin();
00618         std::list<std::string>::const_iterator e = parts.end();
00619         for ( bool first = true; i!=e; first=false,++i)
00620         {
00621           if (!first) schedule += ',';
00622           schedule += *i ;
00623         }
00624         schedule += "'";
00625         modules_["schedule"].push_back(schedule);
00626         writeSchedule(out);
00627       }
00628     }
00629 
00630 
00631     //  assumes there's just one in the map
00632     void PythonFormWriter::writeSchedule(std::ostream & out)
00633     {
00634       out << "# schedule\n";
00635       out << ", 'schedule': " << modules_["schedule"].front() << "\n";
00636     }
00637 
00638   } // namespace pset
00639 } // namespace edm

Generated on Tue Jun 9 17:36:29 2009 for CMSSW by  doxygen 1.5.4