c++boostboost-spirit-x3

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


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.


Solution

  • 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