c++parsingboostboost-spirit-qi

Boost::spirit::qi compiler error when trying to integrate a skip parser


I'm trying for the first time to write my own grammar with spirit::qi, and I don't succeed in integrating a skip parser that should ignore C style comments as well as whitespaces. Here's my source file (I reduced the grammar to just a few rules, in order to be more concise:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "formulaNode.h"

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

using FormulaNodePtr = compiler::FormulaNode*;
using ExpressionPtr = compiler::Expression*;

template <typename Iterator>
struct ArithmeticGrammar : qi::grammar<Iterator, FormulaNodePtr(), qi::space_type> {
    ArithmeticGrammar() : ArithmeticGrammar::base_type(formula, "Formula") {
        using namespace qi;

        localSkipper = qi::space | ("/*" >> *(qi::char_ - "*/") >> "*/");

        formula = unsignedFactor [_val = _1];

        unsignedFactor =
            numericConstant [_val = phoenix::new_<compiler::Constant>(_1)];

        numericConstant = qi::as_string[
            qi::lexeme[  -qi::char_('-') >>
                (+qi::digit >> -(qi::char_('.') >> +qi::digit)) >>
                -(qi::char_('e') >> -qi::char_('-') >> +qi::digit) 
            ]];
    }

    qi::rule<Iterator, FormulaNodePtr(), qi::space_type> formula;
    qi::rule<Iterator, ExpressionPtr(), qi::space_type> unsignedFactor;
    qi::rule<Iterator, std::string(), qi::space_type> numericConstant;
    qi::rule<Iterator, qi::space_type> localSkipper;
};

// Parsing function
bool parseExpression(const std::string& input, FormulaNodePtr& result)
{
    using Iterator = std::string::const_iterator;
    ArithmeticGrammar<Iterator> grammar;

    Iterator it = input.begin();
    Iterator end = input.end();

    bool success = qi::phrase_parse(it, end, grammar, grammar.localSkipper, result);

    if (!success || it != end)
    {
        if (result == nullptr) {
            std::cout << "Parsing failed\n";
        }
        delete result;
        result = nullptr;
    }

    return success && (it == end);
}

int main()
{
    std::string input;
    std::cout << "Enter input: ";
    std::getline(std::cin, input);

    while (!input.empty()) {
        FormulaNodePtr result = nullptr;
        if (parseExpression(input, result))
        {
            std::cout << "Parsed successfully. Expression tree:" << std::endl;
            result->print(std::cout, 0);
            delete result;
        } else {
            std::cout << "Parsing failed. Please enter a valid expression." << std::endl;
        }
        std::getline(std::cin, input);
    }

    return EXIT_SUCCESS;
}

The way I use the skipper parser is taken from various internet examples. Yet when I try to compile this, I get this error message - can someone explain that to me?

