1

I am having trouble making a visitor for the following reduced program:

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi_expect.hpp>
#include <boost/spirit/home/x3/directive/expect.hpp>

#include <iostream>
#include <ostream>
#include <string>

namespace x3 = boost::spirit::x3;
namespace ascii = x3::ascii;
namespace qi = boost::spirit::qi;

namespace wctl_parser {

struct Boolean;
struct Conjunction;
struct ArithmeticBinaryExpression;

using AtomicProp = std::string;

struct Boolean {
    bool boolean;
};

using ArithmeticExpression = x3::variant<
    unsigned long,
    AtomicProp,
    x3::forward_ast<ArithmeticBinaryExpression>
>;

using Expression = x3::variant<
    AtomicProp,
    Boolean,
    x3::forward_ast<Conjunction>,
    x3::forward_ast<ArithmeticExpression>
>;

struct Conjunction {
    x3::forward_ast<Expression> left;
    x3::forward_ast<Expression> right;
};

struct ArithmeticBinaryExpression {
    x3::forward_ast<ArithmeticExpression> left;
    std::string op;
    x3::forward_ast<ArithmeticExpression> right;    
};

} // End namespace

// BOOST_FUSION_ADAPT_STRUCT(wctl_parser::AtomicProp, prop)
BOOST_FUSION_ADAPT_STRUCT(wctl_parser::Boolean, boolean)
BOOST_FUSION_ADAPT_STRUCT(wctl_parser::Conjunction, left, right)
BOOST_FUSION_ADAPT_STRUCT(wctl_parser::ArithmeticBinaryExpression, left, op, right)
using namespace wctl_parser;

struct SyntaxTreePrinter {

    SyntaxTreePrinter(std::ostream& out) : out(out) {};

    using result_type = void;

    template<typename T>
    void operator()(const x3::forward_ast<T>& forwarded) {
        (*this)(forwarded.get());
    }

    void operator()(const ArithmeticExpression& expression) {
        // expression.apply_visitor(*this);
    }

    void operator()(const Expression& expression) {
        // expression.apply_visitor(*this);
    }

    void operator()(const AtomicProp& property) {
        out << '[' << property << ']';
    }

    void operator()(const long unsigned int expr) {
        out << '[' << expr << ']';
    }

    void operator()(const Boolean& boolean) {
        out << '[' << (boolean.boolean ? "true" : "false") << ']';
    };

    void operator()(const Conjunction& expr) {
        out << "[AND ";
        boost::apply_visitor(*this, expr.left);
        out << ' ';
        boost::apply_visitor(*this, expr.right);
        out << ']';
    };

    void operator()(const ArithmeticBinaryExpression& expr) {
        out << '[' << expr.op << ' ';
        boost::apply_visitor(*this, expr.left);
        out << ' ';
        boost::apply_visitor(*this, expr.right);
        out << ']';
    };

private:
    std::ostream& out;
};

int main() {
    wctl_parser::Expression root;
    auto visitor = SyntaxTreePrinter(std::cout);
    root.apply_visitor(visitor);
    return 0;
}

It seems x3::ast_forward and boost::apply_visitor are not compatible. I tried manually making the first template in SyntaxTreePrinter, but it does help. The compilation errors I get are:

In instantiation of ‘typename Visitor::result_type boost::apply_visitor(Visitor&, Visitable&) [with Visitor = SyntaxTreePrinter; Visitable = const boost::spirit::x3::forward_ast<boost::spirit::x3::variant<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, wctl_parser::Boolean, boost::spirit::x3::forward_ast<wctl_parser::Conjunction>, boost::spirit::x3::forward_ast<boost::spirit::x3::variant<long unsigned int, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, boost::spirit::x3::forward_ast<wctl_parser::ArithmeticBinaryExpression> > > > >; typename Visitor::result_type = void]’:

./minimal_error.cpp:94:46:   required from here

/boost_1_65_1/boost/variant/detail/apply_visitor_unary.hpp:70:22: error: ‘const class boost::spirit::x3::forward_ast<boost::spirit::x3::variant<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, wctl_parser::Boolean, boost::spirit::x3::forward_ast<wctl_parser::Conjunction>, boost::spirit::x3::forward_ast<boost::spirit::x3::variant<long unsigned int, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, boost::spirit::x3::forward_ast<wctl_parser::ArithmeticBinaryExpression> > > > >’ has no member named ‘apply_visitor’
     return visitable.apply_visitor(visitor);
            ~~~~~~~~~~^~~~~~~~~~~~~

/boost_1_65_1/boost/variant/detail/apply_visitor_unary.hpp:70:43: error: return-statement with a value, in function returning 'void' [-fpermissive]
     return visitable.apply_visitor(visitor);

How do I properly construct a visitor for my AST?

senevoldsen
  • 337
  • 3
  • 8
  • 1
    https://wandbox.org/permlink/YlVEPhgKPNMiKADh – llonesmiz Nov 03 '17 at 14:10
  • Thanks, that did the trick. So it is because of the limited deduction/overload resolutions right of two consecutive x3::forward_ast, but it is fine if there is a ::variant between? – senevoldsen Nov 03 '17 at 14:44
  • I think the problem was that you were using `forward_ast` where it wasn't needed(marked `//CHANGED` in the example) . With your comment I think you are referring to the awful construction of root and that can be helped by overloading operators, or sidestepped if you get your type from Spirit. – llonesmiz Nov 03 '17 at 15:22

0 Answers0