c++ - Why can I not access the value in a semantic action? -
i'm trying write parser create ast using boost::spirit. first step i'm trying wrap numerical values in ast node. code i'm using:
ast_nodeptr make_ast_nodeptr(const int& i) { return std::make_shared<ast_node>(i); } namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; namespace l = qi::labels; template<typename iterator> struct test_grammar : qi::grammar<iterator, ast_nodeptr(), ascii::space_type> { test_grammar() : test_grammar::base_type(test) { test = qi::int_ [qi::_val = make_ast_nodeptr(qi::_1)]; } qi::rule<iterator, ast_nodeptr(), ascii::space_type> test; };
as far understood documentation q::_1 should contain value parsed qi::int_, above code gives me error along lines
invalid initialization of reference of type ‘const int&’ expression of type ‘const _1_type {aka const boost::phoenix::actor<boost::spirit::argument<0> >}
why not work though qi::_1 supposed hold parsed valued? how else parse input custom ast?
you're using regular function inside semantic action.
this means in contructor compiler try invoke make_ast_nodeptr
function argument supplied: qi::_1
.
q. why not work though qi::_1 supposed hold parsed valued?
a. qi::_1
not hold parsed value. represents (is-a-placeholder-for) first unbound argument in function call
this can /obviously/ never work. function expects integer.
so gives?
you need make "lazy" or "deferred" function use in semantic action. using pre-supplied boost phoenix functors, spell out:
test = qi::int_ [ qi::_val = px::construct<ast_nodeptr>(px::new_<ast_node>(qi::_1)) ];
you don't need helper function way. result both ugly , suboptimal. so, let's better!
using phoenix function wrapper
struct make_shared_f { std::shared_ptr<ast_node> operator()(int v) const { return std::make_shared<ast_node>(v); } }; px::function<make_shared_f> make_shared_;
with defined, can simplify semantic action to:
test = qi::int_ [ qi::_val = make_shared_(qi::_1) ];
actually, if make generic can reuse many types:
template <typename t> struct make_shared_f { template <typename... args> std::shared_ptr<t> operator()(args&&... args) const { return std::make_shared<t>(std::forward<args>(args)...); } }; px::function<make_shared_f<ast_node> > make_shared_;
demo
here's self-contained example showing style fixes in process:
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> #include <memory> struct ast_node { ast_node(int v) : _value(v) {} int value() const { return _value; } private: int _value; }; using ast_nodeptr = std::shared_ptr<ast_node>; ast_nodeptr make_ast_nodeptr(const int& i) { return std::make_shared<ast_node>(i); } namespace qi = boost::spirit::qi; namespace px = boost::phoenix; template<typename iterator> struct test_grammar : qi::grammar<iterator, ast_nodeptr()> { test_grammar() : test_grammar::base_type(start) { using boost::spirit::ascii::space; start = qi::skip(space) [ test ]; test = qi::int_ [ qi::_val = make_shared_(qi::_1) ]; } private: struct make_shared_f { std::shared_ptr<ast_node> operator()(int v) const { return std::make_shared<ast_node>(v); } }; px::function<make_shared_f> make_shared_; // qi::rule<iterator, ast_nodeptr()> start; qi::rule<iterator, ast_nodeptr(), boost::spirit::ascii::space_type> test; }; int main() { ast_nodeptr parsed; std::string const input ("42"); auto f = input.begin(), l = input.end(); test_grammar<std::string::const_iterator> g; bool ok = qi::parse(f, l, g, parsed); if (ok) { std::cout << "parsed: " << (parsed? std::to_string(parsed->value()) : "nullptr") << "\n"; } else { std::cout << "failed\n"; } if (f!=l) { std::cout << "remaining input: '" << std::string(f, l) << "'\n"; } }
prints
parsed: 42
bonus: alternative using boost_phoenix_adapt_function
you can use free function if wish, , use follows:
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> #include <memory> struct ast_node { ast_node(int v) : _value(v) {} int value() const { return _value; } private: int _value; }; using ast_nodeptr = std::shared_ptr<ast_node>; ast_nodeptr make_ast_nodeptr(int i) { return std::make_shared<ast_node>(i); } boost_phoenix_adapt_function(ast_nodeptr, make_ast_nodeptr_, make_ast_nodeptr, 1) namespace qi = boost::spirit::qi; namespace px = boost::phoenix; template<typename iterator> struct test_grammar : qi::grammar<iterator, ast_nodeptr()> { test_grammar() : test_grammar::base_type(start) { using boost::spirit::ascii::space; start = qi::skip(space) [ test ] ; test = qi::int_ [ qi::_val = make_ast_nodeptr_(qi::_1) ] ; } private: qi::rule<iterator, ast_nodeptr()> start; qi::rule<iterator, ast_nodeptr(), boost::spirit::ascii::space_type> test; }; int main() { ast_nodeptr parsed; std::string const input ("42"); auto f = input.begin(), l = input.end(); test_grammar<std::string::const_iterator> g; bool ok = qi::parse(f, l, g, parsed); if (ok) { std::cout << "parsed: " << (parsed? std::to_string(parsed->value()) : "nullptr") << "\n"; } else { std::cout << "failed\n"; } if (f!=l) { std::cout << "remaining input: '" << std::string(f, l) << "'\n"; } }
Comments
Post a Comment