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...
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 << ")";
}
} }
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()))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With