Advertisement

help with a command parser loop?

Started by May 11, 2002 08:35 PM
11 comments, last by AbsolutDew 22 years, 9 months ago
Uhm. Sorry to keep asking stupid questions, but I can't figure out what is wrong with this. The first command you put in, you need to hit enter twice, after that it only recognizes commands every other iteration. ( I type quit and it says unrecognized command, then type quit again and it stops )
    
        string strCommand;
	bool bDone = false;
	
	while( bDone == false )
	{
		cout << endl;
		cout << "> ";
		getline( cin, strCommand );
		cout << endl;
		
		transform( strCommand.begin(), strCommand.end(), strCommand.begin(), tolower );

		if( strCommand == "quit" )
		{
			bDone = true;
		}
		else if( strCommand == "stats" )
			cout << "Stats" << endl;
		else
			cout << "Unrecognized Command." << endl;
	}
    
while i'm here, does anyone know a better way to organize this? the list will probably get really ugly when i have like 20 commands or more. [edited by - absolutdew on May 11, 2002 9:37:04 PM]
feel like posting getline. i dont see the prob. doesnt mean its in getline just means i dunno.
Advertisement
well, what complier do you use, does it have a debugger? if it does find out what strCommand is the time it doesn''t work
declspec: getline is a standard function

Edit: Test, it compiles and run fine in VC7 and g++ 2.95.2 (Solaris)

Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost | STLport | FLTK | ACCU Recommended Books ]


[edited by - Fruny on May 11, 2002 10:27:18 PM]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
As for organisation (C-style, C++ising it is up to you)


    struct interpreter_t{  const char* keyword;    // const lets us convert implicitely  void (*command)(void);};interpreter_t table[] ={   "foo", do_quit,   "", NULL};void execute( string input ){   interpreter_t* ptr = table;   while( ptr->command )   {      if( ptr->keyword == input )      {         ptr->command();         break;      }      ++ptr;   }}    


Ahh.. memories of MUD programming

Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost | STLport | FLTK | ACCU Recommended Books ]


[edited by - Fruny on May 11, 2002 10:37:49 PM]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
post it anyway! lol *sighs at self*
Advertisement
declspec, try here: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcstdlib/html/vclrf_istream_Basicistreamgetline.asp

I'm usin vc6. Fruny it worked ok for you? It does like it's suppose to when i use cin.. but i'd like to be able to use more then one word for commands. I watched strCommand, and it did contain quit. It seems to skip over the if statements until the next time around.

this is what it looks like for me:

> stats (hit return)(hit return)Stats> stats (hit return)Unrecognized Command.> (hit return)Stats> quit (hit return)Unrecognized Command.> (hit return)Press any key to continue  


[edited by - absolutdew on May 11, 2002 12:45:35 AM]
i stepped through with the debugger like you recommended. I type in quit, and strCommand stays blank going to the final else statement. (unrecognized command) Then the next time through, right after the while statement, strCommand get's the value i typed in previously, and quits.

I think im not using getline right..

[edited by - absolutdew on May 12, 2002 1:23:29 AM]
The Dinkumware-supplied STL that comes with MSVC is buggy. Grab the fixes here.
Ok, once your compiler is fixed, try this

Interpreter.h

        #ifndef INTERPRETER_HEADER#define INTERPRETER_HEADER#include <string>#include <vector>#include <map>#include <iostream>#include <algorithm>#include <iterator>#include <boost/tokenizer.hpp>class Interpreter{public:	typedef std::vector<std::string> arg_type;	typedef void (*command_type)( Interpreter&, arg_type& );private:	typedef std::map<std::string, command_type> table_type;	table_type table_;	std::ostream& os_;	bool done_;	const std::string prompt_;	command_type error_;	Interpreter* parent_;	void Execute( Interpreter& it, arg_type& args );	void AddCommand( const std::string& key, command_type cmd );public:	Interpreter( std::ostream& os, const std::string& prompt = "", 				 command_type error = NULL, Interpreter* parent = NULL );	inline void Register( const std::string& key, command_type cmd ) { table_[key] = cmd; }	inline bool done() { return done_; };	void operator()( std::istream& is );	inline std::ostream& os() { return os_; };	inline const std::string& prompt() { return prompt_; };	inline void finish() { done_ = true; };	inline void error( Interpreter& i, arg_type& args );};extern void DoNothing( Interpreter&, Interpreter::arg_type& );extern void DoQuit( Interpreter&, Interpreter::arg_type& );extern void DoError( Interpreter&, Interpreter::arg_type& );#endif  


Intepreter.cpp


  #include "Interpreter.h"Interpreter::Interpreter( std::ostream& os, const std::string& prompt, 						  command_type error, Interpreter* parent ) : os_( os ), done_( false ), error_( error ), prompt_( prompt ), parent_( parent ){};voidInterpreter::operator ()( std::istream& is ){	done_ = false;	std::string str;	os() << prompt();	std::getline( is, str );	if (str.empty()) return;	std::transform( str.begin(), str.end(), str.begin(), tolower );	boost::tokenizer<> tok ( str );	arg_type args( tok.begin(), tok.end() );	if( args.empty() ) return;	Execute( *this, args );}voidInterpreter::Execute( Interpreter& it, arg_type& args ){		table_type::iterator itor = table_.find( args[0] );	if( itor != table_.end() && itor->second )	{		itor->second( it, args );		return;	};	if( parent_ )	{		parent_->Execute( it, args );		return;	}	if( error_ )	{		error_( it, args );		return;	}	os() << "Unrecognised command '" << args[0] << "'" << std::endl;}void DoError( Interpreter& i, Interpreter::arg_type& args ) {	i.os() << "Unrecognised command '" << args[0] << "'" << std::endl;};void DoNothing( Interpreter&, Interpreter::arg_type& ) {};void DoQuit( Interpreter& i, Interpreter::arg_type& ) { 	i.finish(); };  


main.cpp

  #include "Interpreter.h"void DoPrint( Interpreter& i, Interpreter::arg_type& args ) {         i.os() << "-------> ";	std::copy( args.begin() + 1, args.end(), std::ostream_iterator<const std::string>( i.os(), " | " ) );        i.os() << std::endl;}voidDoPrinter( Interpreter& i, Interpreter::arg_type& ){	i.os() << "Starting Printer Shell" << std::endl;	Interpreter printer( std::cout, "Printer> ", NULL, &i );	printer.Register( "print", &DoPrint );	printer.Register( "printer", &DoNothing );	while( !printer.done() ) printer( std::cin );	i.os() << "Stopping Printer Shell" << std::endl;}int main(int argc, char* argv[]){	Interpreter shell( std::cout, "Shell> ", NULL, NULL );	shell.Register( "quit", &DoQuit );	shell.Register( "printer", &DoPrinter );	while( !shell.done() ) shell( std::cin );	return 0;}    


I used the boost::tokenizer class to do the parsing of the string, but you could do without it.

You can also use boost::lexical_cast to do conversions from strings to numerical values in the arguments.

Comments welcome ! (even if my code isn't commented )

Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost. | STLport | FLTK | ACCU Recommended Books ]


[edited by - Fruny on May 12, 2002 3:29:07 AM]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan

This topic is closed to new replies.

Advertisement