g++ -std=c++11 -Wall -Wextra -pedantic Main2.cpp -o parser
In file included from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14,
from /usr/include/boost/spirit/home/qi.hpp:21,
from /usr/include/boost/spirit/include/qi.hpp:16,
from Main2.cpp:1:
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp: In instantiation of ‘bool boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Context = boost::spirit::context<boost::fusion::cons<compiler::FormulaNode*&, boost::fusion::nil_>, boost::spirit::locals<> >; Skipper = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type> >; Attribute = compiler::FormulaNode*; Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; T1 = compiler::FormulaNode*(); T2 = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>; T3 = boost::spirit::unused_type; T4 = boost::spirit::unused_type]’:
/usr/include/boost/spirit/home/qi/reference.hpp:43:72:   required from ‘bool boost::spirit::qi::reference<Subject>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; Context = boost::spirit::context<boost::fusion::cons<compiler::FormulaNode*&, boost::fusion::nil_>, boost::spirit::locals<> >; Skipper = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type> >; Attribute = compiler::FormulaNode*; Subject = const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, compiler::FormulaNode*(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type>]’
/usr/include/boost/spirit/home/qi/parse.hpp:164:13:   required from ‘bool boost::spirit::qi::phrase_parse(Iterator&, Iterator, const Expr&, const Skipper&, boost::spirit::qi::skip_flag, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; Expr = ArithmeticGrammar<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> > >; Skipper = boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type>; Attr = compiler::FormulaNode*]’
/usr/include/boost/spirit/home/qi/parse.hpp:197:32:   required from ‘bool boost::spirit::qi::phrase_parse(Iterator&, Iterator, const Expr&, const Skipper&, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; Expr = ArithmeticGrammar<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> > >; Skipper = boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type>; Attr = compiler::FormulaNode*]’
Main2.cpp:49:83:   required from here
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:304:17: error: no match for call to ‘(const function_type {aka const boost::function<bool(__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, boost::spirit::context<boost::fusion::cons<compiler::FormulaNode*&, boost::fusion::nil_>, boost::fusion::vector<> >&, const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >&)>}) (__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, compiler::FormulaNode*(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type>::context_type&, const boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type> >&)’
if (f(first, last, context, skipper))
                 ^~
In file included from /usr/include/boost/function/detail/maybe_include.hpp:43,
from /usr/include/boost/function/detail/function_iterate.hpp:14,
from /usr/include/boost/preprocessor/iteration/detail/iter/forward1.hpp:67,
from /usr/include/boost/function.hpp:70,
from /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:16,
from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14,
from /usr/include/boost/spirit/home/qi.hpp:21,
from /usr/include/boost/spirit/include/qi.hpp:16,
from Main2.cpp:1:
/usr/include/boost/function/function_template.hpp:754:17: note: candidate: ‘boost::function4<R, T1, T2, T3, T4>::result_type boost::function4<R, T1, T2, T3, T4>::operator()(T0, T1, T2, T3) const [with R = bool; T0 = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&; T1 = const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&; T2 = boost::spirit::context<boost::fusion::cons<compiler::FormulaNode*&, boost::fusion::nil_>, boost::fusion::vector<> >&; T3 = const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >&; boost::function4<R, T1, T2, T3, T4>::result_type = bool]’
result_type operator()(BOOST_FUNCTION_PARMS) const
                 ^~~~~~~~
/usr/include/boost/function/function_template.hpp:754:17: note:   no known conversion for argument 4 from ‘const boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type> >’ to ‘const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >&’
In file included from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14,
from /usr/include/boost/spirit/home/qi.hpp:21,
from /usr/include/boost/spirit/include/qi.hpp:16,
from Main2.cpp:1:
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp: In instantiation of ‘bool boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Context = const boost::spirit::unused_type; Skipper = boost::spirit::unused_type; Attribute = const boost::spirit::unused_type; Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; T1 = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>; T2 = boost::spirit::unused_type; T3 = boost::spirit::unused_type; T4 = boost::spirit::unused_type]’:
/usr/include/boost/spirit/home/qi/reference.hpp:43:72:   required from ‘bool boost::spirit::qi::reference<Subject>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; Context = const boost::spirit::unused_type; Skipper = boost::spirit::unused_type; Attribute = const boost::spirit::unused_type; Subject = const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type>]’
/usr/include/boost/spirit/home/qi/skip_over.hpp:27:30:   required from ‘void boost::spirit::qi::skip_over(Iterator&, const Iterator&, const T&) [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; T = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type> >]’
/usr/include/boost/spirit/home/qi/parse.hpp:169:26:   required from ‘bool boost::spirit::qi::phrase_parse(Iterator&, Iterator, const Expr&, const Skipper&, boost::spirit::qi::skip_flag, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; Expr = ArithmeticGrammar<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> > >; Skipper = boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type>; Attr = compiler::FormulaNode*]’
/usr/include/boost/spirit/home/qi/parse.hpp:197:32:   required from ‘bool boost::spirit::qi::phrase_parse(Iterator&, Iterator, const Expr&, const Skipper&, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; Expr = ArithmeticGrammar<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> > >; Skipper = boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type>; Attr = compiler::FormulaNode*]’
Main2.cpp:49:83:   required from here
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:304:17: error: no match for call to ‘(const function_type {aka const boost::function<bool(__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type&, boost::fusion::nil_>, boost::fusion::vector<> >&, const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >&)>}) (__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type>::context_type&, const boost::spirit::unused_type&)’
if (f(first, last, context, skipper))
                 ^~
In file included from /usr/include/boost/function/detail/maybe_include.hpp:43,
from /usr/include/boost/function/detail/function_iterate.hpp:14,
from /usr/include/boost/preprocessor/iteration/detail/iter/forward1.hpp:67,
from /usr/include/boost/function.hpp:70,
from /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:16,
from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14,
from /usr/include/boost/spirit/home/qi.hpp:21,
from /usr/include/boost/spirit/include/qi.hpp:16,
from Main2.cpp:1:
/usr/include/boost/function/function_template.hpp:754:17: note: candidate: ‘boost::function4<R, T1, T2, T3, T4>::result_type boost::function4<R, T1, T2, T3, T4>::operator()(T0, T1, T2, T3) const [with R = bool; T0 = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&; T1 = const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&; T2 = boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type&, boost::fusion::nil_>, boost::fusion::vector<> >&; T3 = const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >&; boost::function4<R, T1, T2, T3, T4>::result_type = bool]’
result_type operator()(BOOST_FUNCTION_PARMS) const
                 ^~~~~~~~
/usr/include/boost/function/function_template.hpp:754:17: note:   no known conversion for argument 4 from ‘const boost::spirit::unused_type’ to ‘const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >&’

When I replace "grammar.localSkipper" with "qi::space", it compiles without error. But of course, I don't have comment skipping.


