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

/home/r0/dav/atlas.dir/atlas3/sources/interface/commands.cpp

Go to the documentation of this file.
00001 /*
00002   This is commands.cpp
00003 
00004   Copyright (C) 2004,2005 Fokko du Cloux
00005   part of the Atlas of Reductive Lie Groups
00006 
00007   See file main.cpp for full copyright notice
00008 */
00009 
00010 #include "commands.h"
00011 
00012 #include <cstring>
00013 #include <iostream>
00014 #include <sstream>
00015 #include <stack>
00016 
00017 #include "error.h"
00018 
00019 /*****************************************************************************
00020 
00021   This module contains the code for the command interface which we use during
00022   the development stage of the Atlas library. It is directly inspired by the
00023   interface for Coxeter, but re-written to be (hopefully) C++ conformant. In
00024   any case, i/o is now done in C++-style, and we also use the containers
00025   map and stack provided by C++, as well as the C++ string facility. Exceptions
00026   remain somewhat of a grey area here.
00027 
00028   The basic class is the CommandMode class. This contains a set of recognized
00029   names, for which it will execute corresponding functions. At each point of
00030   time, there is a stack of such modes, the local variable modeStack, the top
00031   of which is the currently active mode. Some commands will lead to pushing
00032   a new mode on the stack; the entry function of the mode is then executed.
00033   Similarly, some commands (typically the "q" command) pop the mode stack; this
00034   results in executing the exit function of the mode.
00035 
00036   Also, to each mode is associated a help mode, which usually provides some
00037   help information for each command available in that mode. It could do more
00038   in some cases.
00039 
00040 ******************************************************************************/
00041 
00042 namespace atlas {
00043 
00044 namespace { // declarations private to commands.cpp
00045 
00046   using namespace commands;
00047 
00048   std::stack<const CommandMode*> modeStack; // the stack of command modes; the
00049                                             // active mode is the top of the
00050                                             // stack
00051 
00052   std::stack<const char*> commandStack;     // lets us postpone some commands;
00053 
00054   input::InputBuffer commandLine;           // the current command line
00055 
00056   bool runFlag;
00057 
00058   // running the command interface
00059 
00060   void ambiguous(const std::vector<const char*>&, const char*);
00061   void execute(const char* name, const CommandMode* mode);
00062   const char* getCommand(const CommandMode* mode);
00063   std::istream& getInteractive(std::istream&, std::string&, const char*);
00064 
00065   // auxiliary functions
00066 
00067   inline bool isEqual(const char* a, const char* b) {
00068     return !memcmp(a,b,strlen(a));
00069   }
00070 
00071   inline bool isInitial(const char* a, const char* b) {
00072     return !memcmp(a,b,strlen(a));
00073   }
00074 
00075 }
00076 
00077 /****************************************************************************
00078 
00079         Chapter I -- The CommandMode class.
00080 
00081   This is the central class of the command module.
00082 
00083   The following member functions are defined :
00084 
00085   - constructors and destructors :
00086 
00087     - CommandMode(prompt,entry,exit,error) : constructor;
00088     - ~CommandMode() : destructor;
00089 
00090   - accessors :
00091 
00092     - prompt : prints the prompt;
00093 
00094   - manipulators :
00095 
00096     - add(name,tag,action,rep) : add a new command to the mode;
00097     - findName : finds a name in the dictionary;
00098     - setAction(name,a) : sets the action associated to name;
00099     - setRepeat(name,b) : sets the repeat flag for name;
00100 
00101 *****************************************************************************/
00102 
00103 namespace commands {
00104 
00105 
00106 /*
00107   Constructor for the command mode class.
00108 */
00109 CommandMode::CommandMode(const char* str,
00110                          void (*entry)(),
00111                          void (*exit)(),
00112                          void (*error)(const char*))
00113   :d_nextList(), // start out without successors
00114    d_map(),      // and without commands
00115    d_prompt(str),
00116    d_entry(entry),
00117    d_exit(exit),
00118    d_error(error)
00119 {} // don't add any commands, so that |empty()| is true initially
00120 
00121 /******** accessors *********************************************************/
00122 
00123 
00124 /*
00125   Synopsis: adds to |e| the list of command names in mode and its descendants,
00126   that begin with |name|.
00127 */
00128 
00129 void CommandMode::extensions(std::set<const char*,StrCmp>& e,
00130                              const char* name) const
00131 {
00132   for (const_iterator pos = d_map.lower_bound(name); pos != d_map.end();
00133        ++pos) {
00134     if (isInitial(name,pos->first))
00135       e.insert(pos->first);
00136     else
00137       break;
00138   }
00139 
00140   for (size_t j = 0; j < n_desc(); ++j) {
00141     const CommandMode& mode = nextMode(j);
00142     mode.extensions(e,name);
00143   }
00144 }
00145 
00146 
00147 /*
00148   Synopsis: puts into |e| the list of command names in mode and its
00149   descendants, that begin with |name|.
00150 
00151   Forwarded to the set-version, so that repetitions will be automatically
00152   weeded out.
00153 */
00154 void CommandMode::extensions(std::vector<const char*>& e,
00155                              const char* name) const
00156 {
00157   std::set<const char*,StrCmp> es;
00158 
00159   extensions(es,name);
00160   e.clear();
00161 
00162   for (std::set<const char*,StrCmp>::const_iterator i = es.begin();
00163        i != es.end(); ++i)
00164     e.push_back(*i);
00165 
00166   return;
00167 }
00168 
00169 
00170 /*
00171   Synopsis: finds the command in the current mode or one of its submodes.
00172 */
00173 CommandMode::const_iterator CommandMode::findName(const char* name) const
00174 {
00175   const_iterator pos = find(name); // search in current mode
00176 
00177   if (pos != end())
00178     return pos;
00179 
00180   // if not find here, look recursively in descendant modes
00181   for (size_t j = 0; j < n_desc(); ++j) {
00182     const CommandMode& next = nextMode(j);
00183     pos = next.findName(name);
00184     if (pos != next.end())
00185       return pos;
00186   }
00187 
00188   return end();
00189 }
00190 
00191 /******** manipulators ******************************************************/
00192 
00193 
00194 /*
00195   Synopsis: adds a new command to the mode.
00196 
00197   The parameters have the following meaning :
00198     - name : name of the command;
00199     - command: the function to be executed by the command;
00200 
00201   NOTE: if the name was already present, we override it.
00202 */
00203 void CommandMode::add(const char* const name, const Command& command)
00204 {
00205   std::pair<const char* const, Command> v(name,command);
00206 
00207   std::pair<CommandDict::iterator,bool> p
00208     = d_map.insert(v);
00209 
00210   if (not p.second) // then name was already present; override its command!
00211     p.first->second=command; // and don't touch |p.first->first==name|
00212 }
00213 
00214 
00215 /*
00216   Sets the action of the command associated to name to a.
00217 
00218   NOTE : it is assumed that name will be found in mode.
00219 */
00220 void CommandMode::setAction(const char* name, void (*a)())
00221 {
00222   CommandMode::iterator pos = find(name);
00223   pos->second.action = a;
00224 }
00225 
00226 }
00227 
00228 /****************************************************************************
00229 
00230         Chapter II -- Function definitions.
00231 
00232   This section contains the definitions of the functions declared in
00233   commands.h :
00234 
00235     - activate(CommandMode*) : makes mode the active mode;
00236     - defaultError(const char*) : default error handler for command reading;
00237     - checkName(CommandMode*, const char*) : tries to find name in mode;
00238     - default_help() : the default help function;
00239     - exitInteractive() : sets runFlag to false;
00240     - printTags(stream&,map<std::string,const char*>&) : prints the tag list;
00241     - quitMode() : exits the current mode;
00242     - relax_f() : does nothing;
00243     - run(CommandMode*) :  runs the program;
00244 
00245 *****************************************************************************/
00246 
00247 namespace commands {
00248 
00249 
00250 /*
00251   Attempts to activate the command mode |mode|, by executing its entry
00252   function, which could throw an |EntryError|. If not, push |mode| onto stack.
00253 */
00254 void activate(const CommandMode& mode)
00255 {
00256   mode.entry(); // could throw an EntryError
00257   modeStack.push(&mode);
00258 }
00259 
00260 
00261 /*
00262   Synopsis: inserts the commands from source into dest. This is used when
00263   going to a "desecendant" mode, to inherit the commands defined for
00264   the parent mode.
00265 
00266   NOTE: we do the insertion through add, so it will override existing
00267   commands. Therefore to redefine commands with the same name in the
00268   descendant mode, add them after making the call to |addCommands|.
00269 */
00270 void addCommands(CommandMode& dest, const CommandMode& source)
00271 {
00272   for (CommandMode::const_iterator i = source.begin(); i != source.end(); ++i)
00273     dest.add(i->first,i->second);
00274 }
00275 
00276 
00277 /*
00278   Synopsis: tries to find name in mode, or in one of its descendants.
00279 */
00280 CheckResult checkName(const CommandMode& mode, const char* name)
00281 {
00282   CommandMode::const_iterator pos = mode.findName(name);
00283 
00284   if (pos == mode.end()) { // command was not found
00285     return NotFound;
00286   }
00287 
00288   // if we get to this point, there is at least one extension of name in mode
00289 
00290   std::vector<const char*> ext;
00291   mode.extensions(ext,name);
00292 
00293   if (ext.size() > 1) { // ambiguous command
00294     ambiguous(ext,name);
00295     return Ambiguous;
00296   }
00297 
00298   // if we get to this point, the command is found in mode
00299 
00300   return Found;
00301 }
00302 
00303 input::InputBuffer& currentLine()
00304 
00305 /*
00306   Synopsis: returns the current command line.
00307 
00308   Explanation: the idea is to enable functions to pick off arguments from the
00309   command line, so that the user can type forward.
00310 */
00311 
00312 {
00313   return commandLine;
00314 }
00315 
00316 
00317 /*
00318   Synopsis: returns the currently active mode.
00319   Mostly useful for communication with readline.
00320 */
00321 const CommandMode* currentMode()
00322 {
00323   return modeStack.top();
00324 }
00325 
00326 
00327 /*
00328   Synopsis: default error handler, which is called when |str| is not a string
00329   which is recognized by the command tree.
00330 
00331   It prints the name of |str| and an error message.
00332 */
00333 void defaultError(const char* str)
00334 {
00335   std::cout << str << ": not found" << std::endl;
00336 }
00337 
00338 
00339 /*
00340   Synopsis: quits interactive mode, after exiting all active modes.
00341 
00342   This should be called only if none of the modes in the stack can throw on
00343   exit, which seems a reasonable assumption.
00344 */
00345 void exitInteractive()
00346 
00347 {
00348   while (modeStack.size())
00349     exitMode(); // applies to mode at top of stack, which is then popped
00350 
00351   runFlag = false; // this will cause the |run| loop to terminate
00352 }
00353 
00354 
00355 /*
00356   Synopsis: exits the current mode.
00357 */
00358 void exitMode()  // it is assumed that exit functions don't throw
00359 {
00360   modeStack.top()->exit();
00361   modeStack.pop();
00362 }
00363 
00364 // The |TagDict| type is addressed below, but no instance is defined here.
00365 
00366 /*
00367   Synopsis: associates tag with name in t.
00368 
00369   NOTE: this is the only way the sun CC compiler will accept it!
00370 */
00371 
00372 void insertTag(TagDict& t, const char* name, const char* tag)
00373 {
00374   TagDict::value_type v(name,tag); // pair of strings acceptable to |TagDict|
00375   t.insert(v);
00376 }
00377 
00378 
00379 /*
00380   Synopsis: outputs the list of commands with their attached tags.
00381 */
00382 void printTags(std::ostream& strm, const TagDict& t)
00383 {
00384   typedef TagDict::const_iterator I;
00385 
00386   for (I i = t.begin(); i != t.end(); ++i) {
00387     const char* key = i->first;
00388     const char* tag = i->second;
00389     strm << "  - " << key << " : " << tag << std::endl;
00390   }
00391 }
00392 
00393 /*
00394   Synopsis: runs an interactive session of the program.
00395 
00396   Gets commands from the user until it gets the "qq" command, at which time it
00397   returns control.
00398 
00399   It works as follows : get an input string from the user (leading whitespace
00400   is chopped off by default in C++); look it up in the current CommandMode, or,
00401   in case of failure, in its descendants; execute it if it is found, get new
00402   input otherwise.
00403 
00404   The initMode argument is the startup mode of the interactive session;
00405   that is, the session starts by executing the entry function of initMode.
00406 */
00407 void run(const CommandMode& initMode)
00408 {
00409   try
00410   {
00411     activate(initMode); // enter mode, pushing it onto |modeStack|
00412 
00413     const char* name;
00414 
00415     for (runFlag = true; runFlag;)// exit is only through "qq"
00416     {                             // (or "q" in startup mode)
00417 
00418       const CommandMode* mode = modeStack.top();  // get current active mode
00419       name = getCommand(mode);
00420 
00421       std::vector<const char*> ext;
00422       mode->extensions(ext,name);
00423 
00424       switch (ext.size())
00425       {
00426       case 0: // command was not found
00427         mode->error(name);
00428         break;
00429       case 1: // command can be unambiguously completed
00430         execute(ext[0],mode);
00431         break;
00432       default: // ambiguous command; execute it if there is an exact match
00433         if (isEqual(ext[0],name))
00434           execute(ext[0],mode);
00435         else
00436           ambiguous(ext,name);
00437         break;
00438       }
00439     } // for(runFlag)
00440   }
00441   catch(EntryError) { // something is very wrong
00442     return;
00443   }
00444 }
00445 
00446 }
00447 
00448 /*****************************************************************************
00449 
00450         Chapter IV -- Local function definitions
00451 
00452   This section contains the definition of the private functions for this
00453   module :
00454 
00455     - ambiguous(ext,name) : handles multiple completions;
00456     - getIntreractive(strm, name, prompt) : get a command from the user;
00457 
00458 ******************************************************************************/
00459 
00460 namespace {
00461 
00462 
00463 /*
00464   Synopsis: outputs an informative message when name has more than one
00465   completion in the dictionary.
00466 
00467   The message is name: ambiguous ( ... list of possible completions ... )
00468 */
00469 void ambiguous(const std::vector<const char*>& ext, const char* name)
00470 {
00471   typedef std::vector<const char*>::const_iterator I;
00472 
00473   std::cout << name << " : ambiguous (";
00474 
00475   for (I i = ext.begin(); i != ext.end();) {
00476     std::cout << *i;
00477     ++i;
00478     if (i != ext.end())
00479       std::cout << ",";
00480   }
00481 
00482   std::cout << ")" << std::endl;
00483 }
00484 
00485 
00486 /*!
00487   \brief Execute the command "name". Also install default error handling.
00488 
00489   Precondition: name is defined either in the current mode or in one of its
00490   descendants.
00491 
00492   If name is found in the current mode, we simply execute it. Otherwise,
00493   we find out in which descendant mode it is found, we attempt the mode
00494   change, and in case of success, push command on the stack, so that it
00495   will be executed at the next loop in |run|.
00496 */
00497 void execute(const char* name, const CommandMode* mode)
00498 {
00499   CommandMode::const_iterator pos = mode->find(name);
00500 
00501   try
00502   {
00503     if (pos != mode->end()) // the command was found in the current mode
00504     {
00505       const Command& command = pos->second;
00506       command();
00507     }
00508     else // we have to look in a submode
00509       for (size_t j = 0; j < mode->n_desc(); ++j)
00510       {
00511         const CommandMode& next = mode->nextMode(j);
00512         pos = next.findName(name);
00513         if (pos != next.end()) // name is defined in a descendent of next
00514         {
00515           activate(next);
00516           commandStack.push(name); // retry command if mode entry successful
00517           break; // only attempt to enter the first matching descendant
00518         }
00519       }
00520   }
00521   catch (commands::EntryError&) { // silently ignore failure to enter mode
00522   }
00523   catch (error::MemoryOverflow& e) {
00524     e("error: memory overflow");
00525   }
00526   catch (error::InputError& e) {
00527     e("aborted");
00528   }
00529 }
00530 
00531 
00532 /*
00533   Synopsis: gets the name of the next command.
00534 
00535   It is gotten either from the commandStack, if there are commands waiting
00536   to be processed, or interactively from the user.
00537 */
00538 const char* getCommand(const CommandMode* mode)
00539 {
00540   static std::string nameString;
00541   const char* name;
00542 
00543   if (commandStack.size()) { // there is a command to process
00544     name = commandStack.top();
00545     commandStack.pop();
00546   } else {  // get command from user
00547     nameString.erase();
00548     getInteractive(std::cin,nameString,mode->prompt());
00549     name = nameString.c_str();
00550   }
00551 
00552   return name;
00553 }
00554 
00555 
00556 /*
00557   Synopsis: gets a command interactively from the user.
00558 
00559   The actual input line is gotten through the readline library. For convenience
00560   we pack it into an InputBuffer (defined in input.h), in order to have a
00561   C++-like interaction.
00562 */
00563 std::istream& getInteractive(std::istream& strm, std::string& name,
00564                              const char* prompt)
00565 {
00566   using namespace input;
00567 
00568   commandLine.getline(strm,prompt);
00569   commandLine >> name;
00570 
00571   return strm;
00572 }
00573 
00574 } // namespace
00575 
00576 } // namespace atlas

Generated on Wed Mar 26 16:49:34 2008 for atlas by  doxygen 1.3.9.1