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: plain into variant of single element tuple #716

Open
Bockeman opened this issue Jan 23, 2022 · 6 comments
Open

X3: plain into variant of single element tuple #716

Bockeman opened this issue Jan 23, 2022 · 6 comments

Comments

@Bockeman
Copy link

This code does not compile

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

namespace x3		= boost::spirit::x3;
template <typename T>
  struct as_type{
    template <typename E>
    constexpr auto operator[](E e) const {return x3::rule<struct _, T> {} = e;}
  };
template <typename T>
  static inline constexpr as_type<T> as;
namespace ast {
  using namespace std;
  struct tagged_string	: std::string	{};
  enum token_enum	{lcomment, rcomment};
  struct tagged_token	{token_enum token;};
  struct comment_item	: x3::variant<tagged_string, tagged_token>
    {using base_type::base_type;using base_type::operator=;};
  struct comment	: std::vector<comment_item>{
    using std::vector<comment_item>::vector;
    using std::vector<comment_item>::operator=;
  };
  struct start_item	: x3::variant<comment>
    {using base_type::base_type; using base_type::operator=;};
  struct start		: std::vector<start_item>{using std::vector<start_item>::vector;};
}
BOOST_FUSION_ADAPT_STRUCT(ast::tagged_token,		token)
namespace grammar {
  x3::symbols<ast::token_enum>	lcomment_token;
  x3::symbols<ast::token_enum>	rcomment_token;
  x3::rule<struct lcomment,	ast::tagged_token>	const tagged_lcomment	= "tagged_lcomment";
  x3::rule<struct rcomment,	ast::tagged_token>	const tagged_rcomment	= "tagged_rcomment";
  x3::rule<struct text,		ast::tagged_string>	const tagged_text	= "tagged_text";
  x3::rule<struct comment,	ast::comment>		const comment		= "comment";
  x3::rule<struct start_rule,	ast::start>		const start_rule	= "start_rule";

  auto const tagged_lcomment_def	= lcomment_token;				// /*
  auto const tagged_rcomment_def	= rcomment_token;				// */
  auto const tagged_text_def		= *(x3::char_ - rcomment_token);		// not */
  
  // Compile fails:
  auto const comment_def	= lcomment_token > tagged_text > rcomment_token;			// /* ... */
  
  // Compiles ok, but ast is not correctly assembled:
  //auto const comment_def	= (x3::rule<class id>{}=lcomment_token) > tagged_text > tagged_rcomment;// /* ... */

  // Compiles ok, and ast is ok:
  //auto const comment_def	= as<ast::tagged_token>[lcomment_token] > tagged_text > tagged_rcomment;// /* ... */
  //auto const comment_def	= tagged_lcomment > tagged_text > tagged_rcomment;			// /* ... */
  
  auto const start_rule_def	= x3::no_skip[+comment];	// 1 ... ;
  
  BOOST_SPIRIT_DEFINE(tagged_lcomment, tagged_rcomment, tagged_text, comment, start_rule);
}
int main() {
  char const*			iter	= "/** /**/", * const end	= iter + std::strlen(iter);
  ast::start			ast;
  grammar::lcomment_token.add	("/*",		ast::lcomment);
  grammar::rcomment_token.add	("*/",		ast::rcomment);
  bool ok			= parse(iter, end, grammar::start_rule, ast) && iter==end;
  if(ok)BOOST_ASSERT(3==boost::get<ast::comment>(ast[0].var).size());
  return !ok;
}

It seems the only way to successfully compile is to wrap the token (x3::symbols<>) into a rule (either inline, or with a separate rule definition). Is this an X3 implementation problem, or is it beyond the anticipated capabilities of X3?

@Kojoley
Copy link
Collaborator

Kojoley commented Jan 24, 2022

Don't know variant or single element tuple or both, but not x3::symbols:

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

namespace ast {
    struct tagged_token { int token; };
}
BOOST_FUSION_ADAPT_STRUCT(ast::tagged_token, token)

int main() {
    namespace x3 = boost::spirit::x3;
    char const* iter = "/** /**/", * const end = iter + std::strlen(iter);
    x3::variant<ast::tagged_token> x;
    x3::parse(iter, end, x3::attr(int{}), x);
}

@Kojoley Kojoley changed the title X3: problem with attributing x3::symbols<> X3: plain into variant of single element tuple Jan 24, 2022
@bfueldner
Copy link

bfueldner commented Jul 20, 2023

It seems that not the single element variant is the problem. It depends on the number of struct/class members. No member works, two or more members works but not one member:

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include <variant>

struct no_member {};

struct one_member {
    int a;
};
BOOST_FUSION_ADAPT_STRUCT(one_member, a)

struct two_members {
    int a{};
    int b{};
};
BOOST_FUSION_ADAPT_STRUCT(two_members, a, b)

int main()
{
    namespace x3 = boost::spirit::x3;

    char const* iter = "", * const end = iter + std::strlen(iter);
    std::variant<no_member, one_member, two_members> x;

    x3::parse(iter, end, x3::attr(no_member{}), x); // This line compiles
    x3::parse(iter, end, x3::attr(one_member{}), x); // This line failes
    x3::parse(iter, end, x3::attr(two_members{}), x); // This line compiles
}

Is there a workaround available for this behaviour?

@cppljevans
Copy link
Contributor

Use boost::variant instead of std::variant.
The problem, I'd guess, is:
https://www.boost.org/doc/libs/1_81_0/boost/spirit/home/x3/support/traits/is_variant.hpp
doesn't consider std::variant as a variant.

@cppljevans
Copy link
Contributor

cppljevans commented Jul 21, 2023

OOPS! Adding obvious change to is_variant.hpp not enough. With the obvious change, now getting, using std::variant:

C:/msys64/home/evansl/prog_dev/boost.org/1_80_0download/download/boost/spirit/home/x3/support/traits/variant_has_substitute.hpp:23:40: error: no type named 'types' in 'std::variant<no_member, one_member, two_members>'
        typedef typename variant_type::types types;
                ~~~~~~~~~~~~~~~~~~~~~~~^~~~~

@cppljevans
Copy link
Contributor

OOPS! Adding obvious change to is_variant.hpp not enough. With the obvious change, now getting, using std::variant:

C:/msys64/home/evansl/prog_dev/boost.org/1_80_0download/download/boost/spirit/home/x3/support/traits/variant_has_substitute.hpp:23:40: error: no type named 'types' in 'std::variant<no_member, one_member, two_members>'
        typedef typename variant_type::types types;
                ~~~~~~~~~~~~~~~~~~~~~~~^~~~~

However, changing obvious portion of variant_has_substitute.hpp to:

    template <typename Variant, typename T>
    struct variant_has_substitute_impl
    ;
    template <template<typename...>typename Variant, typename T, typename... Elements>
    struct variant_has_substitute_impl< Variant<Elements...>, T>
    {
        // Find a type from the Variant that can be a substitute for T.
        // return true_ if one is found, else false_

        typedef Variant<Elements...> variant_type;
        typedef fusion::vector<Elements...> types;

and #including <boost/fusion/container.hpp> before seems to solve problem.

@bfueldner
Copy link

@cppljevans Thank you for your response. Replacing std::variant with boost::variant (or x3::variant) did the trick!

Sorry for confusing with the original post of @Bockeman.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants