From: g0dil Date: Wed, 19 Mar 2008 15:34:54 +0000 (+0000) Subject: Console: Added Module X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=327ff174bdc67db20c64d92c9a171abfa5443333;p=senf.git Console: Added Module Console: Implement very (very) fundamental node tree Console: Implement parser git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@747 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Console/Doxyfile b/Console/Doxyfile new file mode 100644 index 0000000..0a359ce --- /dev/null +++ b/Console/Doxyfile @@ -0,0 +1,10 @@ +@INCLUDE = "$(TOPDIR)/doclib/Doxyfile.global" + +PROJECT_NAME = libConsole +GENERATE_TAGFILE = doc/Console.tag +ALPHABETICAL_INDEX = NO + +TAGFILES = \ + "$(TOPDIR)/Socket/doc/Socket.tag" \ + "$(TOPDIR)/Scheduler/doc/Scheduler.tag" \ + "$(TOPDIR)/Utils/doc/Utils.tag" diff --git a/Console/Mainpage.dox b/Console/Mainpage.dox new file mode 100644 index 0000000..f430b75 --- /dev/null +++ b/Console/Mainpage.dox @@ -0,0 +1,42 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \mainpage The Configuration and Runtime Control Framework + + The Console library implements a runtime interactive (network) console which allows to + configure, control and manipulate a running application in any way. Additionally this library + provides support for configuration files and command line parsing which can be used with or + without the network console. + */ + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// mode: flyspell +// mode: auto-fill +// End: diff --git a/Console/Node.cc b/Console/Node.cc new file mode 100644 index 0000000..964eb22 --- /dev/null +++ b/Console/Node.cc @@ -0,0 +1,77 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Node non-inline non-template implementation */ + +#include "Node.hh" +//#include "Node.ih" + +// Custom includes + +//#include "Node.mpp" +#define prefix_ +///////////////////////////////cc.p//////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +//senf::console::DirectoryNode + +prefix_ void senf::console::DirectoryNode::add(GenericNode::ptr node, bool uniquify) +{ + if (children_.find(node->name()) != children_.end()) { + if (! uniquify) + throw DuplicateNodeNameException() << ": '" << node->name() << "'"; + unsigned suffix (0); + std::string newName; + do { + ++suffix; + newName = node->name() + boost::lexical_cast(suffix); + } while (children_.find(newName) != children_.end()); + name(*node, newName); + } + children_.insert(std::make_pair(node->name(),node)); +} + +prefix_ senf::console::GenericNode & +senf::console::DirectoryNode::lookup(std::string const & name) + const +{ + ChildMap::const_iterator i (children_.find(name)); + if (i == children_.end()) + throw UnknownNodeNameException() << ": '" << name << "'"; + return *(i->second); +} + +///////////////////////////////cc.e//////////////////////////////////////// +#undef prefix_ +//#include "Node.mpp" + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Console/Node.cci b/Console/Node.cci new file mode 100644 index 0000000..88ed2c6 --- /dev/null +++ b/Console/Node.cci @@ -0,0 +1,128 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Node inline non-template implementation */ + +//#include "Node.ih" + +// Custom includes +#include "../Utils/senfassert.hh" + +#define prefix_ inline +///////////////////////////////cci.p/////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// senf::console::GenericNode + +prefix_ std::string const & senf::console::GenericNode::name() + const +{ + return name_; +} + +prefix_ senf::console::GenericNode::GenericNode(std::string const & name, bool managed) + : name_ (name), managed_ (managed) +{} + +prefix_ void senf::console::GenericNode::name(std::string const & name) +{ + name_ = name; +} + +prefix_ void senf::console::GenericNode::name(GenericNode & node, std::string const & name) +{ + node.name_ = name; +} + +prefix_ senf::console::DirectoryNode & senf::console::GenericNode::parent() + const +{ + SENF_ASSERT( parent_ ); + return *parent_; +} + +prefix_ bool senf::console::GenericNode::managed() + const +{ + return managed_; +} + +prefix_ bool senf::console::GenericNode::release() +{ + // Beware ! call release() first so the call is not short-circuited way ! + return intrusive_refcount_base::release() && managed_; +} + +/////////////////////////////////////////////////////////////////////////// +// senf::console::DirectoryNode + +prefix_ void senf::console::DirectoryNode::add(std::auto_ptr node, bool uniquify) +{ + SENF_ASSERT( node->managed() ); + add(GenericNode::ptr(node.release()), uniquify); +} + +prefix_ void senf::console::DirectoryNode::add(GenericNode & node, bool uniquify) +{ + SENF_ASSERT( ! node.managed() ); + add(GenericNode::ptr(&node),uniquify); +} + +prefix_ senf::console::DirectoryNode & +senf::console::DirectoryNode::operator[](std::string const & name) + const +{ + return dynamic_cast(lookup(name)); +} + +prefix_ senf::console::CommandNode & +senf::console::DirectoryNode::operator()(std::string const & name) + const +{ + return dynamic_cast(lookup(name)); +} + +prefix_ senf::console::DirectoryNode::DirectoryNode(std::string const & name, bool managed) + : GenericNode(name, managed) +{} + +/////////////////////////////////////////////////////////////////////////// +// senf::console::CommandNode + +prefix_ senf::console::CommandNode::CommandNode(std::string const & name, bool managed) + : GenericNode(name, managed) +{} + +///////////////////////////////cci.e/////////////////////////////////////// +#undef prefix_ + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Console/Node.hh b/Console/Node.hh new file mode 100644 index 0000000..154bc97 --- /dev/null +++ b/Console/Node.hh @@ -0,0 +1,140 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Node public header */ + +#ifndef HH_Node_ +#define HH_Node_ 1 + +// Custom includes +#include +#include +#include +#include "../Utils/intrusive_refcount.hh" +#include "../Utils/Exception.hh" + +//#include "Node.mpp" +///////////////////////////////hh.p//////////////////////////////////////// + +namespace senf { +namespace console { + + class DirectoryNode; + class CommandNode; + + /** \brief + */ + class GenericNode + : public intrusive_refcount_t + { + public: + /////////////////////////////////////////////////////////////////////////// + // Types + + typedef boost::intrusive_ptr ptr; + + /////////////////////////////////////////////////////////////////////////// + + std::string const & name() const; + DirectoryNode & parent() const; + bool managed() const; + + protected: + explicit GenericNode(std::string const & name, bool managed = false); + + void name(std::string const & name); + static void name(GenericNode & node, std::string const & name); + void parent(DirectoryNode * parent); + + private: + bool release(); + + std::string name_; + bool managed_; + DirectoryNode * parent_; + + friend class intrusive_refcount_base; + }; + + /** \brief + */ + class DirectoryNode : public GenericNode + { + public: + typedef boost::intrusive_ptr ptr; + + void add(std::auto_ptr node, bool uniquify = true); + void add(GenericNode & node, bool uniquify = true); + + DirectoryNode & operator[](std::string const & name) const; + CommandNode & operator()(std::string const & name) const; + + protected: + explicit DirectoryNode(std::string const & name, bool managed = false); + + private: + void add(GenericNode::ptr node, bool uniquify); + GenericNode & lookup(std::string const & name) const; + + typedef std::map ChildMap; + ChildMap children_; + }; + + struct DuplicateNodeNameException : public senf::Exception + { DuplicateNodeNameException() : senf::Exception("Duplicate node name") {}}; + + struct UnknownNodeNameException : public senf::Exception + { UnknownNodeNameException() : senf::Exception("Unknown node name") {}}; + + /** \brief + */ + class CommandNode : public GenericNode + { + public: + typedef boost::intrusive_ptr ptr; + + protected: + explicit CommandNode(std::string const & name, bool managed = false); + + private: + + }; + +}} + +///////////////////////////////hh.e//////////////////////////////////////// +#include "Node.cci" +//#include "Node.ct" +//#include "Node.cti" +#endif + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Console/Node.test.cc b/Console/Node.test.cc new file mode 100644 index 0000000..2170d42 --- /dev/null +++ b/Console/Node.test.cc @@ -0,0 +1,53 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Node.test unit tests */ + +//#include "Node.test.hh" +//#include "Node.test.ih" + +// Custom includes +#include "Node.hh" + +#include +#include + +#define prefix_ +///////////////////////////////cc.p//////////////////////////////////////// + +BOOST_AUTO_UNIT_TEST(node) +{} + +///////////////////////////////cc.e//////////////////////////////////////// +#undef prefix_ + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Console/Parse.cc b/Console/Parse.cc new file mode 100644 index 0000000..502dd1c --- /dev/null +++ b/Console/Parse.cc @@ -0,0 +1,182 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Parse non-inline non-template implementation */ + +#include "Parse.hh" +#include "Parse.ih" + +// Custom includes +#include + +//#include "Parse.mpp" +#define prefix_ +///////////////////////////////cc.p//////////////////////////////////////// + +namespace senf { +namespace console { +namespace detail { + + struct ParserAccess + { + static void init(ParseCommandInfo & info) + { info.init(); } + + static void setCommand(ParseCommandInfo & info, std::string const & commandPath) + { info.setCommand(commandPath); } + + static void startArgument(ParseCommandInfo & info) + { info.startArgument(); } + + static void endArgument(ParseCommandInfo & info) + { info.endArgument(); } + + static void addToken(ParseCommandInfo & info, ArgumentToken const & token) + { info.addToken(token); } + + static void finalize(ParseCommandInfo & info) + { info.finalize(); } + + static ArgumentToken makeToken(std::string const & token) + { return ArgumentToken(token); } + }; + + struct ParseDispatcher + { + ParseDispatcher() + : info_ (0) {} + + ParseCommandInfo * info_; + + ParseCommandInfo & info() { + SENF_ASSERT( info_ ); + return *info_; + } + + struct BindInfo { + BindInfo( ParseDispatcher & d, ParseCommandInfo & i) + : dispatcher (d) { dispatcher.info_ = &i; } + + ~BindInfo() { dispatcher.info_ = 0; } + + ParseDispatcher & dispatcher; + }; + + void beginCommand(std::string const & command) + { ParserAccess::init(info()); + ParserAccess::setCommand(info(), command); } + + void endCommand() + { ParserAccess::finalize(info()); } + + void pushArgument(std::string const & argument) + { ParserAccess::startArgument(info()); + ParserAccess::addToken(info(), ParserAccess::makeToken(argument)); + ParserAccess::endArgument(info()); } + + void openGroup() + { ParserAccess::startArgument(info()); } + + void closeGroup() + { ParserAccess::endArgument(info()); } + + void pushPunctuation(std::string const & token) + { ParserAccess::addToken(info(), ParserAccess::makeToken(token)); } + + void pushWord(std::string const & token) + { ParserAccess::addToken(info(), ParserAccess::makeToken(token)); } + }; + +}}} + +/////////////////////////////////////////////////////////////////////////// +// senf::console::ParseCommandInfo + +struct senf::console::ParseCommandInfo::MakeRange +{ + MakeRange() {} + MakeRange(ParseCommandInfo::token_iterator b) : b_ (b) {} + + senf::console::ParseCommandInfo::token_iterator b_; + + typedef ParseCommandInfo::argument_value_type result_type; + + result_type operator()(TempArguments::iterator::value_type const & v) const { + return result_type( b_ + v.first, b_ + v.second ); + } +}; + +prefix_ void senf::console::ParseCommandInfo::finalize() +{ + arguments_.resize( tempArguments_.size() ); + + std::copy( boost::make_transform_iterator( tempArguments_.begin(), + MakeRange(tokens_.begin()) ), + boost::make_transform_iterator( tempArguments_.end(), + MakeRange() ), + arguments_.begin() ); + + tempArguments_.clear(); +} + +/////////////////////////////////////////////////////////////////////////// +// senf::console::SingleCommandParser + +struct senf::console::SingleCommandParser::Impl +{ + detail::ParseDispatcher dispatcher; + detail::CommandGrammar::Context context; + detail::CommandGrammar grammar; + detail::SkipGrammar skipGrammar; + + Impl() : dispatcher(), context(), grammar(dispatcher, context) {} +}; + +prefix_ senf::console::SingleCommandParser::SingleCommandParser() + : impl_ (new Impl()) +{} + +prefix_ senf::console::SingleCommandParser::~SingleCommandParser() +{} + +prefix_ bool senf::console::SingleCommandParser::parseCommand(std::string command, + ParseCommandInfo & info) +{ + detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info); + return boost::spirit::parse( command.c_str(), impl().grammar, impl().skipGrammar ).full; +} + +///////////////////////////////cc.e//////////////////////////////////////// +#undef prefix_ +//#include "Parse.mpp" + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Console/Parse.cci b/Console/Parse.cci new file mode 100644 index 0000000..1a72abf --- /dev/null +++ b/Console/Parse.cci @@ -0,0 +1,149 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Parse inline non-template implementation */ + +// We do NOT want to include the complete parser definition into every other compilation unit +// (disabled) #include "Parse.ih" + +// Custom includes +#include "../Utils/senfassert.hh" + +#define prefix_ inline +///////////////////////////////cci.p/////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// senf::console::ArgumentToken + +prefix_ std::string const & senf::console::ArgumentToken::value() + const +{ + return token_; +} + +prefix_ senf::console::ArgumentToken::ArgumentToken(std::string token) + : token_ (token) +{} + +/////////////////////////////////////////////////////////////////////////// +// senf::console::ParseCommandInfo + +prefix_ std::string const & senf::console::ParseCommandInfo::commandPath() + const +{ + return commandPath_; +} + +prefix_ senf::console::ParseCommandInfo::size_type senf::console::ParseCommandInfo::arguments() + const +{ + return arguments_.size(); +} + +prefix_ senf::console::ParseCommandInfo::argument_iterator +senf::console::ParseCommandInfo::begin_arguments() + const +{ + return arguments_.begin(); +} + +prefix_ senf::console::ParseCommandInfo::argument_iterator +senf::console::ParseCommandInfo::end_arguments() + const +{ + return arguments_.end(); +} + +prefix_ senf::console::ParseCommandInfo::size_type senf::console::ParseCommandInfo::tokens() + const +{ + return tokens_.size(); +} + +prefix_ senf::console::ParseCommandInfo::token_iterator +senf::console::ParseCommandInfo::begin_tokens() + const +{ + return tokens_.begin(); +} + +prefix_ senf::console::ParseCommandInfo::token_iterator +senf::console::ParseCommandInfo::end_tokens() + const +{ + return tokens_.end(); +} + +//////////////////////////////////////// +// private members + +prefix_ void senf::console::ParseCommandInfo::init() +{ + commandPath_ = ""; + tokens_.clear(); + arguments_.clear(); + tempArguments_.clear(); +} + +prefix_ void senf::console::ParseCommandInfo::setCommand(std::string const & commandPath) +{ + commandPath_ = commandPath; +} + +prefix_ void senf::console::ParseCommandInfo::startArgument() +{ + tempArguments_.push_back( TempArgumentRange( tokens_.size(), tokens_.size() ) ); +} + +prefix_ void senf::console::ParseCommandInfo::endArgument() +{ + tempArguments_.back().second = tokens_.size(); +} + +prefix_ void senf::console::ParseCommandInfo::addToken(ArgumentToken const & token) +{ + tokens_.push_back(token); +} + +/////////////////////////////////////////////////////////////////////////// +// senf::console::SingleCommandParser + +prefix_ senf::console::SingleCommandParser::Impl & senf::console::SingleCommandParser::impl() +{ + SENF_ASSERT(impl_); + return *impl_; +} + +///////////////////////////////cci.e/////////////////////////////////////// +#undef prefix_ + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Console/Parse.hh b/Console/Parse.hh new file mode 100644 index 0000000..4a5d32a --- /dev/null +++ b/Console/Parse.hh @@ -0,0 +1,159 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Parse public header */ + +#ifndef HH_Parse_ +#define HH_Parse_ 1 + +// Custom includes +#include +#include +#include +#include +#include + +//#include "Parse.mpp" +///////////////////////////////hh.p//////////////////////////////////////// + +namespace senf { +namespace console { + + namespace detail { struct ParserAccess; } + + /** \brief + */ + class ArgumentToken + { + public: + std::string const & value() const; + + protected: + + private: + explicit ArgumentToken(std::string token); + + std::string token_; + + friend class detail::ParserAccess; + }; + + + /** \brief + */ + class ParseCommandInfo + { + typedef std::vector Tokens; + + public: + typedef Tokens::const_iterator token_iterator; + typedef boost::iterator_range argument_value_type; + + private: + typedef std::vector Arguments; + + public: + typedef Arguments::const_iterator argument_iterator; + typedef Arguments::size_type size_type; + + std::string const & commandPath() const; + + size_type arguments() const; + argument_iterator begin_arguments() const; + argument_iterator end_arguments() const; + + size_type tokens() const; + token_iterator begin_tokens() const; + token_iterator end_tokens() const; + + protected: + + private: + void init(); + void setCommand(std::string const & commandPath); + void startArgument(); + void endArgument(); + void addToken(ArgumentToken const & token); + void finalize(); + + struct MakeRange; + + std::string commandPath_; + + typedef std::pair TempArgumentRange; + typedef std::vector TempArguments; + + Tokens tokens_; + Arguments arguments_; + TempArguments tempArguments_; + + friend class detail::ParserAccess; + }; + + /** \brief + */ + class SingleCommandParser + : boost::noncopyable + { + public: + /////////////////////////////////////////////////////////////////////////// + // Types + + /////////////////////////////////////////////////////////////////////////// + ///\name Structors and default members + ///@{ + + SingleCommandParser(); + ~SingleCommandParser(); + + ///@} + /////////////////////////////////////////////////////////////////////////// + + bool parseCommand(std::string command, ParseCommandInfo & info); + + private: + struct Impl; + + Impl & impl(); + + boost::scoped_ptr impl_; + }; + +}} + +///////////////////////////////hh.e//////////////////////////////////////// +#include "Parse.cci" +//#include "Parse.ct" +//#include "Parse.cti" +#endif + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Console/Parse.ih b/Console/Parse.ih new file mode 100644 index 0000000..d5a9e2d --- /dev/null +++ b/Console/Parse.ih @@ -0,0 +1,258 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Parse internal header */ + +#ifndef IH_Parse_ +#define IH_Parse_ 1 + +// Custom includes +#include +#include +#include +#include +#include +#include +#include + +///////////////////////////////ih.p//////////////////////////////////////// + +namespace senf { +namespace console { +namespace detail { + + struct append_action + { + template + void act(T & ref, Value const & value) const + { ref += T(1, value); } + + template + void act(T & ref, Iterator const & f, Iterator const & l) const + { ref += T(f,l); } + }; + + template + inline boost::spirit::ref_value_actor + append_a(T & ref) + { + return boost::spirit::ref_value_actor(ref); + } + + template + inline boost::spirit::ref_const_ref_actor + append_a(T & ref, Value const & value) + { + return boost::spirit::ref_const_ref_actor(ref, value); + } + + template + struct CommandGrammar : boost::spirit::grammar > + { + /////////////////////////////////////////////////////////////////////////// + // The parse context (variables needed while parsing) + + struct Context { + std::string str; + char ch; + }; + + Context & context; + + /////////////////////////////////////////////////////////////////////////// + // Dispatching semantic actions + + ParseDispatcher & dispatcher; + + struct Dispatch_actor + { + Dispatch_actor(boost::function fn_) : fn (fn_) {} + + template + void operator()(Value const & value) const + { fn(); } + + template + void operator()(Iterator const & f, Iterator const & l) const + { fn(); } + + boost::function fn; + }; + + template + Dispatch_actor dispatch(Callback cb) const + { return Dispatch_actor(boost::bind(cb, boost::ref(dispatcher))); } + + template + Dispatch_actor dispatch(Callback cb, Arg const & arg) const + { return Dispatch_actor(boost::bind(cb, boost::ref(dispatcher), arg)); } + + /////////////////////////////////////////////////////////////////////////// + + CommandGrammar(ParseDispatcher & d, Context & c) + : context(c), dispatcher(d) {} + + template + struct definition + { + boost::spirit::rule command, path, argument, word, string, hexstring, token; + boost::spirit::rule punctuation, hexbyte, balanced_tokens, simple_argument; + boost::spirit::rule complex_argument; + + definition(CommandGrammar const & self) { + using namespace boost::spirit; + typedef ParseDispatcher PD; + + command + = path [ self.dispatch(&PD::beginCommand, + boost::cref(self.context.str)) ] + >> * argument + >> ! ch_p(';') + >> eps_p [ self.dispatch(&PD::endCommand) ] + ; + + argument + = simple_argument [ self.dispatch(&PD::pushArgument, + boost::cref(self.context.str)) ] + | complex_argument + ; + + simple_argument // All these return their value in context.str + = string + | hexstring + | path + ; + + complex_argument // Argument consists of multiple tokens + = ch_p('(') [ self.dispatch(&PD::openGroup) ] + >> * token + >> ch_p(')') [ self.dispatch(&PD::closeGroup) ] + ; + + string // Returns value in context.str + = eps_p [ clear_a(self.context.str) ] + >> lexeme_d + [ + ch_p('"') + >> * ( ( lex_escape_ch_p[ assign_a(self.context.ch) ] + - '"' + ) [ append_a(self.context.str, + self.context.ch) ] + ) + >> ch_p('"') + ] + ; + + hexstring // Returns value in context.str + = eps_p [ clear_a(self.context.str) ] + >> confix_p( "x\"", * hexbyte, '"' ) + ; + + path // Returns value in context.str + = eps_p [ clear_a(self.context.str) ] + >> ( ! ch_p('/') [ append_a(self.context.str) ] + ) + >> ( word [ append_a(self.context.str) ] + % ch_p('/') [ append_a(self.context.str) ] + ) + ; + + balanced_tokens + = ch_p('(') [ self.dispatch(&PD::pushPunctuation, "(") ] + >> * token + >> ch_p(')') [ self.dispatch(&PD::pushPunctuation, ")") ] + ; + + token + = simple_argument [ self.dispatch(&PD::pushWord, + boost::cref(self.context.str)) ] + | punctuation [ self.dispatch(&PD::pushPunctuation, + boost::cref(self.context.str)) ] + | balanced_tokens + ; + + punctuation // Returns value in context.str + = regex_p("[,=]") [ assign_a(self.context.str) ] + ; + + word + = regex_p("[^ \t\n\r;,=(){}/\"]+") + ; + + hexbyte + = uint_parser() + [ append_a(self.context.str) ] + ; + + BOOST_SPIRIT_DEBUG_RULE(command); + BOOST_SPIRIT_DEBUG_RULE(path); + BOOST_SPIRIT_DEBUG_RULE(argument); + BOOST_SPIRIT_DEBUG_RULE(word); + BOOST_SPIRIT_DEBUG_RULE(string); + BOOST_SPIRIT_DEBUG_RULE(hexstring); + BOOST_SPIRIT_DEBUG_RULE(token); + BOOST_SPIRIT_DEBUG_RULE(punctuation); + BOOST_SPIRIT_DEBUG_RULE(hexbyte); + BOOST_SPIRIT_DEBUG_RULE(balanced_tokens); + BOOST_SPIRIT_DEBUG_RULE(simple_argument); + BOOST_SPIRIT_DEBUG_RULE(complex_argument); + } + + boost::spirit::rule const & start() const { return command; } + }; + }; + + struct SkipGrammar + : public boost::spirit::grammar + { + template + struct definition + { + boost::spirit::rule rule; + + definition(SkipGrammar const & self) { + rule + = boost::spirit::regex_p("[ \t]+") + | boost::spirit::comment_p('#') + ; + } + + boost::spirit::rule const & start() const { return rule; } + }; + }; + +}}} + +///////////////////////////////ih.e//////////////////////////////////////// +#endif + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Console/Parse.test.cc b/Console/Parse.test.cc new file mode 100644 index 0000000..a6d170b --- /dev/null +++ b/Console/Parse.test.cc @@ -0,0 +1,168 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Parse.test unit tests */ + +//#include "Parse.test.hh" +//#include "Parse.test.ih" + +// Custom includes +#include +#include "Parse.hh" +#include "Parse.ih" + +#include +#include + +#define prefix_ +///////////////////////////////cc.p//////////////////////////////////////// + +namespace +{ + struct TestParseDispatcher + { + TestParseDispatcher(std::ostream & os) : os_ (os) {} + + std::ostream & os_; + + void beginCommand(std::string const & command) + { os_ << "beginCommand( " << command << " )\n"; } + void endCommand() + { os_ << "endCommand()\n"; } + + void pushArgument(std::string const & argument) + { os_ << "pushArgument( " << argument << " )\n"; } + void openGroup() + { os_ << "openGroup()\n"; } + void closeGroup() + { os_ << "closeGroup()\n"; } + void pushPunctuation(std::string const & token) + { os_ << "pushPunctuation( " << token << " )\n"; } + void pushWord(std::string const & token) + { os_ << "pushWord( " << token << " )\n"; } + }; +} + +BOOST_AUTO_UNIT_TEST(commandParser) +{ + senf::console::detail::CommandGrammar::Context context; + std::stringstream ss; + TestParseDispatcher dispatcher (ss); + senf::console::detail::CommandGrammar grammar (dispatcher, context); + senf::console::detail::SkipGrammar skipGrammar; + + char text[] = + "# Comment\n" + "doo / bii / doo arg/../path" + " flab::blub" + " 123.434>a" + " (a,b,c (huhu))" + " \"foo\\\"bar\" #\n" + " x\"01 02 # Inner comment\n" + " 0304\""; + + BOOST_CHECK( boost::spirit::parse( + text, + grammar, + skipGrammar ) . full ); + BOOST_CHECK_EQUAL( ss.str(), + "beginCommand( doo/bii/doo )\n" + "pushArgument( arg/../path )\n" + "pushArgument( flab::blub )\n" + "pushArgument( 123.434>a )\n" + "openGroup()\n" + "pushWord( a )\n" + "pushPunctuation( , )\n" + "pushWord( b )\n" + "pushPunctuation( , )\n" + "pushWord( c )\n" + "pushPunctuation( ( )\n" + "pushWord( huhu )\n" + "pushPunctuation( ) )\n" + "closeGroup()\n" + "pushArgument( foo\"bar )\n" + "pushArgument( \x01\x02\x03\x04 )\n" + "endCommand()\n" ); +} + +BOOST_AUTO_UNIT_TEST(singleCommandParser) +{ + senf::console::SingleCommandParser parser; + + char const text[] = + "# Comment\n" + "doo / bii / doo arg/../path" + " flab::blub" + " 123.434>a" + " (a,b,c (huhu))" + " \"foo\\\"bar\" #\n" + " x\"01 02 # Inner comment\n" + " 0304\""; + + senf::console::ParseCommandInfo info; + BOOST_CHECK( parser.parseCommand(text, info) ); + + BOOST_CHECK_EQUAL( info.commandPath(), "doo/bii/doo" ); + BOOST_REQUIRE_EQUAL( info.arguments(), 6u ); + BOOST_REQUIRE_EQUAL( info.tokens(), 13u ); + + char const * tokens[] = { "arg/../path", + "flab::blub", + "123.434>a", + "a", ",", "b", ",", "c", "(", "huhu", ")", + "foo\"bar", + "\x01\x02\x03\x04" }; + + BOOST_REQUIRE_EQUAL( info.begin_arguments()[0].size(), 1u ); + BOOST_CHECK_EQUAL( info.begin_arguments()[0].begin()->value(), tokens[0] ); + + BOOST_REQUIRE_EQUAL( info.begin_arguments()[1].size(), 1u ); + BOOST_CHECK_EQUAL( info.begin_arguments()[1].begin()->value(), tokens[1] ); + + BOOST_REQUIRE_EQUAL( info.begin_arguments()[2].size(), 1u ); + BOOST_CHECK_EQUAL( info.begin_arguments()[2].begin()->value(), tokens[2] ); + + BOOST_REQUIRE_EQUAL( info.begin_arguments()[3].size(), 8u ); + for (unsigned i (0); i<8; ++i) + BOOST_CHECK_EQUAL( info.begin_arguments()[3].begin()[i].value(), tokens[3+i] ); + + BOOST_REQUIRE_EQUAL( info.begin_arguments()[4].size(), 1u ); + BOOST_CHECK_EQUAL( info.begin_arguments()[4].begin()->value(), tokens[11] ); + + BOOST_REQUIRE_EQUAL( info.begin_arguments()[5].size(), 1u ); + BOOST_CHECK_EQUAL( info.begin_arguments()[5].begin()->value(), tokens[12] ); +} + +///////////////////////////////cc.e//////////////////////////////////////// +#undef prefix_ + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Console/SConscript b/Console/SConscript new file mode 100644 index 0000000..ce543c3 --- /dev/null +++ b/Console/SConscript @@ -0,0 +1,14 @@ +# -*- python -*- + +Import('env') +import SENFSCons + +########################################################################### + +sources = SENFSCons.GlobSources() +SENFSCons.StandardTargets(env) +SENFSCons.Lib(env, + library = 'Console', + sources = sources, + LIBS = [ 'Socket', 'Scheduler', 'Utils' ]) +SENFSCons.Doxygen(env) diff --git a/Console/main.test.cc b/Console/main.test.cc new file mode 100644 index 0000000..b6d1686 --- /dev/null +++ b/Console/main.test.cc @@ -0,0 +1,49 @@ +// $Id$ +// +// Copyright (C) 2006 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +// Definition of non-inline non-template functions + +//#include "test.hh" +//#include "test.ih" + +// Custom includes +#define BOOST_AUTO_TEST_MAIN +#include "../Utils/auto_unit_test.hh" +#include + +#define prefix_ +///////////////////////////////cc.p//////////////////////////////////////// + + +///////////////////////////////cc.e//////////////////////////////////////// +#undef prefix_ + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// comment-column: 40 +// End: