Skip to content

Instantly share code, notes, and snippets.

@cppljevans
Last active August 24, 2016 08:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cppljevans/3d3bec54be8f3d0c4d8b94af84a6e529 to your computer and use it in GitHub Desktop.
Save cppljevans/3d3bec54be8f3d0c4d8b94af84a6e529 to your computer and use it in GitHub Desktop.
spirit toy.cpp modified to show need for BOOST_SPIRIT_DEFINE
//Purpose:
// Demonstrate the purpose BOOST_SPIRIT_DEFINE
// is to implement recursive rules.
//OriginalSource:
// https://github.com/boostorg/spirit/blob/develop/workbench/x3/toy/toy.cpp
//Modifications:
// Rm'ed use of context to implement recursiion and replaced with
// BOOST_SPIRIT_DEFINE
//===========================================================================
#include <iostream>
#include <utility>
#include <cstring>
#include <boost/type_traits/is_same.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>
#include <boost/preprocessor/variadic/elem.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
template <typename Derived>
struct parser
{
Derived const& derived() const
{
return *static_cast<Derived const*>(this);
}
};
template <typename Char>
struct char_parser : parser<char_parser<Char>>
{
char_parser(Char ch) : ch(ch) {}
template <typename Iterator, typename Context>
bool parse(Iterator& first, Iterator last, Context const& ctx) const
{
if (first != last && *first == ch)
{
++first;
return true;
}
return false;
}
Char ch;
};
template <typename Char>
inline char_parser<Char> char_(Char ch)
{
return char_parser<Char>(ch);
};
template <typename Left, typename Right>
struct sequence_parser : parser<sequence_parser<Left, Right>>
{
sequence_parser(Left left, Right right)
: left(left), right(right) {}
template <typename Iterator, typename Context>
bool parse(Iterator& first, Iterator last, Context const& ctx) const
{
return left.parse(first, last, ctx)
&& right.parse(first, last, ctx);
}
Left left;
Right right;
};
template <typename Left, typename Right>
inline sequence_parser<Left, Right> operator>>(
parser<Left> const& left, parser<Right> const& right)
{
return sequence_parser<Left, Right>(
left.derived(), right.derived());
}
template <typename Left, typename Right>
struct alternative_parser : parser<alternative_parser<Left, Right>>
{
alternative_parser(Left left, Right right)
: left(left), right(right) {}
template <typename Iterator, typename Context>
bool parse(Iterator& first, Iterator last, Context const& ctx) const
{
if (left.parse(first, last, ctx))
return true;
return right.parse(first, last, ctx);
}
Left left;
Right right;
};
template <typename Left, typename Right>
inline alternative_parser<Left, Right> operator|(
parser<Left> const& left, parser<Right> const& right)
{
return alternative_parser<Left, Right>(
left.derived(), right.derived());
}
struct empty_context
{
};
template <typename ID>
struct rule;
#define USE_ALWAYS_FALSE
#ifdef USE_ALWAYS_FALSE
/*!
* @brief
* Only used to avoid firing the static_assert in
* default parse_rule below when the
* specialized parse_rule is the one that would
* actually be called.
*/
template <typename ID>
struct always_false
{
static constexpr bool value=false;
};
#endif
/*!
* @brief
* default parse_rule implementation
* copied from:
* https://github.com/boostorg/spirit/blob/develop/include/boost/spirit/home/x3/nonterminal/rule.hpp#L26
* but modified to always static_assert.
*/
template <typename ID, typename Iterator, typename Context>
bool parse_rule(
rule<ID> rule_
, Iterator& first, Iterator const& last
, Context const& context)
{
static_assert
#ifdef USE_ALWAYS_FALSE
( always_false<ID>::value
#else
( false
#endif
, "Undefined rule. Use BOOST_SPIRIT_DEFINE to define rule."
);
return false;
}
template <typename ID, typename RHS>
struct rule_definition : parser<rule_definition<ID, RHS>>
{
typedef rule<ID> lhs_type;
rule_definition(RHS rhs)
: rhs(rhs) {}
template <typename Iterator, typename Context>
bool parse(Iterator& first, Iterator last, Context const& ctx) const
{
return rhs.parse(first, last, ctx);
}
RHS rhs;
};
template <typename ID>
struct rule : parser<rule<ID>>
{
template <typename Derived>
rule_definition<ID, Derived>
operator=(parser<Derived> const& definition) const
{
return rule_definition<ID, Derived>(definition.derived());
}
template <typename Iterator, typename Context>
bool parse(Iterator& first, Iterator last, Context const& ctx) const
{
return parse_rule(*this, first, last, ctx);
}
};
template <typename Iterator, typename Derived>
inline bool parse(parser<Derived> const& p, Iterator& first, Iterator last)
{
empty_context ctx;
return p.derived().parse(first, last, ctx);
}
/*!
\def BOOST_SPIRIT_DEFINE_(r, data, rule_def)
\a r is ignored.
\a data is ignored.
\a rule_def is an instance of rule_definition.
\purpose
Creates a parse_rule definition specialized for
decltype(rule_def)::lhs_type. This allows the
lhs_type::parse function, when it calls parse_rule,
to call the parse_rule specific to it.
*/
#define BOOST_SPIRIT_DEFINE_(r, data, rule_def) \
template <typename Iterator, typename Context> \
inline bool parse_rule( \
decltype(rule_def)::lhs_type rule_ \
, Iterator& first, Iterator const& last \
, Context const& context) \
{ \
auto const& def_ = (rule_def); \
bool result=def_.parse(first, last, context); \
return result; \
} \
/***/
#define BOOST_SPIRIT_DEFINE(...) BOOST_PP_SEQ_FOR_EACH( \
BOOST_SPIRIT_DEFINE_, _, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
/***/
///////////////////////////////////////////////////////////////////////////////
// test code
template <typename Parser>
bool test_parse(Parser const& p, char const* in)
{
return parse(p, in, in + std::strlen(in));
}
namespace x_definition
{
struct x_id{};
auto const x = rule<x_id>();
#define USE_DEFINE
#ifdef USE_DEFINE
BOOST_SPIRIT_DEFINE( x = char_('x') | char_('a') >> x );
auto const g = x;
#else
auto const g = ( x = char_('x') | char_('a') >> x );
#endif
}
int main()
{
{ // a non-recursive parser
auto abc = char_('a') >> char_('b') >> char_('c');
std::cout << test_parse(abc, "abc") << std::endl;
std::cout << test_parse(abc, "abx") << std::endl;
std::cout << "==========================================" << std::endl;
}
{ // a recursive rule
using x_definition::g;
std::cout << test_parse(g, "x") << std::endl;
std::cout << test_parse(g, "ax") << std::endl;
std::cout << test_parse(g, "aaaaax") << std::endl;
std::cout << test_parse(g, "aaz") << std::endl;
std::cout << "==========================================" << std::endl;
}
return 0;
}
@vvir
Copy link

vvir commented Aug 13, 2016

Hi Larry,

I really appreciate this and will study it.

Vince

@cppljevans
Copy link
Author

Slightly simplified code by using always_false in the default parse_rule.
Also provided macro, USE_ALWAYS_FALSE, so you can see it's purpose
by switching it on and off.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment