Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r69503 - in trunk/libs/spirit: optimization/qi repository/doc repository/doc/qi repository/example/qi repository/test repository/test/qi
From: joel_at_[hidden]
Date: 2011-03-02 19:32:53


Author: djowel
Date: 2011-03-02 19:32:48 EST (Wed, 02 Mar 2011)
New Revision: 69503
URL: http://svn.boost.org/trac/boost/changeset/69503

Log:
teejay's keyword directive
Added:
   trunk/libs/spirit/optimization/qi/keywords.cpp (contents, props changed)
   trunk/libs/spirit/repository/doc/qi/kwd.qbk (contents, props changed)
   trunk/libs/spirit/repository/example/qi/keywords.cpp (contents, props changed)
   trunk/libs/spirit/repository/test/qi/keywords.cpp (contents, props changed)
Text files modified:
   trunk/libs/spirit/optimization/qi/Jamfile | 11 +++++++++--
   trunk/libs/spirit/repository/doc/qi.qbk | 3 ++-
   trunk/libs/spirit/repository/doc/qi/directives.qbk | 1 +
   trunk/libs/spirit/repository/example/qi/Jamfile | 2 +-
   trunk/libs/spirit/repository/test/Jamfile | 3 ++-
   5 files changed, 15 insertions(+), 5 deletions(-)

Modified: trunk/libs/spirit/optimization/qi/Jamfile
==============================================================================
--- trunk/libs/spirit/optimization/qi/Jamfile (original)
+++ trunk/libs/spirit/optimization/qi/Jamfile 2011-03-02 19:32:48 EST (Wed, 02 Mar 2011)
@@ -5,9 +5,16 @@
 # Distributed under the Boost Software License, Version 1.0. (See accompanying
 # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 #==============================================================================
-project spirit-qi-benchmark ;
-
+project spirit-qi-benchmark
+ : requirements
+ <include>.
+ <toolset>gcc:<cxxflags>-ftemplate-depth-300
+ <toolset>darwin:<cxxflags>-ftemplate-depth-300
+ :
+ :
+ ;
 # performance tests
 exe int_parser : int_parser.cpp ;
 exe real_parser : real_parser.cpp ;
 exe attr_vs_actions : attr_vs_actions.cpp ;
+exe keywords : keywords.cpp ;

