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:

live on coliru

#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:

live on coliru

#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

Popular posts from this blog

html - Outlook 2010 Anchor (url/address/link) -

javascript - Why does running this loop 9 times take 100x longer than running it 8 times? -

Getting gateway time-out Rails app with Nginx + Puma running on Digital Ocean -