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{ namespa
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 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
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
static std::ostream& operator<<(std::ostream& os, std::vector 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
#include
#include
#include
#include
namespace client { namespace ast {
struct ts;
struct fnc;
//using string = std::vector;
using string = std::string; // for easier printing/debugging
struct ts {
unsigned int id;
ts(unsigned id=0):id(id) {}
};
typedef boost::variant > node;
struct fnc {
string id;
std::vector 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 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
static std::ostream& operator<<(std::ostream& os, std::vector 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 const funct("function");
auto const ts // = x3::rule {"timeseries"}
= lexeme [ '#' >> uint_ >> '#' ];
auto const fname // = x3::rule {"function_name"}
= lexeme [ '@' >> raw [ alpha >> *(alnum | '_') ] ];
auto const expr // = rule {"expr"}
= ts|funct;
auto const funct_def = fname >> '(' >> -expr % ',' >> ')';
BOOST_SPIRIT_DEFINE(funct)
}
auto const& grammar = x3::skip(x3::space) [grammar_def::funct];
}
#include
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()))