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