Ambiguous variant and boost spirit x3
Asked Answered
H

1

10

Trying to tweak the boost spirit x3 calc example to parse functions that can take functions as arguments. However it does not compile.

namespace client{ namespace ast{
    struct ts;
    struct fnc;
    typedef boost::variant<
    ts,
    boost::recursive_wrapper<fnc>
    > node;
    struct ts{
        unsigned int id;
    };
    struct fnc{
        std::vector<char> id;
        std::vector<node> args;
    };
}}
BOOST_FUSION_ADAPT_STRUCT(
    client::ast::ts,
    (unsigned int, id)
)
BOOST_FUSION_ADAPT_STRUCT(
    client::ast::fnc,
    (std::vector<char>, id)
    (std::vector<client::ast::node>, args)
)
namespace client{
    namespace x3 = boost::spirit::x3;
    namespace calc_grammar{
        using x3::uint_;
        using x3::alpha;
        using x3::alnum;
        using x3::lit;
        using x3::char_;
        x3::rule<class funct, ast::fnc> const funct("function");
        x3::rule<class ts, ast::ts> const ts("timeseries");
        x3::rule<class funct_name, std::vector<char>> const funct_name("function_name");
        auto const funct_def = funct_name >> lit('(') >> -((ts|funct)%lit(',')) >> lit(')');
        auto const ts_def = lit('#') >> uint_ >> lit('#');
        auto const funct_name_def = lit('@') >> alpha >> *(alnum|char_('_'));
        auto const calc = x3::grammar(
            "calc", 
            funct = funct_def, 
            ts = ts_def,
            funct_name = funct_name_def
        );
    }
    using calc_grammar::calc;
}

error C2665: 'boost::detail::variant::make_initializer_node::apply::initializer_node::initialize': none of the 5 overloads could convert all the argument types

There's also a note to user in variant.hpp

// NOTE TO USER :
// Compile error here indicates that the given type is not 
// unambiguously convertible to one of the variant's types
// (or that no conversion exists).

Yet I am none the wiser...

Hest answered 24/6, 2013 at 7:55 Comment(11)
Until you get a better answer adding a default constructor that does nothing and a constructor that takes an unsigned int and stores it in id to your ts struct seems to make it work for me with g++ 4.8.1.Surmullet
@cv_and_he completely agree with the analysis; @Hest you may want to report this issue at the [spirit-general] user list - I think the devs are actively solliciting feedback (boost-spirit.com seems down at the moment)Antineutron
@cv_and_he thanks for tips, but still did't fix it. using mvsc, i guess i should have put that in my initial post.Hest
@not-sehe i will try that, i put it here first because it seemed less daunting.Hest
@Hest I agree: SO is sweet. But, boost-spirit-x3 has very limited audience as of now, so your chances are better at the list (and your feedback will also be reach the interested devs!). Cheers. (I created the new tag, by the way)Antineutron
MSVC shouldn't matter that much: uncomment the two constructors in this sample to verify my quick test: ideone.com/ZlWcMR (and the diff: paste.ubuntu.com/5797692) (PS. with the ctors uncommented, it should compile)Antineutron
@not-sehe neither compiles, regardless of level of commenting on the ctors. same error.Hest
@Hest Wokay. I'm sad to hear that. I can only assume it will be due the lackluster support for buggy-adics in MSVC :( (I'd still report it, it might be down to a bug in boost-svn's variant.hpp for MSVC)Antineutron
@not-sehe yep, i sent a mail to spirit-general, and am now crossing my fingers. thanks!Hest
@Hest I haven't seen the message yet. Did you subscribe (with the email address you wanted to use) first?Antineutron
@not-sehe i thought i had, somehow i managed to mess that up too i think... tried again now.Hest
A
6

I spotted this old question. X3 has evolved a bit in the mean time and I though I'd answer it now anyways.

I suspected that the main issue might have been with (missing) (implicit) constructors on the variant members.

Anyhow, here's a live demo with a more lightweight grammar:

namespace grammar_def {
    using namespace x3;

    rule<class funct, ast::fnc> const funct("function");

