1

I tried to run some simple parser that will parse [ 1, 11, 3, 6-4]. Basically, integer list with range notation.

I want to put everything into AST without semantic action. So I use x3::variant. My code 'seems' very similar to the expression example. However, it can't compile under g++ 6.2. It indeed compile ok with clang++ 6.0 but yield wrong result.

The boost version is 1.63. It seems that I have some 'move' or initialization issue.

#include <iostream>
#include <list>
#include <vector>
#include <utility> 

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/include/io.hpp>

namespace ns
{
  namespace ast
  {
    namespace x3 = boost::spirit::x3;

    // forward definition
    class uintObj;
    struct varVec;

    // define type
    using uintPair_t = std::pair<unsigned int, unsigned int>;
    using uintVec_t = std::vector<uintObj>;

    // general token value:
    class uintObj : public x3::variant <
      unsigned int,
      uintPair_t
      >
    {
     public:
      using base_type::base_type;
      using base_type::operator=;
    };

    struct varVec
    {
      uintVec_t valVector;
    };
  }
}

BOOST_FUSION_ADAPT_STRUCT(
  ns::ast::varVec,
  valVector
  )

namespace ns
{
  namespace parser
  {
    // namespace x3 = boost::spirit::x3;
    // using namespace x3;
    using namespace boost::spirit::x3;

    // definition of the range pair:
    rule<class uintPair, ast::uintPair_t> const uintPair = "uintPair";

    auto const uintPair_def =
      uint_
      >> '-'
      >> uint_
      ;

    rule<class uintObj, ast::uintObj> const uintObj = "uintObj";

    auto const uintObj_def =
      uint_
      | uintPair
      ;

    // define rule definition : rule<ID, attrib>
    // more terse definition :
    // struct varVec_class;
    // using varVec_rule_t = x3::rule<varVec_class, ast::varVec>;
    // varVec_rule_t const varVec = "varVec";
    // varVec is the rule, "varVec" is the string name of the rule.
    rule<class varVec, ast::varVec> const varVec = "varVec";

    auto const varVec_def =
      '['
      >> uintObj % ','
      >> ']'
      ;

    BOOST_SPIRIT_DEFINE(
      varVec,
      uintObj,
      uintPair
      );
  }
}

int main()
{
  std::string input ("[1, 11, 3, 6-4]\n");
  std::string::const_iterator begin = input.begin();
  std::string::const_iterator end = input.end();

  ns::ast::varVec result;                 // ast tree
  using ns::parser::varVec;               // grammar
  using boost::spirit::x3::ascii::space;

  bool success = phrase_parse(begin, end, varVec, space, result);

  if (success && begin == end)
    std::cout << "good" << std::endl;
  else
    std::cout << "bad" << std::endl;

  return 0;
}
sehe
  • 374,641
  • 47
  • 450
  • 633
Sink
  • 57
  • 3
  • I know there are other questions similar to mine. My question is not that there is another way to do it. I just feel a simple AST without semantic action seems more straitforward. https://stackoverflow.com/questions/34599506/parsing-comma-separated-list-of-ranges-and-numbers-with-semantic-actions – Sink Nov 26 '18 at 21:43
  • After swap the uintPair and uint_, the clang++ compiled result is good. However, another issue is that my g++ v6.2 can't compile still. the command is like g++ -I/install/boost_1_63_0/include -std=c++14 -o par21.o -c par21.cpp. Not just my version. Sehe's version can't be compiled by my g++ either. – Sink Nov 27 '18 at 17:54
  • See comments over at my answer – sehe Nov 27 '18 at 17:57

2 Answers2

4

Swap the alternative order for the uintObj_def

auto const uintObj_def =
    uintPair
  | uint_
  ;

The formulation you have now will always match on a uint_ because the uintPair begins with a valid uint_.

mjcaisse
  • 221
  • 1
  • 4
1

mjcaisse's answer calls out the main problem I think you had. There were a few missing pieces, so I decided to make a simplified version that shows parsing results:

Live On Wandbox

#include <iostream>
#include <iomanip>

//#include <boost/fusion/adapted.hpp>
//#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>

namespace x3 = boost::spirit::x3;

namespace ns { namespace ast {

    // forward definition
    struct uintObj;
    //struct varVec;

    // define type
    using uintPair_t = std::pair<unsigned int, unsigned int>;
    using uintVec_t = std::vector<uintObj>;

    // general token value:
    struct uintObj : x3::variant<unsigned int, uintPair_t> {
        using base_type::base_type;
        using base_type::operator=;

        friend std::ostream& operator<<(std::ostream& os, uintObj const& This) {
            struct {
                std::ostream& os;
                void operator()(unsigned int v) const { os << v; }
                void operator()(uintPair_t v) const { os << v.first << "-" << v.second; }
            } vis { os };
            boost::apply_visitor(vis, This);
            return os;
        }
    };

    using varVec = uintVec_t;
} }

namespace ns { namespace parser {
    using namespace boost::spirit::x3;

    template <typename T> auto as = [](auto p) { return rule<struct _, T> {} = p; };

    auto const uintPair = as<ast::uintPair_t> ( uint_ >> '-' >> uint_       );
    auto const uintObj  = as<ast::uintObj>    ( uintPair | uint_            );
    auto const varVec   = as<ast::varVec>     ( '[' >> uintObj % ',' >> ']' );
} }

int main() {
    using namespace ns;
    std::string const input("[1, 11, 3, 6-4]\n");
    auto begin = input.begin(), end = input.end();

    ast::varVec result; // ast tree
    bool success = phrase_parse(begin, end, parser::varVec, x3::ascii::space, result);

    if (success) {
        std::cout << "good\n";
        for (auto& r : result) 
            std::cout << r << "\n";
    }
    else
        std::cout << "bad\n";

    if (begin != end)
        std::cout << "Remaining unparsed: " << std::quoted(std::string(begin, end)) << std::endl;
}

Prints

good
1
11
3
6-4
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Ok, the code indeed look way more simplified and pretty. I am wondering that both youtube and boost website x3 tutorial use similar approach that have something similar to "expr_class", "expr_def", "expr" and BOOST_SPIRIT_DEFINE(). What's the reason to do that if we can have 'easier' way like the code above. – Sink Nov 27 '18 at 05:56
  • About defining rules with `BOOST_SPIRIT_DEFINE`: that's only required for 1. recursive rules 2. external linkage. (Perhaps some of my [answers](https://stackoverflow.com/search?q=user%3A85371+BOOST_SPIRIT_DEFINE) will shed some light) – sehe Nov 27 '18 at 17:53
  • 1
    Regarding the compiler problem, GCC 6.3.0 apparently has trouble generating the special members for `uintObj`. Adding them fixes it: [Live On Wandbox](https://wandbox.org/permlink/xTJRjm41f0hoi8hR) – sehe Nov 27 '18 at 17:57