Question

I have a grammar rule that looks like this:

rule<Iterator, utree()> expr, factor;

expr =
    (
       +(
           (
              toks.symbol // toks.symbol attribute type is an std::string
              [
                 // I'm trying to force the attribute type to be utree
                 _val = construct<utree::list_type>()
                 // some code here
              ]
           |  (
                 factor >>
                +(
                    // the 3 tokens here have unsigned long attribute
                    toks.arrow >> factor
                 |  toks.squiggleArrow >> factor
                 |  toks.dot >> factor
                 )
              )
              [

                 // I'm trying to force the attribute type to be utree
                 _val = construct<utree::list_type>()
                 // some code here
              ]
           ) >> toks.assign // toks.assign is token_def<omit>
        ) >> factor
     ) [ _val = ternary(_1, _2) ]
   ;

As far as I understand from here:

http://boost-spirit.com/home/articles/attribute_handling/attribute-propagation-and-attribute-compatibility/

attribute compatibility should be disabled in this case because semantic actions are there. Nonetheless, I'm seeing a compilation error inside ternary() that suggests that the type of _1 is not a vector as I would expect, but rather it is:

vector<
    variant<std::string,
            fusion::vector2<utree,
                            fusion::vector2<long unsigned int, utree>
                           >
           >
       >

which means that for some reason, semantic actions didn't kick in!

Any hints why this is happening?

Note: I pasted a minimized example showing the problem here:

http://pastebin.com/rgiy2QBW

Thanks!

Was it helpful?

Solution

The assignment that the compiler complains about is inside the ternaryImpl::operator() body, which means that, obviously, the semantic actions did kick in!

Now, although you are right that SA's prevent automatic attribute propagation (unless operator %= is used for rule assignment), this does not mean that the type exposed by the primitive parsers magically changes.

The type you list in the question, accurately reflects what the parser expressions / operators will return:

  • Or (|) parses into a variant
  • Sequence (>>) parses into a fusion::vector2<...> etc.

Now, here is my simple, minimal change that will make this compile. The trick is, precisely, to make attribute assignment work for you, by splitting a subrule out with an explicit attribute type. This allows Spirit to do the attribute conversion for you.

struct Parser: public qi::grammar<Iterator, utree()>
{
    template <typename Tokens>
        Parser(const Tokens &toks):
            Parser::base_type(expression)
    {
        chain = +(
            (
               toks.symbol
               [
                  _val = construct<utree::list_type>()
                  // some code here
               ]
            |  (
                  factor >>
                 +(
                     toks.arrow >> factor
                  |  toks.dot >> factor
                  )
               )
               [
                  _val = construct<utree::list_type>()
                  // some code here
               ]
            ) >> toks.assign
         );
        expression = factor
            | (chain >> factor) [ _val = ternary(_1, _2) ]
            ;
    }

   rule<Iterator, utree::list_type()> chain;
   rule<Iterator, utree()> expression, factor, test;
};

Note: if you want you should be able to do the same without extra rule definitions (using qi::attr_cast<>, qi::as<> e.g.) but I doubt it will be readable/maintainable.

PS. Similar points can be taken away from the calc_utree_naive.cpp which, necessarily, spells more explicit rule attribute types than the calc_utree_ast.cpp version, which uses SA's.

Here is the complete, compiling version, with some inline notes in comments:

// #define BOOST_SPIRIT_USE_PHOENIX_V3
// #define BOOST_RESULT_OF_USE_DECLTYPE
#include <algorithm>
#include <string>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_utree.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>

namespace lex    = boost::spirit::lex;
namespace qi     = boost::spirit::qi;
namespace spirit = boost::spirit;
namespace phx    = boost::phoenix;

using lex::token_def;
using qi::rule;
using qi::_1;
using qi::_2;
using qi::_val;
using spirit::utree;
using phx::construct;

// base iterator type
typedef std::string::iterator BaseIteratorT;

// token type
typedef lex::lexertl::token<BaseIteratorT, boost::mpl::vector</*double, int, */std::string> > TokenT;

// lexer type
typedef lex::lexertl::actor_lexer<TokenT> LexerT;

template <typename LexerT>
struct Tokens: public lex::lexer<LexerT>
{
   Tokens()
   {
      using lex::_pass;
      using lex::pass_flags;

      // literals
      symbol = "[a-zA-Z_?](\\w|\\?)*|@(\\w|\\?)+";
      arrow  = "->";
      dot    = '.';
      assign = "=";

      // literal rules
      this->self += symbol;
      this->self += arrow;
      this->self += dot;
      this->self += assign;
   }

   ~Tokens() {}

   // literal tokens
   token_def<std::string> symbol;
   token_def<> arrow, dot; // HINT: lex::omit here? 
   /*
    * ^ Otherwise, expect these to be all exposed as Qi attributes as well, so
    * _1, _2, _3, _4 a bit more than you'd expect
    */
   token_def<lex::omit> assign;
};

struct ternaryImpl
{
   template <typename Expr1Type, typename Expr2Type>
   struct result { typedef utree type; };

   template <typename Expr1Type, typename Expr2Type>
   utree operator()(Expr1Type &vec, Expr2Type &operand) const {
      utree ret;

      for (typename Expr1Type::iterator it = vec.begin(); it != vec.end(); ++it) {
         // some code
         ret = *it;
         // more code
      }

      // some code here

      return ret;
   }
};

phx::function<ternaryImpl> ternary = ternaryImpl();

template <typename Iterator>
struct Parser: public qi::grammar<Iterator, utree()>
{
    template <typename Tokens>
        Parser(const Tokens &toks):
            Parser::base_type(expression)
    {
        chain = +(
            (
               toks.symbol
               [
                  _val = construct<utree::list_type>()
                  // some code here
               ]
            |  (
                  factor >>
                 +(
                     toks.arrow >> factor
                  |  toks.dot >> factor
                  )
               )
               [
                  _val = construct<utree::list_type>()
                  // some code here
               ]
            ) >> toks.assign
         );
        expression = factor
            | (chain >> factor) [ _val = ternary(_1, _2) ]
            ;
    }

   rule<Iterator, utree::list_type()> chain;
   rule<Iterator, utree()> expression, factor, test;
};

int main()
{
   typedef Tokens<LexerT>::iterator_type IteratorT;

   Tokens<LexerT> l;
   Parser<IteratorT> p(l);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top