Added: trunk/libs/spirit/optimization/qi/keywords.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/spirit/optimization/qi/keywords.cpp 2011-03-02 19:32:48 EST (Wed, 02 Mar 2011)
@@ -0,0 +1,553 @@
+/*=============================================================================
+ Copyright (c) 2001-2011 Joel de Guzman
+ Copyright (c) 2011 Thomas Bernard
+
+ Distributed under the Boost Software License, Version 1.0. (See accompanying
+ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================*/
+
+#define FUSION_MAX_VECTOR_SIZE 50
+#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
+#define BOOST_MPL_LIMIT_LIST_SIZE 50
+#define BOOST_MPL_LIMIT_VECTOR_SIZE 50
+
+#include "../measure.hpp"
+#include <boost/config/warning_disable.hpp>
+#include <boost/spirit/include/qi.hpp>
+#include <boost/spirit/include/phoenix_core.hpp>
+#include <boost/spirit/include/phoenix_operator.hpp>
+#include <boost/spirit/include/phoenix_object.hpp>
+#include <boost/spirit/include/phoenix_fusion.hpp>
+#include <boost/spirit/include/phoenix_container.hpp>
+#include <boost/fusion/include/adapt_struct.hpp>
+#include <boost/fusion/include/io.hpp>
+#include <boost/spirit/include/qi_permutation.hpp>
+#include <boost/spirit/home/qi/string/tst_map.hpp>
+#include <boost/spirit/repository/include/qi_kwd.hpp>
+#include <boost/spirit/repository/include/qi_keywords.hpp>
+#include <boost/optional.hpp>
+#include <boost/spirit/home/phoenix/core/argument.hpp>
+#include <boost/spirit/home/phoenix/bind/bind_member_variable.hpp>
+
+#include <iostream>
+#include <string>
+#include <complex>
+#include <vector>
+#include <iterator>
+#include <stdexcept>
+
+#include <boost/preprocessor/control/if.hpp>
+#include <boost/preprocessor/seq/for_each_i.hpp>
+#include <boost/preprocessor/seq/size.hpp>
+#include <boost/preprocessor/cat.hpp>
+#include <boost/preprocessor/stringize.hpp>
+
+#define KEYS_5
+
+#include "keywords.hpp"
+
+#define declOptions(r, data, i, elem) boost::optional<int> BOOST_PP_CAT(option,i);
+#define fusionOptions(r, data, i, elem) (boost::optional<int>, BOOST_PP_CAT(option,i))
+
+
+namespace client
+{
+ namespace qi = boost::spirit::qi;
+ namespace ascii = boost::spirit::ascii;
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Our parsedData struct
+ ///////////////////////////////////////////////////////////////////////////
+ //[tutorial_parsedData_struct
+ struct parsedDataOptions
+ {
+ BOOST_PP_SEQ_FOR_EACH_I(declOptions,_,keys)
+ };
+ struct parsedData
+ {
+
+ std::string name;
+ parsedDataOptions options;
+ void clear()
+ {
+ name.clear();
+ }
+ };
+
+ struct parsedData2
+ {
+ std::string name;
+ BOOST_PP_SEQ_FOR_EACH_I(declOptions,_,keys)
+
+ void clear()
+ {
+ name.clear();
+ }
+ };
+}
+
+std::ostream &operator<<(std::ostream & os, client::parsedData &data)
+{
+ os << data.name <<std::endl;
+
+#define generateOutput1(r, d, i, elem) if( BOOST_PP_CAT(data.options.option, i) ) os<< BOOST_PP_STRINGIZE( BOOST_PP_CAT(option,i)) <<" "<< * BOOST_PP_CAT(data.options.option , i)<<std::endl;
+ BOOST_PP_SEQ_FOR_EACH_I(generateOutput1,_,keys)
+
+ os<<std::endl;
+
+ return os;
+}
+
+std::ostream &operator<<(std::ostream & os, client::parsedData2 &data)
+{
+ os << data.name <<std::endl;
+
+#define generateOutput2(r, d, i, elem) if(BOOST_PP_CAT(data.option, i)) os<< BOOST_PP_STRINGIZE( BOOST_PP_CAT(option,i)) <<" "<< * BOOST_PP_CAT(data.option,i)<<std::endl;
+ BOOST_PP_SEQ_FOR_EACH_I(generateOutput2,_,keys)
+
+ os<<std::endl;
+
+ return os;
+}
+
+
+
+BOOST_FUSION_ADAPT_STRUCT(
+ client::parsedDataOptions,
+ BOOST_PP_SEQ_FOR_EACH_I(fusionOptions,_,keys)
+ )
+
+BOOST_FUSION_ADAPT_STRUCT(
+ client::parsedData,
+ (std::string, name)
+ (client::parsedDataOptions, options)
+ )
+
+BOOST_FUSION_ADAPT_STRUCT(
+ client::parsedData2,
+ (std::string, name)
+ BOOST_PP_SEQ_FOR_EACH_I(fusionOptions,_,keys)
+ )
+enum variation
+{
+ full,
+ no_assign,
+ assign
+};
+namespace client
+{
+
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Our parsedData parser
+ ///////////////////////////////////////////////////////////////////////////////
+ //[tutorial_parsedData_parser
+ template <typename Iterator>
+ struct permutation_parser : qi::grammar<Iterator, parsedData(), ascii::space_type>
+ {
+ permutation_parser() : permutation_parser::base_type(start)
+ {
+ using qi::int_;
+ using qi::lit;
+ using qi::double_;
+ using qi::lexeme;
+ using ascii::char_;
+ using boost::phoenix::at_c;
+ using boost::phoenix::assign;
+ using qi::_r1;
+ using qi::_1;
+ using qi::_val;
+ using qi::omit;
+ using qi::repeat;
+
+
+ quoted_string %= lexeme[+(char_-' ')];
+
+#define generateOptions1(r, data, i, elem) BOOST_PP_IF(i, ^(lit(elem) > int_) , (lit(elem) > int_))
+ options = (BOOST_PP_SEQ_FOR_EACH_I(generateOptions1,_,keys));
+
+ start %=
+ quoted_string
+ >> options;
+ ;
+ v_vals = repeat(1,2)[int_];
+ }
+
+ typedef parsedData parser_target_type;
+
+ qi::rule<Iterator, std::string(), ascii::space_type> quoted_string;
+ qi::rule<Iterator, parsedDataOptions(), ascii::space_type> options;
+ qi::rule<Iterator, std::vector<int>(), ascii::space_type> v_vals;
+
+ qi::rule<Iterator, parsedData(), ascii::space_type> start;
+ };
+
+ template <typename Iterator>
+ struct alternative_parser : qi::grammar<Iterator, parsedData2(), ascii::space_type>
+ {
+ alternative_parser() : alternative_parser::base_type(start)
+ {
+ using qi::int_;
+ using qi::lit;
+ using qi::double_;
+ using qi::lexeme;
+ using ascii::char_;
+ using boost::phoenix::at_c;
+ using qi::_r1;
+ using qi::_1;
+ using qi::_val;
+
+ quoted_string %= lexeme[+(char_-' ')];
+
+#define generateOptions2(r, data, i, elem) BOOST_PP_IF(i, |(lit(elem) > int_[at_c<i+1>(_r1)=_1]) , (lit(elem) > int_[at_c<i+1>(_r1)=_1]))
+ options = (BOOST_PP_SEQ_FOR_EACH_I(generateOptions2,_,keys));
+
+ start =
+ quoted_string [at_c<0>(_val)=_1]
+ >> *options(_val);
+ ;
+ }
+
+ typedef parsedData2 parser_target_type;
+
+ qi::rule<Iterator, std::string(), ascii::space_type> quoted_string;
+ qi::rule<Iterator, void(parsedData2 & ), ascii::space_type> options;
+ qi::rule<Iterator, parsedData2(), ascii::space_type> start;
+ };
+
+
+
+ template <typename Iterator,typename variation>
+ struct tst_parser : qi::grammar<Iterator, parsedData2(), ascii::space_type>
+ {
+ typedef variation variation_type;
+
+ tst_parser() : tst_parser::base_type(startalias)
+ {
+ namespace phx = boost::phoenix;
+ using qi::int_;
+ using qi::lit;
+ using qi::double_;
+ using qi::lexeme;
+ using ascii::char_;
+ using boost::phoenix::at_c;
+ using qi::_r1;
+ using qi::_1;
+ using qi::_a;
+ using qi::_val;
+ using qi::locals;
+ using qi::parameterized_nonterminal;
+
+ startalias = start.alias();
+ quoted_string %= lexeme[+(char_-' ')];
+
+#define generateRules(r, data, i, elem) BOOST_PP_CAT(rule,i) = int_[phx::at_c<i+1>(*phx::ref(currentObj))=_1];
+ BOOST_PP_SEQ_FOR_EACH_I(generateRules,_,keys)
+
+#define generateOptions3(r, data, i, elem) (elem,& BOOST_PP_CAT(rule,i))
+
+
+ options.add BOOST_PP_SEQ_FOR_EACH_I(generateOptions3,_,keys);
+ switch(variation_type::value)
+ {
+ case full:
+ {
+ start =
+ quoted_string [at_c<0>(_val)=_1][phx::ref(currentObj)=&_val]
+ >> *( options [_a=_1] >> lazy(*_a));
+ ;
+ break;
+ }
+ case no_assign:
+ {
+ start =
+ quoted_string [at_c<0>(_val)=_1][phx::ref(currentObj)=&_val]
+ >> *( options >> int_);
+ ;
+ break;
+ }
+ case assign:
+ {
+ start =
+ quoted_string [at_c<0>(_val)=_1][phx::ref(currentObj)=&_val]
+ >> *( options [_a=_1] >> int_);
+ ;
+ break;
+ }
+ }
+
+
+ }
+
+ parsedData2 *currentObj;
+
+ typedef parsedData2 parser_target_type;
+
+ qi::rule<Iterator, std::string(), ascii::space_type> quoted_string;
+ typedef qi::rule<Iterator, ascii::space_type> optionsRule;
+#define declareRules(r, data, i, elem) optionsRule BOOST_PP_CAT(rule,i);
+
+ BOOST_PP_SEQ_FOR_EACH_I(declareRules,_,keys)
+
+ qi::symbols<char,optionsRule* > options;
+ qi::rule<Iterator, parsedData2(), ascii::space_type> startalias;
+ qi::rule<Iterator, parsedData2(), qi::locals<optionsRule*>, ascii::space_type> start;
+ };
+
+
+
+ template <typename Iterator,typename variation>
+ struct tst_map_parser : qi::grammar<Iterator, parsedData2(), ascii::space_type>
+ {
+ typedef variation variation_type;
+ tst_map_parser() : tst_map_parser::base_type(startalias)
+ {
+ namespace phx = boost::phoenix;
+ using qi::int_;
+ using qi::lit;
+ using qi::double_;
+ using qi::lexeme;
+ using ascii::char_;
+ using boost::phoenix::at_c;
+ using qi::_r1;
+ using qi::_1;
+ using qi::_a;
+ using qi::_val;
+ using qi::locals;
+ using qi::parameterized_nonterminal;
+
+ startalias = start.alias();
+ quoted_string %= lexeme[+(char_-' ')];
+
+#define generateRules3(r, data, i, elem) BOOST_PP_CAT(rule,i) = int_[phx::at_c<i+1>(*phx::ref(currentObj))=_1];
+ BOOST_PP_SEQ_FOR_EACH_I(generateRules3,_,keys)
+
+#define generateOptions3(r, data, i, elem) (elem,& BOOST_PP_CAT(rule,i))
+
+
+ options.add BOOST_PP_SEQ_FOR_EACH_I(generateOptions3,_,keys);
+
+ switch(variation_type::value)
+ {
+ case full:
+ {
+ start =
+ quoted_string [at_c<0>(_val)=_1][phx::ref(currentObj)=&_val]
+ >> *( options [_a=_1] >> lazy(*_a));
+ ;
+ break;
+ }
+ case no_assign:
+ {
+ start =
+ quoted_string [at_c<0>(_val)=_1][phx::ref(currentObj)=&_val]
+ >> *( options >> int_);
+ ;
+ break;
+ }
+ case assign:
+ {
+ start =
+ quoted_string [at_c<0>(_val)=_1][phx::ref(currentObj)=&_val]
+ >> *( options [_a=_1] >> int_);
+ ;
+ break;
+ }
+ }
+ }
+
+ parsedData2 *currentObj;
+
+ typedef parsedData2 parser_target_type;
+
+ qi::rule<Iterator, std::string(), ascii::space_type> quoted_string;
+ typedef qi::rule<Iterator, ascii::space_type> optionsRule;
+#define declareRules(r, data, i, elem) optionsRule BOOST_PP_CAT(rule,i);
+
+ BOOST_PP_SEQ_FOR_EACH_I(declareRules,_,keys)
+
+ qi::symbols<char,optionsRule*, boost::spirit::qi::tst_map<char,optionsRule*> > options;
+ qi::rule<Iterator, parsedData2(), ascii::space_type> startalias;
+ qi::rule<Iterator, parsedData2(), qi::locals<optionsRule*>, ascii::space_type> start;
+ };
+
+
+ template <typename Iterator>
+ struct kwd_parser : qi::grammar<Iterator, parsedData(), ascii::space_type>
+ {
+ kwd_parser() : kwd_parser::base_type(start)
+ {
+ using qi::int_;
+ using qi::lit;
+ using qi::double_;
+ using qi::lexeme;
+ using ascii::char_;
+ using qi::_r1;
+ using qi::_1;
+ using qi::_val;
+ using boost::spirit::repository::qi::kwd;
+
+ quoted_string %= lexeme[+(char_-' ')];
+
+#define generateOptions4(r, data, i, elem) BOOST_PP_IF(i, / kwd( elem )[ int_ ] , kwd( elem )[ int_ ] )
+ options = (BOOST_PP_SEQ_FOR_EACH_I(generateOptions4,_,keys));
+
+ start %=
+ quoted_string
+ >> options;
+ ;
+ }
+
+ typedef parsedData parser_target_type;
+
+ qi::rule<Iterator, std::string(), ascii::space_type> quoted_string;
+ qi::rule<Iterator, parsedDataOptions(), ascii::space_type> options;
+ qi::rule<Iterator, boost::fusion::vector<boost::optional<int>,boost::optional<int> > () , ascii::space_type> v_vals;
+
+ qi::rule<Iterator, parsedData(), ascii::space_type> start;
+ };
+
+}
+
+
+template <typename parserType>
+ struct timeParser : test::base{
+ timeParser(const std::string & str) : str(str)
+ {
+ }
+ parserType &get_parser(){
+ static parserType parser;
+ return parser;
+ }
+
+ std::string str;
+
+ void benchmark()
+ {
+
+ using boost::spirit::ascii::space;
+ bool r = false;
+ std::string::const_iterator end = str.end();
+ std::string::const_iterator iter = str.begin();
+
+
+ typename parserType::parser_target_type data;
+ r = phrase_parse(iter, end, get_parser(), space, data);
+
+ if (r && iter == end)
+ {
+ this->val += data.name.size();
+ }
+ else
+ {
+ throw std::runtime_error("Parsing failed");
+ }
+ }
+
+ };
+
+
+
+
+typedef std::string::const_iterator iterator_type;
+typedef client::permutation_parser<iterator_type> permutation_parser;
+typedef client::kwd_parser<iterator_type> kwd_parser;
+typedef client::alternative_parser<iterator_type> alternative_parser;
+typedef client::tst_map_parser<iterator_type, boost::mpl::int_<full> > tst_map_parser;
+
+struct permutation_timer_fwd : timeParser<permutation_parser>
+{
+ permutation_timer_fwd() : timeParser<permutation_parser>(fwd) {}
+};
+
+struct permutation_timer_back : timeParser<permutation_parser>
+{
+ permutation_timer_back() : timeParser<permutation_parser>(back) {}
+};
+
+struct alternative_timer_fwd : timeParser<alternative_parser>
+{
+ alternative_timer_fwd() : timeParser<alternative_parser>(fwd) {}
+};
+
+struct alternative_timer_back : timeParser<alternative_parser>
+{
+ alternative_timer_back() : timeParser<alternative_parser>(back) {}
+};
+
+struct tst_timer_fwd_full : timeParser< client::tst_parser<iterator_type, boost::mpl::int_<full> > >
+{
+ tst_timer_fwd_full() : timeParser< client::tst_parser<iterator_type, boost::mpl::int_<full> > >(fwd) {}
+};
+
+struct tst_timer_fwd_no_assign : timeParser< client::tst_parser<iterator_type, boost::mpl::int_<no_assign> > >
+{
+ tst_timer_fwd_no_assign() : timeParser< client::tst_parser<iterator_type,boost::mpl::int_<no_assign> > >(fwd) {}
+};
+
+struct tst_timer_fwd_assign : timeParser< client::tst_parser<iterator_type,boost::mpl::int_<assign> > >
+{
+ tst_timer_fwd_assign() : timeParser< client::tst_parser<iterator_type,boost::mpl::int_<assign> > >(fwd) {}
+};
+
+
+
+struct tst_timer_back : timeParser< client::tst_parser<iterator_type,boost::mpl::int_<full> > >
+{
+ tst_timer_back() : timeParser< client::tst_parser<iterator_type,boost::mpl::int_<full> > >(back) {}
+};
+
+struct tst_map_timer_fwd : timeParser<tst_map_parser>
+{
+ tst_map_timer_fwd() : timeParser<tst_map_parser>(fwd) {}
+};
+
+struct tst_map_timer_back : timeParser<tst_map_parser>
+{
+ tst_map_timer_back() : timeParser<tst_map_parser>(back) {}
+};
+
+struct kwd_timer_fwd : timeParser<kwd_parser>
+{
+ kwd_timer_fwd() : timeParser<kwd_parser>(fwd) {}
+};
+
+struct kwd_timer_back : timeParser<kwd_parser>
+{
+ kwd_timer_back() : timeParser<kwd_parser>(back) {}
+};
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////
+// Main program
+////////////////////////////////////////////////////////////////////////////
+ int
+main()
+{
+
+ BOOST_SPIRIT_TEST_BENCHMARK(
+ 10000000000, // This is the maximum repetitions to execute
+ (permutation_timer_fwd)
+ (permutation_timer_back)
+ (alternative_timer_fwd)
+ (alternative_timer_back)
+ (tst_timer_fwd_full)
+ (tst_timer_fwd_no_assign)
+ (tst_timer_fwd_assign)
+ (tst_timer_back)
+ (tst_map_timer_fwd)
+ (tst_map_timer_back)
+ (kwd_timer_fwd)
+ (kwd_timer_back)
+ )
+
+ // This is ultimately responsible for preventing all the test code
+ // from being optimized away. Change this to return 0 and you
+ // unplug the whole test's life support system.
+ return test::live_code != 0;
+}
+
+