    auto const ts        = lexeme [ '#' >> uint_ >> '#' ];
    auto const fname     = lexeme [ '@' >> raw [ alpha >> *(alnum | '_') ] ];
    auto const expr      = ts|funct;

    auto const funct_def = fname >> '(' >> -expr % ',' >> ')';

    BOOST_SPIRIT_DEFINE(funct)
}

I also added some output streaming helpers. Note how I changed the id type to std::string for simplicity (it's hard/impossible to overload operator<< for vector<char> without invading namespace std):

namespace client { namespace ast {
    static std::ostream& operator<<(std::ostream& os, ts const& v) {
        using namespace boost::fusion;
        return os << tuple_open("") << tuple_close("") << tuple_delimiter("") << as_vector(v);
    }

    static std::ostream& operator<<(std::ostream& os, fnc const& v) {
        using namespace boost::fusion;
        return os << tuple_open("") << tuple_close("") << tuple_delimiter("") << as_vector(v);
    }
    template<typename T>
    static std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
        os << "("; for (auto& el : v) os << (&el==&v[0]?"":", ") << el; return os << ")";
    }
} }

Demo

This has more (optional) plumbing to allow for richer debug information:

Live On Coliru

//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/x3.hpp>

namespace client { namespace ast {
    struct ts;
    struct fnc;

    //using string = std::vector<char>;
    using string = std::string; // for easier printing/debugging

    struct ts {
        unsigned int id;
        ts(unsigned id=0):id(id) {}
    };

    typedef boost::variant<ts, boost::recursive_wrapper<fnc> > node;

    struct fnc {
        string id;
        std::vector<node> args;
    };
} }

BOOST_FUSION_ADAPT_STRUCT(client::ast::ts, id)
BOOST_FUSION_ADAPT_STRUCT(client::ast::fnc, id, args)

//namespace std { static ostream& operator<<(ostream&os, vector<char> const& v) { return os.write(&v[0], v.size()); } }

namespace client { namespace ast {
    static std::ostream& operator<<(std::ostream& os, ts const& v) {
        using namespace boost::fusion;
        return os << tuple_open("") << tuple_close("") << tuple_delimiter("") << as_vector(v);
    }

    static std::ostream& operator<<(std::ostream& os, fnc const& v) {
        using namespace boost::fusion;
        return os << tuple_open("") << tuple_close("") << tuple_delimiter("") << as_vector(v);
    }
    template<typename T>
    static std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
        os << "("; for (auto& el : v) os << (&el==&v[0]?"":", ") << el; return os << ")";
    }
} }

namespace client {
    namespace x3 = boost::spirit::x3;
    namespace grammar_def {
        using namespace x3;

        x3::rule<class funct, ast::fnc> const funct("function");

        auto const ts     // = x3::rule<class ts, ast::ts> {"timeseries"}
                             = lexeme [ '#' >> uint_ >> '#' ];
        auto const fname  // = x3::rule<class fname, ast::string> {"function_name"}
                             = lexeme [ '@' >> raw [ alpha >> *(alnum | '_') ] ];
        auto const expr   // = rule<struct expr_, ast::node > {"expr"}
                             = ts|funct;

        auto const funct_def = fname >> '(' >> -expr % ',' >> ')';

        BOOST_SPIRIT_DEFINE(funct)
    }

    auto const& grammar = x3::skip(x3::space) [grammar_def::funct];
}

#include <iostream>

int main() {
    std::string const s {
        "@pow( #1#, \n"
            "     @trunc(\n"
            "           @pi ()\n"
            "   ) )"};
    std::cout << "Parsing '" << s << "'\n";

    auto f = s.begin();
    client::ast::fnc parsed;

    if (parse(f, s.end(), client::grammar, parsed)) {
        std::cout << "Parse succeeded: " << parsed << "\n";
    } else {
        std::cout << "Parse failed\n";
    }

    if (f != s.end())
        std::cout << "Remaining unparsed input: '" << std::string(f, s.end()) << "'\n";
}

Prints:

Parsing '@pow( #1#, 
     @trunc(
           @pi ()
   ) )'
Parse succeeded: pow(1, trunc(pi()))
Antineutron answered 4/1, 2016 at 1:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.