Skip to content

Instantly share code, notes, and snippets.

@kwk
Last active December 30, 2015 11:39
Show Gist options
  • Select an option

  • Save kwk/7823603 to your computer and use it in GitHub Desktop.

Select an option

Save kwk/7823603 to your computer and use it in GitHub Desktop.
This parses a Person in the form "Some Name" 123 into a simple struct. `123` is the person's age. I know that his is overly complicated but I want to utilize the setter methods on the `Person` object to have more fine-grained control over the way my `Person` object is being modified. Here, this is just for showing how it can be done. Oh yeah, I …
#include <string>
#include <iostream>
#include <utility>
#include <boost/config/warning_disable.hpp> // Disable deprecated warnings on VC and Intel
#include <boost/spirit/include/qi.hpp> // Parsing
#include <boost/spirit/include/phoenix.hpp> // Semantic action
#include <boost/fusion/adapted.hpp> // for BOOST_FUSION_ADAPT_STRUCT etc.
#include <boost/fusion/container.hpp>
struct Person;
std::ostream & operator<<(std::ostream & out, const Person & person);
struct Person {
Person()
: name()
, age(0)
{
}
void setName(const std::string & n) { name = n; }
void setAge(const int & a) { age = a; }
std::string name;
int age;
};
namespace fusion = boost::fusion;
namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
BOOST_FUSION_ADAPT_STRUCT(
Person,
(std::string, name)
(int, age)
)
template <typename Iterator>
struct PersonSkipper : boost::spirit::qi::grammar<Iterator>
{
PersonSkipper()
: PersonSkipper::base_type(start)
, start()
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
ascii::space_type space;
start = space;
}
boost::spirit::qi::rule<Iterator> start;
};
template <typename Iterator>
struct PersonGrammar : qi::grammar<Iterator, Person(), PersonSkipper<Iterator> >
{
PersonGrammar()
: PersonGrammar::base_type(start_rule, "person")
{
using namespace qi::labels;
start_rule = qi::eps[qi::_val = Person()] // initialize
> name_rule(qi::_val)
> age_rule(qi::_val);
name_rule = quoted_string[phoenix::bind(&Person::setName, qi::_r1, qi::_1)];
age_rule = qi::int_[phoenix::bind(&Person::setAge, qi::_r1, qi::_1)];
quoted_string %= qi::lexeme['"' >> +(qi::char_ - '"') >> '"'];
}
qi::rule<Iterator, Person(), PersonSkipper<Iterator> > start_rule;
qi::rule<Iterator, void(Person&), PersonSkipper<Iterator> > age_rule;
qi::rule<Iterator, void(Person&), PersonSkipper<Iterator> > name_rule;
qi::rule<Iterator, std::string(), PersonSkipper<Iterator> > quoted_string;
};
//----------------------------------------------------------
// These functions are borrowed from boost itself
//----------------------------------------------------------
template <typename Char, typename Parser>
bool test(Char const* in, Parser const& p, bool full_match = true)
{
// we don't care about the result of the "what" function.
// we only care that all parsers have it:
boost::spirit::qi::what(p);
Char const* last = in;
while (*last)
last++;
return boost::spirit::qi::parse(in, last, p)
&& (!full_match || (in == last));
}
template <typename Char, typename Parser, typename Skipper>
bool test(Char const* in, Parser const& p
, Skipper const& s, bool full_match = true)
{
// we don't care about the result of the "what" function.
// we only care that all parsers have it:
boost::spirit::qi::what(p);
Char const* last = in;
while (*last)
last++;
return boost::spirit::qi::phrase_parse(in, last, p, s)
&& (!full_match || (in == last));
}
template <typename Char, typename Parser, typename Attr>
bool test_attr(Char const* in, Parser const& p
, Attr& attr, bool full_match = true)
{
// we don't care about the result of the "what" function.
// we only care that all parsers have it:
boost::spirit::qi::what(p);
Char const* last = in;
while (*last)
last++;
return boost::spirit::qi::parse(in, last, p, attr)
&& (!full_match || (in == last));
}
template <typename Char, typename Parser, typename Attr, typename Skipper>
bool test_attr(Char const* in, Parser const& p
, Attr& attr, Skipper const& s, bool full_match = true)
{
// we don't care about the result of the "what" function.
// we only care that all parsers have it:
boost::spirit::qi::what(p);
Char const* last = in;
while (*last)
last++;
return boost::spirit::qi::phrase_parse(in, last, p, s, attr)
&& (!full_match || (in == last));
}
//----------------------------------------------------------
//----------------------------------------------------------
/**
* The main program tries to use the \a Person grammar as
* a whole as well as each of its rules individually.
*
* Expect this output:
* The person named "John Doe" is 66 years old.
* The person named "" is 66 years old.
* The person named "Some name nomatter how long" is 66 years old.
*/
int main(int argc, char **argv)
{
{ // Test parsing a whole person object
Person person;
PersonGrammar<const char*> parser;
PersonSkipper<const char*> skipper;
test_attr("\"John Doe\" 66", parser, person, skipper);
std::cout << person << std::endl;
}
{ // Test parsing the age
Person person;
PersonGrammar<const char*> parser;
PersonSkipper<const char*> skipper;
test("66", parser.age_rule(phoenix::ref(person)), skipper);
std::cout << person << std::endl;
}
{ // Test parsing the name
Person person;
PersonGrammar<const char*> parser;
PersonSkipper<const char*> skipper;
test("\"Some name nomatter how long\"", parser.name_rule(phoenix::ref(person)), skipper);
std::cout << person << std::endl;
}
return 0;
}
std::ostream & operator<<(std::ostream & out, const Person & person)
{
out << "The person named \"" << person.name << "\" is " << person.age << " years old." << std::endl;
return out;
}
project(inherited_attribute)
cmake_minimum_required(VERSION 2.8)
add_executable(inherited_attribute boost-spirit-inherited-attribute.cpp)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment