Spirit-Qi: How to Write a Nonterminal Parser

Spirit-Qi: How can I write a nonterminal parser?

Your example

I don't see how your sample requires any of this. Just reorder your branches, then realize that the short branch is just a special case of the qualified case with n=1: Live On Coliru¹ (or using X3 version if you prefer).

The generic case

Now, having mentioned x3, it has the capacity to make your live much easier!

Here's what I think you wanted, in the general case:

namespace parser {

template <typename... Parsers>
struct longest_parser : x3::parser_base {
longest_parser(Parsers... sub) : _alternatives {sub...} { }

template <typename It, typename Ctx, typename Other, typename Attr>
bool parse(It& f, It l, Ctx& ctx, Other const& other, Attr& attr) const {
auto const saved = f;

//// To exclude pre-skip from length comparisons, do pre-skip here:
// x3::skip_over(f, l, ctx);
auto seq = std::make_index_sequence<sizeof...(Parsers)>();

auto best = select_best(f, l, ctx, seq);
//std::cout << "Longest match at index #" << best << "\n";

bool ok = dispatch(f, l, ctx, other, attr, best, seq);

if (!ok)
f = saved;

return ok;
}

private:
template <typename It, typename Ctx, typename P>
size_t length_of(It f, It l, Ctx ctx, P const& p) const {
boost::iterator_range<It> matched;
return x3::raw[p].parse(f, l, ctx, x3::unused, matched)? boost::size(matched) : 0;
}

template <typename It, typename Ctx, size_t... I>
size_t select_best(It f, It l, Ctx& ctx, std::index_sequence<I...>) const {
std::array<size_t, sizeof...(I)> lengths { length_of(f, l, ctx, std::get<I>(_alternatives))... };
return std::distance(lengths.begin(), std::max_element(lengths.begin(), lengths.end()));
}

template <typename It, typename Ctx, typename Other, typename Attr, size_t... I>
bool dispatch(It& f, It l, Ctx& ctx, Other const& other, Attr& attr, size_t targetIdx, std::index_sequence<I...>) const {
//return (real_parse<I>(f, l, ctx, other, attr, targetIdx) || ...);
std::array<bool, sizeof...(I)> b = { real_parse<I>(f, l, ctx, other, attr, targetIdx)... };

return std::accumulate(b.begin(), b.end(), false, std::logical_or<bool>());
}

template <size_t Idx, typename It, typename Ctx, typename Other, typename Attr>
bool real_parse(It& f, It l, Ctx& ctx, Other const& other, Attr& attr, size_t targetIdx) const {
if (targetIdx != Idx)
return false;

return std::get<Idx>(_alternatives).parse(f, l, ctx, other, attr);
}

std::tuple<Parsers...> _alternatives;
};

template <typename... Ps>
longest_parser<Ps...> longest(Ps... p) { return {x3::as_parser(p)...}; }
}

Note the fold expression you could use in dispatch if your compiler supports it (Coliru does, edit it to see!).

Note also the subtle choice regarding skippable (probably whitespace); if it's not significant for the length comparisons, uncomment the pre-skip.

Live Demo

Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <type_traits>
#include <iostream>
#include <numeric>

namespace x3 = boost::spirit::x3;

namespace std {
template <typename T> // just for easy debug printing; hack
static std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
for (auto& el : v) std::cout << '[' << el << ']';
return os;
}
}

using string_vec = std::vector<std::string>;
using result_type = boost::variant<std::string, double, string_vec>;

template <typename Parser>
void parse(const std::string message, const std::string &input, const std::string &rule, const Parser &parser) {
auto iter = input.begin(), end = input.end();

std::cout << "-------------------------\n";
std::cout << message << "\n";
std::cout << "Rule: " << rule << "\n";
std::cout << "Parsing: '" << input << "'\n";

result_type parsed_result;
bool result = phrase_parse(iter, end, parser, x3::space, parsed_result);

if (result) {
std::cout << "Parsed " << parsed_result << "\n";
} else {
std::cout << "Parser failed\n";
}
if (iter != end)
std::cout << "EOI not reached. Unparsed: '" << std::string(iter, end) << "'\n";
}

namespace parser {

template <typename... Parsers>
struct longest_parser : x3::parser_base {
longest_parser(Parsers... sub) : _alternatives {sub...} { }

template <typename It, typename Ctx, typename Other, typename Attr>
bool parse(It& f, It l, Ctx& ctx, Other const& other, Attr& attr) const {
auto const saved = f;

//// To exclude pre-skip from length comparisons, do pre-skip here:
// x3::skip_over(f, l, ctx);
auto seq = std::make_index_sequence<sizeof...(Parsers)>();

auto best = select_best(f, l, ctx, seq);
//std::cout << "Longest match at index #" << best << "\n";

bool ok = dispatch(f, l, ctx, other, attr, best, seq);

if (!ok)
f = saved;

return ok;
}

private:
template <typename It, typename Ctx, typename P>
size_t length_of(It f, It l, Ctx ctx, P const& p) const {
boost::iterator_range<It> matched;
return x3::raw[p].parse(f, l, ctx, x3::unused, matched)? boost::size(matched) : 0;
}

template <typename It, typename Ctx, size_t... I>
size_t select_best(It f, It l, Ctx& ctx, std::index_sequence<I...>) const {
std::array<size_t, sizeof...(I)> lengths { length_of(f, l, ctx, std::get<I>(_alternatives))... };
return std::distance(lengths.begin(), std::max_element(lengths.begin(), lengths.end()));
}

template <typename It, typename Ctx, typename Other, typename Attr, size_t... I>
bool dispatch(It& f, It l, Ctx& ctx, Other const& other, Attr& attr, size_t targetIdx, std::index_sequence<I...>) const {
//return (real_parse<I>(f, l, ctx, other, attr, targetIdx) || ...);
std::array<bool, sizeof...(I)> b = { real_parse<I>(f, l, ctx, other, attr, targetIdx)... };

return std::accumulate(b.begin(), b.end(), false, std::logical_or<bool>());
}

template <size_t Idx, typename It, typename Ctx, typename Other, typename Attr>
bool real_parse(It& f, It l, Ctx& ctx, Other const& other, Attr& attr, size_t targetIdx) const {
if (targetIdx != Idx)
return false;

return std::get<Idx>(_alternatives).parse(f, l, ctx, other, attr);
}

std::tuple<Parsers...> _alternatives;
};

template <typename... Ps>
longest_parser<Ps...> longest(Ps... p) { return {x3::as_parser(p)...}; }
}

int main() {
auto id = x3::rule<void, std::string> {} = x3::lexeme [ x3::char_("a-zA-Z_") >> *x3::char_("a-zA-Z0-9_") ];
auto qualified = x3::rule<void, string_vec> {} = id % "::";

#define TEST_CASE(label, input, rule) parse(label, input, #rule, rule)
TEST_CASE("unqualified" , "willy" , parser::longest(id, x3::int_, x3::double_));
TEST_CASE("unqualified with whitespace", " willy \t" , parser::longest(id, x3::int_, x3::double_));
TEST_CASE("integral or number" , "123.78::anton::lutz" , parser::longest(id, x3::int_, x3::double_));
TEST_CASE("qualified" , "willy anton::lutz" , parser::longest(id, x3::int_, x3::double_));
TEST_CASE("qualified with whitespace" , "willy \tanton::lutz" , parser::longest(id, x3::int_, x3::double_));

TEST_CASE("unqualified" , "willy" , parser::longest(id, x3::int_, x3::double_, qualified));
TEST_CASE("unqualified with whitespace", " willy \t" , parser::longest(id, x3::int_, x3::double_, qualified));
TEST_CASE("integral or number" , "123.78::anton::lutz" , parser::longest(id, x3::int_, x3::double_, qualified));
TEST_CASE("qualified" , "willy::anton::lutz" , parser::longest(id, x3::int_, x3::double_, qualified));
TEST_CASE("qualified with whitespace" , "willy ::\tanton::lutz", parser::longest(id, x3::int_, x3::double_, qualified));

TEST_CASE("unqualified" , "willy" , parser::longest(x3::int_, x3::double_, qualified));
TEST_CASE("unqualified with whitespace", " willy \t" , parser::longest(x3::int_, x3::double_, qualified));
TEST_CASE("integral or number" , "123.78::anton::lutz" , parser::longest(x3::int_, x3::double_, qualified));
TEST_CASE("qualified" , "willy::anton::lutz" , parser::longest(x3::int_, x3::double_, qualified));
TEST_CASE("qualified with whitespace" , "willy ::\tanton::lutz", parser::longest(x3::int_, x3::double_, qualified));
}

Prints

-------------------------
unqualified
Rule: parser::longest(id, x3::int_, x3::double_)
Parsing: 'willy'
Parsed willy
-------------------------
unqualified with whitespace
Rule: parser::longest(id, x3::int_, x3::double_)
Parsing: ' willy '
Parsed willy
-------------------------
integral or number
Rule: parser::longest(id, x3::int_, x3::double_)
Parsing: '123.78::anton::lutz'
Parsed 123.78
EOI not reached. Unparsed: '::anton::lutz'
-------------------------
qualified
Rule: parser::longest(id, x3::int_, x3::double_)
Parsing: 'willy anton::lutz'
Parsed willy
EOI not reached. Unparsed: 'anton::lutz'
-------------------------
qualified with whitespace
Rule: parser::longest(id, x3::int_, x3::double_)
Parsing: 'willy anton::lutz'
Parsed willy
EOI not reached. Unparsed: 'anton::lutz'
-------------------------
unqualified
Rule: parser::longest(id, x3::int_, x3::double_, qualified)
Parsing: 'willy'
Parsed willy
-------------------------
unqualified with whitespace
Rule: parser::longest(id, x3::int_, x3::double_, qualified)
Parsing: ' willy '
Parsed willy
-------------------------
integral or number
Rule: parser::longest(id, x3::int_, x3::double_, qualified)
Parsing: '123.78::anton::lutz'
Parsed 123.78
EOI not reached. Unparsed: '::anton::lutz'
-------------------------
qualified
Rule: parser::longest(id, x3::int_, x3::double_, qualified)
Parsing: 'willy::anton::lutz'
Parsed [willy][anton][lutz]
-------------------------
qualified with whitespace
Rule: parser::longest(id, x3::int_, x3::double_, qualified)
Parsing: 'willy :: anton::lutz'
Parsed [willy][anton][lutz]
-------------------------
unqualified
Rule: parser::longest(x3::int_, x3::double_, qualified)
Parsing: 'willy'
Parsed [willy]
-------------------------
unqualified with whitespace
Rule: parser::longest(x3::int_, x3::double_, qualified)
Parsing: ' willy '
Parsed [willy]
-------------------------
integral or number
Rule: parser::longest(x3::int_, x3::double_, qualified)
Parsing: '123.78::anton::lutz'
Parsed 123.78
EOI not reached. Unparsed: '::anton::lutz'
-------------------------
qualified
Rule: parser::longest(x3::int_, x3::double_, qualified)
Parsing: 'willy::anton::lutz'
Parsed [willy][anton][lutz]
-------------------------
qualified with whitespace
Rule: parser::longest(x3::int_, x3::double_, qualified)
Parsing: 'willy :: anton::lutz'
Parsed [willy][anton][lutz]

Note the different results depending on the parser expressions in the alternatives.

Boost::spirit get value from non-terminal

You don't have to work that hard :)

Spirit has automatic attribute propagation. Actually, I'd say that is it's main selling feature. So you can:

char parsed_char;
bool ok = qi::phrase_parse(f,l, '(' >> qi::char_("0-9") >> ')', qi::space, parsed_char);

This will simply bind the exposed attribute of the char_ parser component to the attribute reference (parsed_char) passed into the variadic parsing API (phrase_parse).

Below is a generalized demonstration, showing the many ways in which you can influence what exactly gets exposed. Exactly what gets exposed is documented with the parser directives, e.g. here, for the '%' list parser.

For your specific question, you'd want to simply:

qi::rule<Iterator, char()> character;
qi::rule<Iterator, char()> parenthesized;

character = qi::char_("0-9a-z_"); // or qi::alnum, qi::graph, qi::alpha etc...
parenthesized = '(' >> character >> ')';

Note importantly, you need to say qi::rule<Iterator, char()> instead of qi::rule<Iterator, char>!

Demonstrations

See it Live on Coliru:

#include <boost/spirit/include/qi.hpp>
#include <cassert>

namespace qi = boost::spirit::qi;

template<typename ParseExpr, typename... Attr>
void test(const std::string& input, const ParseExpr& p, Attr&... attrs)
{
auto f = input.begin(),
l = input.end();

bool ok = qi::phrase_parse(f,l, p, qi::space, attrs...);

if (!ok)
std::cerr << "parse failed at: '" << std::string(f,l) << "'\n";

if (f!=l)
std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
}

int main()
{
char parsed_char1, parsed_char2;
int parsed_int;
std::string parsed_str;

test("( 0 )", // input
'(' >> qi::char_("0-9") >> ')', // parser/grammar
parsed_char1 // output
);
assert(parsed_char1 == '0');

test("( q 123 )",
'(' >> qi::graph >> qi::int_ >> ')',
parsed_char1,
parsed_int);
assert(parsed_char1 == 'q');
assert(parsed_int == 123);

// parsing strings: with the skipper
test("( hello world )",
'(' >> *~qi::char_(")") >> ')',
parsed_str = "");
assert(parsed_str == "helloworld");

// parsing strings: qi::char_ exposes the char
test("( hello world )",
qi::char_('(') >> *~qi::char_(")") >> qi::char_(')'),
parsed_char1, parsed_str = "", parsed_char2);
assert(parsed_char1 == '(');
assert(parsed_str == "helloworld");
assert(parsed_char2 == ')');

// parsing strings: qi::char_ exposes the char, chars get 'combined' into attribute
test("( hello world )",
qi::char_('(') >> *~qi::char_(")") >> qi::char_(')'),
parsed_str = "");
assert(parsed_str == "(helloworld)");

// parsing strings: as a lexeme
test("( hello world )",
'(' >> qi::lexeme [ *~qi::char_(")") ] >> ')',
parsed_str = "");
assert(parsed_str == "hello world ");

// parsing strings: as bigger lexeme
test("( hello world )",
qi::lexeme [ '(' >> *~qi::char_(")") >> ')' ],
parsed_str = "");
assert(parsed_str == " hello world ");

// parsing anything as "raw" - exposes an iterator pair, but still 'converts' to a string!
test("( hello 42 false )",
qi::raw [ '(' >> qi::lexeme[*qi::graph] >> qi::int_ >> qi::bool_ >> ')' ],
parsed_str = "");
assert(parsed_str == "( hello 42 false )");

// note: this would fail to parse, because with the skipper, *qi::graph would eat "42 false )" as well:
std::cout << "next parse should fail:\n";
test("( hello 42 false )", qi::raw [ '(' >> *qi::graph >> qi::int_ >> qi::bool_ >> ')' ]);
}

boost::spirit lazy parser with arguments?

Trying to do this kind of sorcery without understanding how actually Phoenix and Spirit communicate is extremely hard. Let's try digging into it:

  1. Rule parametrization happens via operator() of qi::rule that creates an instance of qi::parameterized_nonterminal parser.
  2. Lazy parser evaluation is performed this way: qi::lazy wraps phoenix::actor into proto::terminal, which is later transformed (by meta compiler) into qi::lazy_parser/qi::lazy_directive.

So, in your example Phoenix actor is converted to a Proto terminal and then call operator creates a Proto expression that Spirit meta compiler does not understand.

My guess was that it should be lazy(phoenix::bind(&get_constant_parser, _r2)(_r1)) because you need to call that operator() on the actual rule, but Phoenix does not let you invoke operator() like this.

What should work is: lazy(phoenix::bind(phoenix::bind(&get_constant_parser, _r2), _r1)).


Long time ago I tried something like you are doing and also failed. I also googled those topics that say it is impossible and stopped at that point. But your question raised my interest and after a short trial and error (i.e. scratching my head and digging into Spirit sources) I came to this proof of concept:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_argument.hpp>
#include <iostream>

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;

int main()
{
using passed_rule_t = qi::rule<char const*, int(int)>;
qi::rule<char const*, int(int, passed_rule_t const&)> lazyinvoke
= qi::lazy(phx::bind(qi::labels::_r2, // binding is a way to call `operator()` lazily
qi::labels::_r1)); // non-lazy equivalent of this is `_r2(_r1)`
int v;
char const* s = nullptr;
passed_rule_t inout = qi::attr(qi::labels::_r1);
if (qi::parse(s, s, lazyinvoke(phx::val(123), phx::cref(inout)), v))
std::cout << "OK: " << v << "\n";
else
std::cout << "Failed\n";
}

https://wandbox.org/permlink/m40DpeMikKRYyvH0

How future-safe is it to write a parser with Boost Spirit X3?

It's already released, so there's little chance of it just vanishing.

I liberally use X3 even in production code: After all, we do have tests for a reason.

That said, I know a number of hairy issues surround the linking of rules spread across separate translation units¹.

Here's a list of things that make me consider not using X3 in the following cases:

  • where Qi's attribute transformation logic is more enticing (makes for more readable rules). See e.g.
  • Phoenix integration is desired Boost Spirit X3 cannot compile repeat directive with variable factor
  • Sharing rules across TUs is desired

Slightly less pressing differences are when:

  • locals are involved ("X3 becomes a real tedium, (if not completely unbearable) with stateful rules (by which I mean rules with "locals")"). A lot of it can be solved using with<>: Boost Spirit X3 cannot compile repeat directive with variable factor but I'm not convinced it's re-entrant
  • lazy rule invocation is required²
  • Lexer is desired (i.e. I wouldn't port a Qi/Lex grammar to X3, except by rewrite)

Note however, there are definite areas where X3 shines:

  • compilation time
  • ease of generating dynamic rules/custom directives (see boost::spirit::x3 attribute compatibility rules, intuition or code? or Recursive x3 parser with results passing around)
  • ease of creating custom parsers (e.g. Spirit-Qi: How can I write a nonterminal parser?)

¹ see the mailing list, and e.g. x3 linker error with separate TU and linking errors while separate parser using boost spirit x3

² In fact, it might be "easy" to create one by creating a custom parser, building on with<> and any_parser<>

Boost.Spirit Alternative Parser parallelization

Usually there is no room for parallelization as composed grammars tries to perform as less lookahead and backtrack as possible. In this case any benefits from parallel parsing will be outweighed by thread spawning and synchronization overhead.

If your grammar actually branches from the beginning like in the shown example - you can rewrite it to run multiple x3::parse in parallel.

There is also problem in current Spirit alternate parser implementation, as it does not flatten the expression tree (while it is a binary tree, usually it is very unbalanced).

How to implement #ifdef in a boost::spirit::qi grammar?

In response to the "SSCCE" code added to the question:

The AST

The only way to treat nested defines correctly (including the case where conditional blocks contain #define/#undef directives!) is to use an AST that represents a tree of the blocks¹:

namespace Common {
typedef std::string pp_sym;
}

namespace Ast {
using Common::pp_sym;

typedef std::string Str;
typedef std::pair<Str, Str> Pair;
typedef std::vector<Pair> Pairs;

struct ConditionalBlock;

namespace tag {
struct define;
struct undefine;
}

template <typename Tag> struct Directive {
pp_sym name;
};

typedef Directive<tag::define> Define;
typedef Directive<tag::undefine> Undef;

typedef boost::make_recursive_variant<
Pairs,
boost::recursive_wrapper<ConditionalBlock>,
Define,
Undef
>::type Block;

typedef std::vector<Block> Blocks;

struct ConditionalBlock {
pp_sym required;
Blocks if_, else_;
};
}

To facilitate parsing these without ever using a semantic action:

BOOST_FUSION_ADAPT_TPL_STRUCT((Tag), (Ast::Directive)(Tag), name)
BOOST_FUSION_ADAPT_STRUCT(Ast::ConditionalBlock, required, if_, else_)

Done.

Parsing

Because of the above work, we can now define the parser exactly as we would have liked!

Notes:

  • now using a skipper to avoid having hardcoded amounts of whitespace required or whitespace intolerance
  • now using seek[eol] to ignore until the end of a line
  • now using distinct to parse identifiers (see boost::spirit::qi keywords and identifiers)
  • Now made the appearance of #else optional (see -else)
  • Removes all semantic actions
  • Enables debug information without any more work

    start   = skip(blank) [ blocks ];
    blocks = *block;
    block = define | undef | conditional_block | +pair;

    pair = !char_("#") >> +~char_("=\r\n") >> '=' >> *(char_ - eol) >> *eol;
    pp_symbol = qr::distinct(char_("A-Za-z_")) [ +char_("A-Za-z_") ];

    define = '#' >> distinct(alnum | '_') [ "define" ] >> pp_symbol >> seek[*eol];
    undef = '#' >> distinct(alnum | '_') [ "undef" ] >> pp_symbol >> seek[*eol];

    else_ = '#' >> distinct(alnum | '_') [ "else" ] >> seek[*eol];
    endif = '#' >> distinct(alnum | '_') [ "endif" ] >> seek[*eol];

    conditional_block =
    ('#' >> distinct(alnum | '_') [ "ifdef" ] >> pp_symbol >> seek[*eol])
    >> *(!(else_|endif) >> block)
    >> -else_
    >> *(!endif >> block)
    >> endif
    ;

    BOOST_SPIRIT_DEBUG_NODES((start)(blocks)(block)(pair)(pp_symbol)(define)(undef)(else_)(endif)(conditional_block))

I'd say that is pretty legible, and it results in the ast containing all the information you could want to use later

Processing Logic

Now that we've separated the processing from the parsing, processing is a single visitation of the tree. We use a single function object Logic::Preprocessor that doubles as the variant visitor:

Logic::Preprocess pp({{"EXTERNAL"}} , "    ");
pp(ast);

In this sample, we start with the preprocessor symbol EXTERNAL defined (as if it was defined "externally", like on a command line).

The implementation of the visitor is pretty straightforward, but let me show the action bits, namely where conditions are taken and branches ignored. To make things very complete I even traverse the branches that are not satisfied, just to show that the full AST is there, but with en isolated instance of the function object so there is no effect:

    void operator()(Ast::ConditionalBlock const& cb) const {
bool const satisfied = ctx.defined.count(cb.required);

auto old_indent = indent;
indent += "\t";
std::cout << old_indent << "#ifdef " << cb.required << " // " << std::boolalpha << satisfied << "\n";

Preprocess isolated{ctx, indent+"// "}; // prevent changes to ctx to affect us for the non-matching branch

(satisfied? *this : isolated)(cb.if_);
std::cout << old_indent << "#else " << " // ifdef " << cb.required << "\n";
(satisfied? isolated : *this)(cb.else_);

std::cout << old_indent << "#endif " << " // ifdef " << cb.required << "\n";
indent.resize(indent.size()-1);
}
void operator()(Ast::Define const& directive) const {
ctx.defined.insert(directive.name);

std::cout << indent << "#define\t" << directive.name;
report();
}
void operator()(Ast::Undef const& directive) const {
ctx.defined.erase(directive.name);

std::cout << indent << "#undef\t" << directive.name;
report();
}

Demo

Observe how this document, which even nests conditional blocks and defines symbols from within conditional branches (so, conditionally), is correctly interpreted:

#define FOO
led_zeppelin=9
the_shins=9
dead_mau5=6
portishead=10
#ifdef FOO
foo_fighters=7
#define ZOO
#else
the_who=6
#define QUX
#endif

#ifdef EXTERNAL

#ifdef ZOO
zoowasdefined=yes
#else
zoowasdefined=no
#endif

#ifdef QUX
quxwasdefined=yes
#else
quxwasdefined=no
#endif
#endif

kanye_west=4
#undef FOO
#define BAR

Our demo program prints: Live On Coliru

Preprocess results:

#define FOO // effective: EXTERNAL FOO
led_zeppelin=9
the_shins=9
dead_mau5=6
portishead=10
#ifdef FOO // true
foo_fighters=7
#define ZOO // effective: EXTERNAL FOO ZOO
#else // ifdef FOO
// the_who=6
// #define QUX // effective: EXTERNAL FOO QUX
#endif // ifdef FOO
#ifdef EXTERNAL // true
#ifdef ZOO // true
zoowasdefined=yes
#else // ifdef ZOO
// zoowasdefined=no
#endif // ifdef ZOO
#ifdef QUX // false
// quxwasdefined=yes
#else // ifdef QUX
quxwasdefined=no
#endif // ifdef QUX
#else // ifdef EXTERNAL
#endif // ifdef EXTERNAL
kanye_west=4
#undef FOO // effective: EXTERNAL ZOO
#define BAR // effective: BAR EXTERNAL ZOO

Defines still in effect: BAR EXTERNAL ZOO

Full Listing

Live On Coliru

#define BOOST_SPIRIT_USE_PHOENIX_V3
//#define BOOST_SPIRIT_DEBUG

#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi_distinct.hpp>
#include <boost/spirit/repository/include/qi_seek.hpp>
#include <boost/variant.hpp>

#include <cassert>

namespace phx = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace qr = boost::spirit::repository::qi;

namespace Common {
typedef std::string pp_sym;
}

namespace Ast {
using Common::pp_sym;

typedef std::string Str;
typedef std::pair<Str, Str> Pair;
typedef std::vector<Pair> Pairs;

struct ConditionalBlock;

namespace tag {
struct define;
struct undefine;
}

template <typename Tag> struct Directive {
pp_sym name;
};

typedef Directive<tag::define> Define;
typedef Directive<tag::undefine> Undef;

typedef boost::make_recursive_variant<
Pairs,
boost::recursive_wrapper<ConditionalBlock>,
Define,
Undef
>::type Block;

typedef std::vector<Block> Blocks;

struct ConditionalBlock {
pp_sym required;
Blocks if_, else_;
};
}

BOOST_FUSION_ADAPT_TPL_STRUCT((Tag), (Ast::Directive)(Tag), name)
BOOST_FUSION_ADAPT_STRUCT(Ast::ConditionalBlock, required, if_, else_)

/***
* Grammar definitions
*/

template <typename Iterator>
struct simple_grammar : qi::grammar<Iterator, Ast::Blocks()> {

simple_grammar() : simple_grammar::base_type(start)
{
using namespace qi;
using qr::distinct;
using qr::seek;

start = skip(blank) [ blocks ];
blocks = *block;
block = define | undef | conditional_block | +pair;

pair = +~char_("=\r\n") >> '=' >> *(char_ - eol) >> *eol;
}}


Related Topics



Leave a reply



Submit