Solution

  • First, don't use raw pointers in semantic actions or as attribute types:

    Second, the skipper MUST be compatible with the rule's declared skipper type. See Boost spirit skipper issues. Most likely you will come across one of these asserts in the diagnostics:

            BOOST_STATIC_ASSERT_MSG(
                (is_convertible<Skipper const&, skipper_type>::value),
                "The passed skipper is not compatible/convertible to one "
                "that the rule was instantiated with");
    

    In your code, the declared skipper type is qi::space_type BUT you try to pass localSkipper. Fixing the incorrect declaration of it without its own skipper:

    qi::rule<Iterator> localSkipper;
    

    Now fix the rules:

    using Skipper = qi::rule<Iterator>;
    qi::rule<Iterator, FormulaNodePtr(), Skipper> formula;
    qi::rule<Iterator, ExpressionPtr(), Skipper>  unsignedFactor;
    
    // lexemes
    qi::rule<Iterator, std::string()> numericConstant;
    Skipper                           localSkipper;
    

    NOTE The numericConstant drops the skipper so it is implicitly a lexeme (see again above link). That means qi::lexeme[] can be avoided.

    Consider replacing that rule with just numericConstant = qi::raw[ qi::double_ ];

    Even better, do not expose the skipper at all, since it's not a "user-serviceable part":

    private:
      using Skipper = qi::rule<Iterator>;
      qi::rule<Iterator, FormulaNodePtr(), Skipper> formula;
      qi::rule<Iterator, ExpressionPtr(), Skipper>  unsignedFactor;
    
      // lexemes
      qi::rule<Iterator, std::string()> numericConstant;
      Skipper                           localSkipper;
    

    Now to avoid dangling references you have to very subtly alias the localSkipper in qi::skip:

    start = qi::skip(localSkipper.alias())[formula];
    

    Live Demo

    Catering for all of the above (and missing code including a pointer conversion):

    Live On Coliru

    #define BOOST_ALLOW_DEPRECATED_HEADERS
    #include <boost/spirit/include/phoenix.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <iostream>
    
    namespace compiler {
        struct Expression {
            virtual ~Expression() = default;
            virtual void print(std::ostream& os, int indent) const = 0;
        };
    
        struct Constant : Expression {
            Constant(const std::string& value) : value(value) {}
    
            void print(std::ostream& os, int indent) const override {
                os << std::string(indent, ' ') << "Constant: " << value << std::endl;
            }
    
            std::string value;
        };
    
        struct FormulaNode {
            FormulaNode(Expression* expression) : expression(expression) {}
    
            void print(std::ostream& os, int indent) const { expression->print(os, indent); }
    
            std::unique_ptr<Expression> expression;
        };
    }
    
    namespace qi      = boost::spirit::qi;
    namespace phoenix = boost::phoenix;
    
    using FormulaNodePtr = compiler::FormulaNode*;
    using ExpressionPtr  = compiler::Expression*;
    
    template <typename Iterator> //
    struct ArithmeticGrammar : qi::grammar<Iterator, FormulaNodePtr()> {
        ArithmeticGrammar() : ArithmeticGrammar::base_type(start) {
            using namespace qi::labels;
    
            start          = qi::skip(localSkipper.alias())[formula];
            formula        = unsignedFactor[_val = phoenix::new_<compiler::FormulaNode>(_1)];
            unsignedFactor = numericConstant[_val = phoenix::new_<compiler::Constant>(_1)];
    
            numericConstant = qi::as_string[
                  -qi::char_('-') >>
                    (+qi::digit >> -(qi::char_('.') >> +qi::digit)) >>
                    -(qi::char_('e') >> -qi::char_('-') >> +qi::digit) 
                ];
        }
    
      private:
        using Skipper = qi::rule<Iterator>;
        qi::rule<Iterator, FormulaNodePtr()>          start;
        qi::rule<Iterator, FormulaNodePtr(), Skipper> formula;
        qi::rule<Iterator, ExpressionPtr(), Skipper>  unsignedFactor;
    
        // lexemes
        qi::rule<Iterator, std::string()> numericConstant;
        Skipper localSkipper = qi::space | "/*" >> *(qi::char_ - "*/") >> "*/";
    };
    
    // Parsing function
    bool parseExpression(std::string const& input, FormulaNodePtr& result) {
        using Iterator = std::string::const_iterator;
        static ArithmeticGrammar<Iterator> const grammar;
    
        if (!parse(begin(input), end(input), grammar >> qi::eoi, result)) {
            delete result;
            result = nullptr;
            return false;
        }
    
        return true;
    }
    
    int main() {
        std::cout << "Enter input: ";
        for (std::string input; getline(std::cin, input); std::cout<<"Enter input: ") {
            if (input.empty())
                break;
    
            FormulaNodePtr result = nullptr;
            if (parseExpression(input, result)) {
                std::cout << "Parsed successfully. Expression tree:" << std::endl;
                result->print(std::cout, 0);
                delete result;
            } else {
                std::cout << "Parsing failed. Please enter a valid expression." << std::endl;
            }
        }
    }
    

    Printing e.g.

    enter image description here