Home > Enterprise >  Boost Spirit x3 - parser doesn't recognize end of line
Boost Spirit x3 - parser doesn't recognize end of line

Time:05-30

I am trying to parse an .obj file, but i can't figure out how to make x3 stop at the end of a line.

My code looks like this:

#include <iostream>


#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/support/iterators/istream_iterator.hpp>
#include <boost/spirit/include/support_multi_pass.hpp>
#include <fstream>
#include <sstream>

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

  

namespace objParser
{
    template <typename Iterator>
    bool parse_obj(Iterator first, Iterator last)
    {
        using boost::spirit::x3::char_;
        using boost::spirit::x3::eol;
        using boost::spirit::x3::eps;
        using boost::spirit::x3::_attr;
        using boost::spirit::x3::phrase_parse;

        auto printText = [&](auto& ctx) { std::cout << _attr(ctx) << std::endl;                 };

        bool result = phrase_parse(first, last,

            //  Begin grammar
            (
                *( char_('#') >> *(~char_('\r\n'))[printText] >> eol)
            ),
            //  End grammar

            eps(false));


        if (!result || first != last) // fail if we did not get a full match
            return false;
        return result;
    }
}

int main()
{
    std::string modelPath = "file.obj";
    
    std::ifstream inFile(modelPath, std::ifstream::in);
    if (inFile.good()) {
        std::cout << "input found" << std::endl;
    }
   
    typedef std::istreambuf_iterator<char> base_iterator_type;
    typedef boost::spirit::multi_pass<base_iterator_type> forward_iterator_type;
    base_iterator_type in_begin(inFile);
    base_iterator_type in_end;
    forward_iterator_type fwd_begin = boost::spirit::make_default_multi_pass(in_begin);
    forward_iterator_type fwd_end = boost::spirit::make_default_multi_pass(in_end);

    objParser::parse_obj(fwd_begin, fwd_end);

    return 0;
}

The grammar is supposed to take the comment preceded by an "#" and print everything until the end of line. Instead the parser prints everything until the end of the file. Also how can I pack the *(~char_('\r\n'))[printText] so, that it takes the line content as a string? currently every char is printed separately.

CodePudding user response:

Like the commenter said. This is why you enable warnings:

<source>:33:43: warning: multi-character character constant [-Wmultichar]
   33 |                 *( char_('#') >> *(~char_('\r\n'))[printText] >> eol)
      |                                           ^~~~~~

Next up, simplify-time: https://compiler-explorer.com/z/hdWhsndMe

return parse(first, last,                                          //
             *('#' >> *(~x3::char_("\r\n"))[printText] >> x3::eol) //
                 >> x3::eoi                                        //
);

Using the eoi directive instead, and not using phrase_parse if the intent is not to skip.

Also how can I pack the *(~char_('\r\n'))[printText] so, that it takes the line content as a string? currently every char is printed separately.

Just say so: https://compiler-explorer.com/z/GaPMdzoGr

return parse(first, last,                                          //
             *('#' >> (*~x3::char_("\r\n"))[printText] >> x3::eol) //
                 >> x3::eoi);

Demo

#include <boost/spirit/home/support/iterators/istream_iterator.hpp>
#include <boost/spirit/home/x3.hpp>
#include <sstream>
#include <iostream>

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

namespace objParser {
    template <typename Iterator> bool parse_obj(Iterator first, Iterator last) {
        auto printText = [&](auto& ctx) {
            std::cout << _attr(ctx) << std::endl;
        };

        return parse(first, last,                                          //
                    *('#' >> (*~x3::char_("\r\n"))[printText] >> x3::eol) //
                        >> x3::eoi);
    }
} // namespace objParser

int main() {
    std::istringstream inFile(R"(# this is presumably a comment
# and so is this
    )");

    boost::spirit::istream_iterator b(inFile >> std::noskipws), e;
    objParser::parse_obj(b, e);
}

Prints

 this is presumably a comment
 and so is this
  • Related