Modified: trunk/libs/spirit/repository/doc/qi.qbk
==============================================================================
--- trunk/libs/spirit/repository/doc/qi.qbk (original)
+++ trunk/libs/spirit/repository/doc/qi.qbk 2011-03-02 19:32:48 EST (Wed, 02 Mar 2011)
@@ -11,7 +11,8 @@
 [include qi/primitive_parsers.qbk]
 [include qi/directives.qbk]
 [include qi/nonterminals.qbk]
-[/include qi/compound_parsers.qbk]
+[include qi/operators.qbk]
+[/include qi/compound_parsers.qbk]
 
 [endsect] [/ Qi]
 

Modified: trunk/libs/spirit/repository/doc/qi/directives.qbk
==============================================================================
--- trunk/libs/spirit/repository/doc/qi/directives.qbk (original)
+++ trunk/libs/spirit/repository/doc/qi/directives.qbk 2011-03-02 19:32:48 EST (Wed, 02 Mar 2011)
@@ -9,4 +9,5 @@
 [section:directives Qi Parser Directives]
 [include confix.qbk]
 [include distinct.qbk]
+[include kwd.qbk]
 [endsect]

Added: trunk/libs/spirit/repository/doc/qi/kwd.qbk
==============================================================================
--- (empty file)
+++ trunk/libs/spirit/repository/doc/qi/kwd.qbk 2011-03-02 19:32:48 EST (Wed, 02 Mar 2011)
@@ -0,0 +1,95 @@
+[/==============================================================================
+ Copyright (C) 2001-2011 Joel de Guzman
+ Copyright (C) 2001-2011 Hartmut Kaiser
+ Copyright (C) 2011 Thomas Bernard
+
+ Distributed under the Boost Software License, Version 1.0. (See accompanying
+ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+===============================================================================/]
+
+[section:kwd Qi Keyword Parser Directive ]
+
+[heading Description]
+
+The `kwd[]` and `ikwd[]` provide a powerful and flexible mechanism for parsing keyword
+based input. It works in conjuction with the / operator to create an effective
+keyword parsing loop. The keyword parsing loop doesn't require the
+keywords to appear in a defined order in the input but also provides the possibility
+to check how many times a keyword appears in the input.
+
+The kwd directive will parse the keywords respecting case sensitivity whereas the ikwd
+direcive is case insensitive. You can mix the kwd and ikwd directives inside a set of
+keywords, but be aware that this has a small overhead. It should be prefered not to
+mix the kwd and ikwd directives.
+
+The kwd directive is very similar to the repeat directive in that it enables to enforce
+keyword occurence constraints but also provides very interesting speed improvement
+over the pure EBNF syntax or the Nabialek-Trick.
+
+[heading Header]
+
+ // forwards to <boost/spirit/repository/home/qi/directive/kwd.hpp>
+ #include <boost/spirit/repository/include/qi_kwd.hpp>
+
+[heading Synopsis]
+
+[table
+ [[Expression] [Semantics]]
+ [[`kwd(keyword)[subject]`] [Parse ( `"keyword"` > `subject`) zero or more times.]]
+ [[`kwd(keyword,n)[subject]`] [Parse ( `"keyword"` > `subject`) exactly `n` times.]]
+ [[`kwd(keyword,min, max)[subject]`] [Parse ( `"keyword"` > `subject`) at least `min` times and at most `max` times.]]
+ [[`kwd(keyword,min, inf)[subject]`] [Parse ( `"keyword"` > `subject`) at least `min` or more. ]]
+]
+
+For non case sensitive keywords use the ikwd directive.
+
+[heading Parameters]
+
+[table
+ [[Parameter] [Description]]
+ [[`keyword`] [The parser for the opening (the prefix).]]
+ [[`subject`] [The parser for the input sequence following the keyword part.]]
+ [[`n`] [Int representing the exact number of times the keyword must be repeated.]]
+ [[`min`] [Int representing the minimum number of times the keyword must be repeated.]]
+ [[`max`] [Int representing the maximum number of times the keyword must be repeated.]]
+]
+
+All three parameters can be arbitrarily complex parsers themselves.
+
+
+[heading Attributes]
+
+[table
+ [[Expression] [Attribute]]
+ [[`kwd(k1)[a]`]
+[``a: A --> kwd(k1)[a]: optional<A> or vector<A>
+a: Unused --> kwd(k1)[a]: Unused``]]
+ [[`kwd(k1,n)[a]`]
+[``a: A --> kwd(k1,n)[a]: optional<A> or vector<A>
+a: Unused --> kwd(k1,n)[a]: Unused``]]
+ [[`kwd(k1,min, max)[a]`]
+[``a: A --> kwd(k1,min, max)[a]: optional<A> or vector<A>
+a: Unused --> kwd(k1,min, max)[a]: Unused``]]
+ [[`kwd(k1,min, inf)[a]`]
+[``a: A --> kwd(k1,min, inf)[a]: optional<A> or vector<A>
+a: Unused --> kwd(k1,min, inf)[a]: Unused``]]
+]
+
+[heading Complexity]
+
+[:The overall complexity is defined by the complexity of its subject
+parser. The complexity of the keyword list construct `kwd` itself is O(N), where N is the number
+of repetitions executed.
+
+The complexity of the keyword list itself determined by the complexity of the internal TST contents :
+
+O(log n+k)
+
+Where k is the length of the string to be searched in a TST with n strings.
+]
+
+[heading Example]
+
+Please refer to keyword_list.
+
+[endsect]

Modified: trunk/libs/spirit/repository/example/qi/Jamfile
==============================================================================
--- trunk/libs/spirit/repository/example/qi/Jamfile (original)
+++ trunk/libs/spirit/repository/example/qi/Jamfile 2011-03-02 19:32:48 EST (Wed, 02 Mar 2011)
@@ -18,4 +18,4 @@
 exe calc1_sr : calc1_sr.cpp ;
 exe mini_xml2_sr : mini_xml2_sr.cpp ;
 exe advance : advance.cpp ;
-
+exe keywords : keywords.cpp ;

Added: trunk/libs/spirit/repository/example/qi/keywords.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/spirit/repository/example/qi/keywords.cpp 2011-03-02 19:32:48 EST (Wed, 02 Mar 2011)
@@ -0,0 +1,214 @@
+/*=============================================================================
+ Copyright (c) 2001-2011 Joel de Guzman
+ http://spirit.sourceforge.net/
+
+ Distributed under the Boost Software License, Version 1.0. (See accompanying
+ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================*/
+//[reference_includes
+#include <boost/spirit/include/qi.hpp>
+#include <boost/spirit/include/phoenix_core.hpp>
+#include <boost/spirit/include/phoenix_operator.hpp>
+#include <boost/fusion/include/adapt_struct.hpp>
+#include <boost/spirit/repository/include/qi_kwd.hpp>
+#include <boost/spirit/repository/include/qi_keywords.hpp>
+#include <iostream>
+#include <string>
+#include <cstdlib>
+#include <iterator>
+//]
+
+//[reference_test
+template <typename P>
+void test_parser(
+ char const* input, P const& p, bool full_match = true)
+{
+ using boost::spirit::qi::parse;
+
+ char const* f(input);
+ char const* l(f + strlen(f));
+ if (parse(f, l, p) && (!full_match || (f == l)))
+ std::cout << "ok" << std::endl;
+ else
+ std::cout << "fail" << std::endl;
+}
+
+template <typename P>
+void test_phrase_parser(
+ char const* input, P const& p, bool full_match = true)
+{
+ using boost::spirit::qi::phrase_parse;
+ using boost::spirit::qi::ascii::space;
+
+ char const* f(input);
+ char const* l(f + strlen(f));
+ if (phrase_parse(f, l, p, space) && (!full_match || (f == l)))
+ std::cout << "ok" << std::endl;
+ else
+ std::cout << "fail" << std::endl;
+}
+//]
+
+//[reference_test_attr
+template <typename P, typename T>
+void test_parser_attr(
+ char const* input, P const& p, T& attr, bool full_match = true)
+{
+ using boost::spirit::qi::parse;
+
+ char const* f(input);
+ char const* l(f + strlen(f));
+ if (parse(f, l, p, attr) && (!full_match || (f == l)))
+ std::cout << "ok" << std::endl;
+ else
+ std::cout << "fail" << std::endl;
+}
+
+template <typename P, typename T>
+void test_phrase_parser_attr(
+ char const* input, P const& p, T& attr, bool full_match = true)
+{
+ using boost::spirit::qi::phrase_parse;
+ using boost::spirit::qi::ascii::space;
+
+ char const* f(input);
+ char const* l(f + strlen(f));
+ if (phrase_parse(f, l, p, space, attr) && (!full_match || (f == l)))
+ std::cout << "ok" << std::endl;
+ else
+ std::cout << "fail" << std::endl;
+}
+//]
+
+
+
+//[reference_keyword_list_test_data_structure
+// Data structure definitions to test the kwd directive
+// and the keywords list operator
+
+struct person {
+ std::string name;
+ int age;
+ double size;
+ std::vector<std::string> favorite_colors;
+
+};
+
+std::ostream &operator<<(std::ostream &os, const person &p)
+{
+ os<<"Person : "<<p.name<<", "<<p.age<<", "<<p.size<<std::endl;
+ std::copy(p.favorite_colors.begin(),p.favorite_colors.end(),std::ostream_iterator<std::string>(os,"\n"));
+ return os;
+}
+
+BOOST_FUSION_ADAPT_STRUCT( person,
+ (std::string, name)
+ (int, age)
+ (double, size)
+ (std::vector<std::string>, favorite_colors)
+)
+//]
+
+int
+main()
+{
+
+ // keyword_list
+ {
+ //[reference_using_declarations_keyword_list
+ using boost::spirit::repository::qi::kwd;
+ using boost::spirit::qi::inf;
+ using boost::spirit::ascii::space_type;
+ using boost::spirit::ascii::char_;
+ using boost::spirit::qi::double_;
+ using boost::spirit::qi::int_;
+ using boost::spirit::qi::rule;
+ //]
+
+ //[reference_keyword_list_rule_declarations
+ rule<const char *, std::string(), space_type> parse_string;
+ rule<const char *, person(), space_type> no_constraint_person_rule, constraint_person_rule;
+
+ parse_string %= '"'> *(char_-'"') > '"';
+ //]
+
+ //[reference_keyword_list_no_constraint_rule
+ no_constraint_person_rule %=
+ kwd("name")['=' > parse_string ]
+ / kwd("age") ['=' > int_]
+ / kwd("size") ['=' > double_ > 'm']
+ ;
+ //]
+
+
+ //[reference_keyword_list
+ //`Parsing a keyword list:
+ // Let's declare a small list of people for which we want to collect information.
+ person John,Mary,Mike,Hellen,Johny;
+ test_phrase_parser_attr(
+ "name = \"John\" \n age = 10 \n size = 1.69m "
+ ,no_constraint_person_rule
+ ,John); // full in orginal order
+ std::cout<<John;
+
+ test_phrase_parser_attr(
+ "age = 10 \n size = 1.69m \n name = \"Mary\""
+ ,no_constraint_person_rule
+ ,Mary); // keyword oder doesn't matter
+ std::cout<<Mary;
+
+ test_phrase_parser_attr(
+ "size = 1.69m \n name = \"Mike\" \n age = 10 "
+ ,no_constraint_person_rule
+ ,Mike); // still the same result
+
+ std::cout<<Mike;
+
+ /*`The code above will print:[teletype]
+
+ Person : John, 10, 1.69
+ Person : Mary, 10, 1.69
+ Person : Mike, 10, 1.69
+ */
+ //]
+
+ //[reference_keyword_list_constraint_rule
+ /*`The parser definition below uses the kwd directive occurence constraint variants to
+ make sure that the name and age keyword occur only once and allows the favorite color
+ entry to appear 0 or more times. */
+ constraint_person_rule %=
+ kwd("name",1) ['=' > parse_string ]
+ / kwd("age" ,1) ['=' > int_]
+ / kwd("size" ,1) ['=' > double_ > 'm']
+ / kwd("favorite color",0,inf) [ '=' > parse_string ]
+ ;
+ //]
+
+ //[reference_keyword_list_constraints
+
+ // Here all the give constraint are resepected : parsing will succeed.
+ test_phrase_parser_attr(
+ "name = \"Hellen\" \n age = 10 \n size = 1.80m \n favorite color = \"blue\" \n favorite color = \"green\" "
+ ,constraint_person_rule
+ ,Hellen);
+ std::cout<<Hellen;
+
+ // Parsing this string will fail because the age and size minimum occurence requirements aren't met.
+ test_phrase_parser_attr(
+ "name = \"Johny\" \n favorite color = \"blue\" \n favorite color = \"green\" "
+ ,constraint_person_rule
+ ,Johny );
+
+ /*`Parsing the first string will succeed but fail for the second string as the
+ occurence constraints aren't met. This code should print:[teletype]
+
+ Person : Hellen, 10, 1.8
+ blue
+ green
+ */
+ //]
+ }
+
+
+ return 0;
+}

Modified: trunk/libs/spirit/repository/test/Jamfile
==============================================================================
--- trunk/libs/spirit/repository/test/Jamfile (original)
+++ trunk/libs/spirit/repository/test/Jamfile 2011-03-02 19:32:48 EST (Wed, 02 Mar 2011)
@@ -9,7 +9,7 @@
 project spirit_v2_repository/test
     : requirements
         <include>.
- <toolset>gcc:<cxxflags>-ftemplate-depth-300
+ <toolset>gcc:<cxxflags>-ftemplate-depth-300 <linkflags>-lrt
         <toolset>darwin:<cxxflags>-ftemplate-depth-300
     :
     :
@@ -26,6 +26,7 @@
     [ run qi/confix.cpp : : : : qi_repo_confix ]
     [ run qi/distinct.cpp : : : : qi_repo_distinct ]
     [ run qi/subrule.cpp : : : : qi_repo_subrule ]
+ [ run qi/keywords.cpp : : : : qi_repo_keywords ]
 
     # run Karma repository tests
     [ run karma/confix.cpp : : : : karma_repo_confix ]

Added: trunk/libs/spirit/repository/test/qi/keywords.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/spirit/repository/test/qi/keywords.cpp 2011-03-02 19:32:48 EST (Wed, 02 Mar 2011)
@@ -0,0 +1,154 @@
+/*=============================================================================
+ Copyright (c) 2001-2011 Joel de Guzman
+ Copyright (c) 2011 Thomas Bernard
+
+ Distributed under the Boost Software License, Version 1.0. (See accompanying
+ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================*/
+#include <string>
+#include <vector>
+
+#include <boost/detail/lightweight_test.hpp>
+#include <boost/utility/enable_if.hpp>
+
+#include <boost/spirit/include/qi_operator.hpp>
+#include <boost/spirit/include/qi_char.hpp>
+#include <boost/spirit/include/qi_string.hpp>
+#include <boost/spirit/include/qi_numeric.hpp>
+#include <boost/spirit/include/qi_directive.hpp>
+#include <boost/spirit/include/qi_action.hpp>
+#include <boost/spirit/include/support_argument.hpp>
+#include <boost/spirit/include/phoenix_core.hpp>
+#include <boost/spirit/include/phoenix_operator.hpp>
+#include <boost/spirit/include/phoenix_container.hpp>
+#include <boost/spirit/repository/include/qi_kwd.hpp>
+#include <boost/spirit/repository/include/qi_keywords.hpp>
+
+#include <string>
+#include <iostream>
+#include "test.hpp"
+
+struct x_attr
+{
+
+};
+
+namespace boost { namespace spirit { namespace traits
+{
+
+
+ template <>
+ struct container_value<x_attr>
+ {
+ typedef char type; // value type of container
+ };
+
+
+ template <>
+ struct push_back_container<x_attr, char>
+ {
+ static bool call(x_attr& /*c*/, char /*val*/)
+ {
+ // push back value type into container
+ return true;
+ }
+ };
+}}}
+
+int
+main()
+{
+ using spirit_test::test_attr;
+ using spirit_test::test;
+ using namespace boost::spirit;
+ using namespace boost::spirit::ascii;
+ using boost::spirit::repository::kwd;
+ using boost::spirit::repository::ikwd;
+ using boost::spirit::qi::inf;
+ using boost::spirit::qi::omit;
+ using boost::spirit::qi::int_;
+ using boost::spirit::qi::lit;
+ using boost::spirit::qi::_1;
+ using boost::spirit::qi::lexeme;
+
+ {
+
+ // no constraints
+ boost::fusion::vector<char,char,int> data;
+ BOOST_TEST( test_attr("c=1 a=a", kwd("a")[ '=' > char_] / kwd("b")[ '=' > char_] / kwd("c")['=' > int_], data, space));
+ BOOST_TEST( test("a=a c=1", kwd("a")[ '=' > char_] / kwd("b")[ '=' > char_] / kwd("c")['=' > int_], space));
+
+ // Exact
+ BOOST_TEST(test("a=a b=b c=1", kwd("a",1)[ '=' > char_] / kwd("b")[ '=' > char_] / kwd("c")['=' > int_], space));
+ BOOST_TEST(test("a=a b=c b=e c=1", kwd("a",1)[ '=' > char_] / kwd("b",2)[ '=' > char_] / kwd("c")['=' > int_], space));
+ BOOST_TEST(!test("b=c b=e c=1", kwd("a",1)[ '=' > char_] / kwd("b",2)[ '=' > char_] / kwd("c")['=' > int_], space));
+
+ // Min - Max
+ BOOST_TEST(test("a=f b=c b=e c=1", kwd("a",1,2)[ '=' > char_] / kwd("b",0,2)[ '=' > char_] / kwd("c",1,2)['=' > int_], space));
+ BOOST_TEST(!test("b=c b=e c=1", kwd("a",1,2)[ '=' > char_] / kwd("b",0,1)[ '=' > char_] / kwd("c",1,2)['=' > int_], space));
+ BOOST_TEST(test("a=g a=f b=c b=e c=1", kwd("a",1,2)[ '=' > char_] / kwd("b",0,2)[ '=' > char_] / kwd("c",1,2)['=' > int_], space));
+ BOOST_TEST(!test("a=f a=e b=c b=e a=p c=1", kwd("a",1,2)[ '=' > char_] / kwd("b",0,1)[ '=' > char_] / kwd("c",1,2)['=' > int_], space));
+
+ // Min - inf
+ BOOST_TEST(test("a=f b=c b=e c=1", kwd("a",1,inf)[ '=' > char_] / kwd("b",0,inf)[ '=' > char_] / kwd("c",1,inf)['=' > int_], space ));
+ BOOST_TEST(!test("b=c b=e c=1", kwd("a",1,inf)[ '=' > char_] / kwd("b",0,inf)[ '=' > char_] / kwd("c",1,inf)['=' > int_], space ));
+ BOOST_TEST(test("a=f a=f a=g b=c b=e c=1 a=e", kwd("a",1,inf)[ '=' > char_] / kwd("b",0,inf)[ '=' > char_] / kwd("c",1,inf)['=' > int_], space ));
+ }
+
+ { // Single keyword, empty string
+ BOOST_TEST(test(" ", kwd("aad")[char_],space));
+ BOOST_TEST(test("aad E ", kwd("aad")[char_],space));
+ //BOOST_TEST(test("AaD E ", ikwd("aad")[char_],space));
+
+ }
+
+ {
+ // Vector container
+ boost::fusion::vector<std::vector<int>,std::vector<int>,std::vector<int> > data;
+ BOOST_TEST(test_attr(" a=1 b=2 b=5 c=3",kwd("a")[ '=' > int_] / kwd("b")[ '=' > int_] / kwd("c")['=' > int_] , data, space)
+ && (boost::fusion::at_c<0>(data).size()==1)
+ && (boost::fusion::at_c<0>(data)[0]==1)
+
+ &&(boost::fusion::at_c<1>(data).size()==2)
+ &&(boost::fusion::at_c<1>(data)[0]==2)
+ &&(boost::fusion::at_c<1>(data)[1]==5)
+
+ &&(boost::fusion::at_c<2>(data).size()==1)
+ &&(boost::fusion::at_c<2>(data)[0]==3)
+ );
+ }
+
+ {
+ // no_case test
+ BOOST_TEST( test("B=a c=1 a=E", no_case[kwd("a")[ "=E" ] / kwd("b")[ '=' > char_] / kwd("c")['=' > int_]], space));
+ BOOST_TEST( test("B=a c=1 a=e", no_case[kwd("a")[ "=E" ] / kwd("b")[ '=' > char_] / kwd("c")['=' > int_]], space));
+ BOOST_TEST( !test("B=a c=1 A=E", no_case[kwd("a")[ '=' > char_]] / kwd("b")[ '=' > char_] / kwd("c")['=' > int_], space));
+ BOOST_TEST( test("b=a c=1 A=E", no_case[kwd("a")[ '=' > char_]] / kwd("b")[ '=' > char_] / kwd("c")['=' > int_], space));
+ BOOST_TEST( !test("A=a c=1 a=E", kwd("a")[ '=' > char_] / kwd("b")[ '=' > char_] / kwd("c")['=' > int_], space));
+ BOOST_TEST( test("A=a c=1 a=E", ikwd("a")[ '=' > char_] / kwd("b")[ '=' > char_] / kwd("c")['=' > int_], space));
+ BOOST_TEST( !test("A=a C=1 a=E", ikwd("a")[ '=' > char_] / kwd("b")[ '=' > char_] / kwd("c")['=' > int_], space));
+ }
+
+ {
+ // iterator restoration
+ BOOST_TEST( test("a=a c=1 ba=d", (kwd("a")[ '=' > char_] / kwd("b")[ '=' > int_] / kwd("c")['=' > int_] ) | lit("ba=") > char_, space));
+ BOOST_TEST( test("A=a c=1 ba=d", (ikwd("a")[ '=' > char_] / kwd("b")[ '=' > int_] / kwd("c")['=' > int_] ) | lit("ba=") > char_, space));
+ }
+
+ { // actions
+ namespace phx = boost::phoenix;
+
+ std::vector<int> v;
+ BOOST_TEST(test("b=2 c=4", kwd("b")['=' > int_][phx::ref(v)=_1] / kwd("c")[ '=' > int_ ],space) &&
+ v[0] == 2 );
+ }
+
+ { // attribute customization
+
+ x_attr x;
+// test_attr("a = b c = d", kwd("a")['=' > char_] / kwd("c")['=' > char_], x);
+ }
+
+ return boost::report_errors();
+}
+


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk