Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

X3: position_tagged without inheritance #742

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions doc/x3/tutorial/annotation.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ The full cpp file for this example can be found here:

First, we'll update our previous employee struct, this time separating the
person into its own struct. So now, we have two structs, the `person` and the
`employee`. Take note too that we now inherit `person` and `employee` from
`x3::position_tagged` which provides positional information that we can use
to tell the AST's position in the input stream anytime.
`employee`. Take note too that we now embed `x3::position_tagged` in
`person` and `employee`. `x3::position_tagged` provides positional information
that we can use to tell the AST's position in the input stream anytime.

namespace client { namespace ast
{
struct person : x3::position_tagged
// First way to embed `x3::position_tagged` in structure...
struct person
{
person(
std::string const& first_name = ""
Expand All @@ -54,9 +55,11 @@ to tell the AST's position in the input stream anytime.
, last_name(last_name)
{}

x3::position_tagged pt;
std::string first_name, last_name;
};

// ...and the second.
struct employee : x3::position_tagged
{
int age;
Expand All @@ -68,7 +71,7 @@ to tell the AST's position in the input stream anytime.
Like before, we need to tell __fusion__ about our structs to make them
first-class fusion citizens that the grammar can utilize:

BOOST_FUSION_ADAPT_STRUCT(client::ast::person,
BOOST_FUSION_ADAPT_STRUCT(client::ast::person, pt,
first_name, last_name
)

Expand Down
1 change: 1 addition & 0 deletions example/x3/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ exe x3_num_list2 : num_list/num_list2.cpp ;
exe x3_num_list3 : num_list/num_list3.cpp ;
exe x3_num_list4 : num_list/num_list4.cpp ;

exe x3_annotation : annotation.cpp ;
exe x3_actions : actions.cpp ;
exe x3_complex_number : complex_number.cpp ;
exe x3_sum : sum.cpp ;
Expand Down
31 changes: 25 additions & 6 deletions example/x3/annotation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
#include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/fusion/include/make_vector.hpp>

#include <iostream>
#include <string>
#include <sstream>

namespace client { namespace ast
{
Expand All @@ -35,7 +37,7 @@ namespace client { namespace ast
///////////////////////////////////////////////////////////////////////////
namespace x3 = boost::spirit::x3;

struct person : x3::position_tagged
struct person
{
person(
std::string const& first_name = ""
Expand All @@ -45,6 +47,7 @@ namespace client { namespace ast
, last_name(last_name)
{}

x3::position_tagged pt;
std::string first_name, last_name;
};

Expand All @@ -54,15 +57,13 @@ namespace client { namespace ast
person who;
double salary;
};

using boost::fusion::operator<<;
}}

// We need to tell fusion about our employee struct
// to make it a first-class fusion citizen. This has to
// be in global scope.

BOOST_FUSION_ADAPT_STRUCT(client::ast::person,
BOOST_FUSION_ADAPT_STRUCT(client::ast::person, pt,
first_name, last_name
)

Expand Down Expand Up @@ -137,6 +138,19 @@ namespace client
// Main program
///////////////////////////////////////////////////////////////////////////////




std::string print_employee(const client::ast::employee& e)
{
// `x3::position_tagged` among the fields spoils the ability to print whole 'client::ast::employee'
std::stringstream ss;
auto pv = boost::fusion::make_vector(e.who.first_name, e.who.last_name);
auto ev = boost::fusion::make_vector(e.age, pv, e.salary);
boost::fusion::out(ss, ev);
return ss.str();
}

///////////////////////////////////////////////////////////////////////////////
// Our main parse entry point
///////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -180,7 +194,7 @@ parse(std::string const& input, position_cache& positions)

for (auto const& emp : ast)
{
std::cout << "got: " << emp << std::endl;
std::cout << "got: " << print_employee(emp) << std::endl;
}
std::cout << "\n-------------------------\n";

Expand Down Expand Up @@ -236,10 +250,15 @@ main()
position_cache positions{input.begin(), input.end()};
auto ast = parse(input, positions);

// Get the source of the 2nd employee and print it
// Get the source of the 2nd employee and print it..
auto pos = positions.position_of(ast[1]); // zero based of course!
std::cout << "Here's the 2nd employee:" << std::endl;
std::cout << std::string(pos.begin(), pos.end()) << std::endl;
std::cout << "-------------------------\n";
// ..and get the source of it's component(person) and print it.
pos = positions.position_of(ast[1].who); // zero based of course!
std::cout << "Here's the component of the 2nd employee:" << std::endl;
std::cout << " " << std::string(pos.begin(), pos.end()) << std::endl;
std::cout << "-------------------------\n";
return 0;
}
38 changes: 34 additions & 4 deletions include/boost/spirit/home/x3/operator/detail/sequence.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <boost/spirit/home/x3/support/traits/is_substitute.hpp>
#include <boost/spirit/home/x3/support/traits/container_traits.hpp>
#include <boost/spirit/home/x3/support/traits/tuple_traits.hpp>
#include <boost/spirit/home/x3/support/ast/position_tagged_fwd.hpp>
#include <boost/spirit/home/x3/core/detail/parse_into_container.hpp>

#include <boost/fusion/include/begin.hpp>
Expand All @@ -22,8 +23,11 @@
#include <boost/fusion/include/empty.hpp>
#include <boost/fusion/include/front.hpp>
#include <boost/fusion/include/iterator_range.hpp>
#include <boost/fusion/include/filter_view.hpp>
#include <boost/fusion/include/size.hpp>

#include <boost/mpl/if.hpp>
#include <boost/mpl/not.hpp>

#include <boost/type_traits/add_reference.hpp>
#include <boost/type_traits/is_same.hpp>
Expand Down Expand Up @@ -232,7 +236,7 @@ namespace boost { namespace spirit { namespace x3 { namespace detail

template <typename Parser, typename Iterator, typename Context
, typename RContext, typename Attribute, typename AttributeCategory>
bool parse_sequence(
bool parse_sequence_filtered(
Parser const& parser, Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, Attribute& attr
, AttributeCategory)
Expand Down Expand Up @@ -284,7 +288,7 @@ namespace boost { namespace spirit { namespace x3 { namespace detail

template <typename Parser, typename Iterator, typename Context
, typename RContext, typename Attribute>
bool parse_sequence(
bool parse_sequence_filtered(
Parser const& parser , Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, Attribute& attr
, traits::container_attribute)
Expand Down Expand Up @@ -322,7 +326,7 @@ namespace boost { namespace spirit { namespace x3 { namespace detail

template <typename Parser, typename Iterator, typename Context
, typename RContext, typename Attribute>
bool parse_sequence(
bool parse_sequence_filtered(
Parser const& parser, Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, Attribute& attr
, traits::associative_attribute)
Expand Down Expand Up @@ -355,6 +359,32 @@ namespace boost { namespace spirit { namespace x3 { namespace detail
, should_split());
}

// overload for attribute that need to be filtered
template <typename Parser, typename Iterator, typename Context
, typename RContext, typename Attribute>
bool parse_sequence(
Parser const& parser, Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, Attribute& attr, mpl::true_)
{
using condition_t = mpl::not_<is_same<mpl::_, position_tagged>>;
const auto filtered_attr = fusion::filter_view<Attribute, condition_t>(attr);
static_assert(fusion::result_of::size<decltype(filtered_attr)>::value > 1,
"Size one sequences are not supported to be used with position_tagged in fields");
return parse_sequence_filtered(parser, first, last, context, rcontext, filtered_attr
, typename traits::attribute_category<Attribute>::type());
}

// overload for attribute that don't need to be filtered
template <typename Parser, typename Iterator, typename Context
, typename RContext, typename Attribute>
bool parse_sequence(
Parser const& parser, Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, Attribute& attr, mpl::false_)
{
return parse_sequence_filtered(parser, first, last, context, rcontext, attr
, typename traits::attribute_category<Attribute>::type());
}

template <typename Left, typename Right, typename Context, typename RContext>
struct parse_into_container_impl<sequence<Left, Right>, Context, RContext>
{
Expand All @@ -375,7 +405,7 @@ namespace boost { namespace spirit { namespace x3 { namespace detail
"is value to be stored under that key");

Attribute attr_{};
if (!parse_sequence(parser
if (!parse_sequence_filtered(parser
, first, last, context, rcontext, attr_, traits::container_attribute()))
{
return false;
Expand Down
4 changes: 3 additions & 1 deletion include/boost/spirit/home/x3/operator/sequence.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
#define BOOST_SPIRIT_X3_SEQUENCE_JAN_06_2013_1015AM

#include <boost/spirit/home/x3/support/traits/attribute_of_binary.hpp>
#include <boost/spirit/home/x3/support/traits/tuple_traits.hpp>
#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/operator/detail/sequence.hpp>
#include <boost/spirit/home/x3/directive/expect.hpp>
#include <boost/spirit/home/x3/support/ast/position_tagged_fwd.hpp>

#include <boost/fusion/include/deque_fwd.hpp>

Expand Down Expand Up @@ -44,7 +46,7 @@ namespace boost { namespace spirit { namespace x3
, Context const& context, RContext& rcontext, Attribute& attr) const
{
return detail::parse_sequence(*this, first, last, context, rcontext, attr
, typename traits::attribute_category<Attribute>::type());
, typename traits::is_typecontaining_sequence<Attribute, position_tagged>());
}
};

Expand Down
54 changes: 49 additions & 5 deletions include/boost/spirit/home/x3/support/ast/position_tagged.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
#if !defined(BOOST_SPIRIT_X3_POSITION_TAGGED_MAY_01_2014_0321PM)
#define BOOST_SPIRIT_X3_POSITION_TAGGED_MAY_01_2014_0321PM

#include <boost/spirit/home/x3/support/traits/tuple_traits.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/core/enable_if.hpp>
#include <boost/fusion/include/find.hpp>
#include <boost/fusion/include/deref.hpp>

namespace boost { namespace spirit { namespace x3
{
Expand Down Expand Up @@ -51,17 +54,16 @@ namespace boost { namespace spirit { namespace x3
(!is_base_of<position_tagged, AST>::value)
, boost::iterator_range<iterator_type>
>::type
position_of(AST const& /* ast */) const
position_of(AST const& ast) const
{
// returns an empty position
return boost::iterator_range<iterator_type>();
return position_of_noninherited(ast, typename traits::is_typecontaining_sequence<AST, position_tagged>());
}

// This will catch all nodes except those inheriting from position_tagged
template <typename AST>
void annotate(AST& /* ast */, iterator_type /* first */, iterator_type /* last */, mpl::false_)
void annotate(AST& ast, iterator_type first, iterator_type last, mpl::false_)
{
// (no-op) no need for tags
annotate_noninherited(ast, first, last, typename traits::is_typecontaining_sequence<AST, position_tagged>());
}

// This will catch all nodes inheriting from position_tagged
Expand All @@ -88,6 +90,48 @@ namespace boost { namespace spirit { namespace x3
iterator_type first() const { return first_; }
iterator_type last() const { return last_; }


private:
// This will catch all nodes except those containing position_tagged as a field
template <typename AST>
typename boost::enable_if_c<
(!is_base_of<position_tagged, AST>::value)
, boost::iterator_range<iterator_type>
>::type
position_of_noninherited(AST const& /* ast */, mpl::false_) const
{
// returns an empty position
return boost::iterator_range<iterator_type>();
}

// This will catch all nodes containing position_tagged as a field
template <typename AST>
typename boost::enable_if_c<
(!is_base_of<position_tagged, AST>::value)
, boost::iterator_range<iterator_type>
>::type
position_of_noninherited(AST const& ast, mpl::true_) const
{
return position_of(*fusion::find<position_tagged>(ast));
}

// This will catch all nodes except those containing position_tagged as a field
template <typename AST>
void annotate_noninherited(AST& /* ast */, iterator_type /* first */, iterator_type /* last */, mpl::false_)
{
// (no-op) no need for tags
}

// This will catch all nodes containing position_tagged as a field
template <typename AST>
void annotate_noninherited(AST& ast, iterator_type first, iterator_type last, mpl::true_)
{
annotate(*fusion::find<position_tagged>(ast), first, last, mpl::true_());
// ^~~~~~~~~~~~
// to force the overloading that i need
}


private:

Container positions;
Expand Down
15 changes: 15 additions & 0 deletions include/boost/spirit/home/x3/support/ast/position_tagged_fwd.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*=============================================================================
Copyright (c) 2022 Denis Mikhailov

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)
==============================================================================*/
#if !defined(BOOST_SPIRIT_X3_POSITION_TAGGED_FWD_NOV_21_2022_1005PM)
#define BOOST_SPIRIT_X3_POSITION_TAGGED_FWD_NOV_21_2022_1005PM

namespace boost { namespace spirit { namespace x3
{
struct position_tagged;
}}}

#endif
20 changes: 20 additions & 0 deletions include/boost/spirit/home/x3/support/traits/move_to.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,18 @@
#include <boost/spirit/home/x3/support/traits/attribute_category.hpp>
#include <boost/spirit/home/x3/support/traits/tuple_traits.hpp>
#include <boost/spirit/home/x3/support/traits/variant_has_substitute.hpp>
#include <boost/spirit/home/x3/support/ast/position_tagged_fwd.hpp>

#include <boost/assert.hpp>
#include <boost/fusion/include/is_sequence.hpp>
#include <boost/fusion/include/front.hpp>
#include <boost/fusion/include/move.hpp>
#include <boost/fusion/include/is_sequence.hpp>
#include <boost/fusion/include/filter_view.hpp>

#include <boost/mpl/not.hpp>
#include <boost/type_traits/is_same.hpp>

#include <utility>

namespace boost { namespace spirit { namespace x3 { namespace traits
Expand Down Expand Up @@ -99,6 +105,20 @@ namespace boost { namespace spirit { namespace x3 { namespace traits
fusion::move(std::move(src), dest);
}

template <typename Source, typename Dest>
inline typename enable_if<
mpl::and_<
is_typecontaining_sequence<Source, position_tagged>,
mpl::not_<is_size_one_sequence<Dest> > >
>::type
move_to(Source& src, Dest& dest, tuple_attribute)
{
// FIXME disable this overload when `is_same_size_sequence<Dest, decltype(filtered_src)>` false
using condition_t = boost::mpl::not_<is_same<boost::mpl::_, position_tagged>>;
auto filtered_src = boost::fusion::filter_view<Source, condition_t>(src);
fusion::move(std::move(filtered_src), dest);
}

template <typename Source, typename Dest>
inline typename enable_if<
is_size_one_sequence<Dest>
Expand Down
Loading