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...

Markus
  • 3,225
  • 6
  • 35
  • 47
user2515328
  • 101
  • 3
  • 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. – llonesmiz Jun 24 '13 at 13:57
  • @cv_and_he completely agree with the analysis; @user2515328 you may want to report this issue at the `[spirit-general]` user list - I think the devs are actively solliciting feedback (http://boost-spirit.com/ seems down at the moment) – sehe Jun 24 '13 at 23:02
  • @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. – user2515328 Jun 25 '13 at 06:41
  • @not-sehe i will try that, i put it here first because it seemed less daunting. – user2515328 Jun 25 '13 at 06:42
  • @user2515328 I agree: SO is sweet. But, [tag: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) – sehe Jun 25 '13 at 06:45
  • MSVC shouldn't matter that much: uncomment the two constructors in this sample to verify my quick test: http://ideone.com/ZlWcMR (and the diff: http://paste.ubuntu.com/5797692/) (PS. with the ctors uncommented, it should compile) – sehe Jun 25 '13 at 06:50
  • @not-sehe neither compiles, regardless of level of commenting on the ctors. same error. – user2515328 Jun 25 '13 at 07:15
  • @user2515328 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) – sehe Jun 25 '13 at 07:17
  • @not-sehe yep, i sent a mail to spirit-general, and am now crossing my fingers. thanks! – user2515328 Jun 25 '13 at 07:19
  • @user2515328 I haven't seen the message yet. Did you subscribe (with the email address you wanted to use) first? – sehe Jun 25 '13 at 10:12
  • @not-sehe i thought i had, somehow i managed to mess that up too i think... tried again now. – user2515328 Jun 25 '13 at 11:51

1 Answers1

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()))
sehe
  • 374,641
  • 47
  • 450
  • 633