I'm concentrating on checking for error conditions in an parser design using Spirit X3. One of which is the character category checks like isalpha or ispunct. According to the X3 documentation Character Parsers they should match what C++ provides as std::isalpha and std::ispunct. However with a code demonstration shown below I do get different results.
#include <cstddef>
#include <cstdio>
#include <cstdint>
#include <cctype>
#include <iostream>
#include <boost/spirit/home/x3/version.hpp>
#include <boost/spirit/home/x3.hpp>
namespace client::parser
{
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
using ascii::char_;
using ascii::space;
using x3::skip;
x3::rule<class main_rule_id, char> const main_rule_ = "main_rule";
const auto main_rule__def = ascii::cntrl;
BOOST_SPIRIT_DEFINE( main_rule_ )
const auto entry_point = skip(space) [ main_rule_ ];
}
int main()
{
printf( "Spirit X3 version: %4.4x\n", SPIRIT_X3_VERSION );
char output;
bool r = false;
bool r2 = false; // answer according to default "C" locale
char input[2];
input[1] = 0;
printf( "ascii::cntrl\n" );
uint8_t i = 0;
next_char:
input[0] = (char)i;
r = parse( (char*)input, input+1, client::parser::entry_point, output );
r2 = (bool)std::iscntrl( (unsigned char)i );
printf( "%2.2x:%d%d", i, r, r2 );
if ( i == 0x7f ) { goto exit_loop; }
++i;
if ( i % 8 ) { putchar( ' ' ); } else { putchar( '\n' ); }
goto next_char;
exit_loop:
return 0;
}
The output is:
Spirit X3 version: 3004
ascii::cntrl
00:11 01:11 02:11 03:11 04:11 05:11 06:11 07:11
08:11 09:01 0a:01 0b:01 0c:01 0d:01 0e:11 0f:11
10:11 11:11 12:11 13:11 14:11 15:11 16:11 17:11
18:11 19:11 1a:11 1b:11 1c:11 1d:11 1e:11 1f:11
20:00 21:00 22:00 23:00 24:00 25:00 26:00 27:00
28:00 29:00 2a:00 2b:00 2c:00 2d:00 2e:00 2f:00
30:00 31:00 32:00 33:00 34:00 35:00 36:00 37:00
38:00 39:00 3a:00 3b:00 3c:00 3d:00 3e:00 3f:00
40:00 41:00 42:00 43:00 44:00 45:00 46:00 47:00
48:00 49:00 4a:00 4b:00 4c:00 4d:00 4e:00 4f:00
50:00 51:00 52:00 53:00 54:00 55:00 56:00 57:00
58:00 59:00 5a:00 5b:00 5c:00 5d:00 5e:00 5f:00
60:00 61:00 62:00 63:00 64:00 65:00 66:00 67:00
68:00 69:00 6a:00 6b:00 6c:00 6d:00 6e:00 6f:00
70:00 71:00 72:00 73:00 74:00 75:00 76:00 77:00
78:00 79:00 7a:00 7b:00 7c:00 7d:00 7e:00 7f:11
So the first bit after the colon is the answer according to X3 and the second bit is the answer according to C++. The mismatch happens on the characters that also fall into the category isspace. Recently I'm more looking into the library headers, but I still haven't found a part that explains this behavior.
Why the disparity? Do I have missed something?
Oh yeah, I love my goto statements. And my retro C style. I hope you do too! Even for an X3 parser.
You accidentally run amok with the skipper which eats any whitespace before you can actually parse it.
I simplified the parser and now it succeeds:
As a note about style: there's no reason ever to
use C style casts (they're dangerous)
write a loop with goto (considered harmful)
use cryptic variable names (r, r2?)
Live On Coliru
#include <boost/spirit/home/x3/version.hpp>
#include <boost/spirit/home/x3.hpp>
#include <cctype>
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <iomanip>
namespace client::parser {
using namespace boost::spirit::x3;
//const auto entry_point = skip(space)[ ascii::cntrl ];
const auto entry_point = ascii::cntrl;
}
int main() {
std::cout << std::boolalpha << std::hex << std::setfill('0');
std::cout << "Spirit X3 version: " << SPIRIT_X3_VERSION << "\n";
for (uint8_t i = 0; i <= 0x7f; ++i) {
auto from_x3 = parse(&i, &i + 1, client::parser::entry_point);
auto from_std = !!std::iscntrl(i);
if (from_x3 != from_std) {
std::cout << "0x" << std::setw(2) << static_cast<unsigned>(i) << "\tx3:" << from_x3 << "\tstd:" << from_std << '\n';
}
}
std::cout << "Done\n";
}
Prints simply
Spirit X3 version: 3000
Done
With the "bad line" commented in instead:
Live On Coliru
Spirit X3 version: 3000
0x09 x3:false std:true
0x0a x3:false std:true
0x0b x3:false std:true
0x0c x3:false std:true
0x0d x3:false std:true
Done
Related
I'm new to programming so I'm sorry if my question is hard to understand.
I have a string modelAnswer as such
string modelAnswer = "ABABACDA";
So it's supposed to be the answers to a quiz and I'm trying to make it so that if user's input is
string studentAnswer = "ABADBDBB"; for example the program will show that I have gotten 3 points as the first three letters of the studentAnswer string matches the modelAnswer.
You can use standard algorithm std::inner_product as for example
#include <iostream>
#include <string>
#include <numeric>
#include <functional>
int main()
{
std::string modelAnswer( "ABABACDA" );
std::string studentAnswer( "ABADBDBB" );
auto n = std::inner_product( modelAnswer.begin(), modelAnswer.end(),
studentAnswer.begin(), size_t( 0 ),
std::plus<size_t>(), std::equal_to<char>() );
std::cout << n << std::endl;
return 0;
}
The program output is
3
It is assumed that the strings have the same length. Otherwise you should use the less string as the first pair of arguments.
For example
#include <iostream>
#include <string>
#include <numeric>
#include <algorithm>
#include <functional>
#include <iterator>
int main()
{
std::string modelAnswer( "ABABACDA" );
std::string studentAnswer( "ABADBDBB" );
auto n = std::inner_product( modelAnswer.begin(),
std::next( modelAnswer.begin(), std::min( modelAnswer.size(), studentAnswer.size() ) ),
studentAnswer.begin(), size_t( 0 ),
std::plus<size_t>(), std::equal_to<char>() );
std::cout << n << std::endl;
return 0;
}
If you are using standard strings, with the proper includes (Mainly #include <string>), you can write a simple for loop to iterate over each character, comparing them.
std::string answer = "ABABACDA";
std::string stringToCompare = "ABADBDBB";
int score = 0;
for (unsigned int i = 0; (i < answer.size()) && (i < stringToCompare.size()); ++i)
{
if (answer[i] == stringToCompare[i])
{
++score;
}
}
printf("Compare string gets a score of %d.\n", score);
The above code works for me, printing the following result:
Compare string gets a score of 3.
Using a stringstream, you can push one character at a time into temporary variables and test for equivalence in a loop.
#include <iostream>
#include <string>
#include <sstream>
int main() {
std::istringstream model("ABABACDA");
std::istringstream student("ABADBDBB");
int diff = 0;
char m, s;
while ((model >> m) && (student >> s))
if (m != s) diff++;
std::cout << diff << std::endl; // 5
return 0;
}
I am working on learning to write spirit grammars and I am trying to create a basic base 16 to base 64 converter that takes in a string representing hex, for example:
49276d206b696c
parse out 6 or less characters (less if the string isn't a perfect multiple of 6) and generate a base 64 encoded string from the input. One grammar I figured would probably work is something like this:
// 6 characters
`(qi::char_("0-9a-fA-F") >> qi::char_("0-9a-fA-F") >>
qi::char_("0-9a-fA-F") >> qi::char_("0-9a-fA-F") >>
qi::char_("0-9a-fA-F") >> qi::char_("0-9a-fA-F")[/*action*/]) |
// or 5 characters
(qi::char_("0-9a-fA-F") >> qi::char_("0-9a-fA-F") >>
qi::char_("0-9a-fA-F") >> qi::char_("0-9a-fA-F") >>
qi::char_("0-9a-fA-F")[/*action*/]) | ...`
etc.... all the way down to one character, Or having a different rule defined for each number of characters, but I think there must be a better way to specify the grammar. I read about spirit repeat and was thinking maybe I could do something like
+(boost::spirit::repeat(1, 6)[qi::char_("0-9a-fA-F")][/*action on characters*/])
however the compiler throws an error on this, because of the sematic action portion of the grammar. Is there a simpler way to specify a grammar to operate on exactly 6 or less characters at a time?
Edit
Here is what I have done so far...
base16convertergrammar.hpp
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <iostream>
namespace grammar {
namespace qi = boost::spirit::qi;
void toBase64(const std::string& p_input, std::string& p_output)
{
if (p_input.length() < 6)
{
// pad length
}
// use back inserter and generator to append to end of p_output.
}
template <typename Iterator>
struct Base16Grammar : qi::grammar<Iterator, std::string()>
{
Base16Grammar() : Base16Grammar::base_type(start, "base16grammar"),
m_base64String()
{
// get six characters at a time and send them off to be encoded
// if there is less than six characters just parse what we have
start = +(boost::spirit::repeat(1, 6)[qi::char_("0-9a-fA-F")][boost::phoenix::bind(toBase64, qi::_1,
boost::phoenix::ref(m_base64String))]);
}
qi::rule<Iterator, std::string()> start;
std::string m_base64String;
};
}
And here is the usage...
base16converter.cpp
#include "base16convertergrammar.hpp"
const std::string& convertHexToBase64(const std::string& p_hexString)
{
grammar::Base16Grammar<std::string::const_iterator> g;
bool r = boost::spirit::qi::parse(p_hexString.begin(), p_hexString.end(), g);
}
int main(int argc, char** argv)
{
std::string test("49276d206b696c6c");
convertHexToBase64(test);
}
First of all, repeat()[] exposes a vector, so vector<char>, not a string.
void toBase64(const std::vector<char>& p_input, std::string& p_output)
Secondly, please don't do all that work. You don't tell us what the input means, but as long as you want to group it in sixes, I'm assuming you want them interpreted as /something/. You could e.g. use the int_parser:
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <iostream>
namespace grammar {
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
template <typename Iterator>
struct Base16Grammar : qi::grammar<Iterator, std::string()>
{
Base16Grammar() : Base16Grammar::base_type(start, "base16grammar")
{
start = +qi::int_parser<uint64_t, 16, 1, 6>() [ qi::_val += to_string(qi::_1) + "; " ];
}
private:
struct to_string_f { template <typename T> std::string operator()(T const& v) const { return std::to_string(v); } };
px::function<to_string_f> to_string;
qi::rule<Iterator, std::string()> start;
};
}
std::string convertHexToBase64(const std::string& p_hexString)
{
grammar::Base16Grammar<std::string::const_iterator> g;
std::string result;
bool r = boost::spirit::qi::parse(p_hexString.begin(), p_hexString.end(), g, result);
assert(r);
return result;
}
int main()
{
for (std::string test : {"49276d206b696c6c"})
std::cout << test << " -> " << convertHexToBase64(test) << "\n";
}
Prints
49276d206b696c6c -> 4794221; 2124649; 27756;
Going out on a limb, you just want to transcode hex-encoded binary into base64.
Since you're already using Boost:
Live On Coliru
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/insert_linebreaks.hpp>
#include <boost/archive/iterators/transform_width.hpp>
// for hex decoding
#include <boost/iterator/function_input_iterator.hpp>
#include <string>
#include <iostream>
#include <functional>
std::string convertHexToBase64(const std::string &hex) {
struct get_byte_f {
using result_type = uint8_t;
std::string::const_iterator hex_it;
result_type operator()() {
auto nibble = [](uint8_t ch) {
if (!std::isxdigit(ch)) throw std::runtime_error("invalid hex input");
return std::isdigit(ch) ? ch - '0' : std::tolower(ch) - 'a' + 10;
};
auto hi = nibble(*hex_it++);
auto lo = nibble(*hex_it++);
return hi << 4 | lo;
}
} get_byte{ hex.begin() };
using namespace boost::archive::iterators;
using It = boost::iterators::function_input_iterator<get_byte_f, size_t>;
typedef insert_linebreaks< // insert line breaks every 72 characters
base64_from_binary< // convert binary values to base64 characters
transform_width< // retrieve 6 bit integers from a sequence of 8 bit bytes
It, 6, 8> >,
72> B64; // compose all the above operations in to a new iterator
return { B64(It{get_byte, 0}), B64(It{get_byte, hex.size()/2}) };
}
int main() {
for (std::string test : {
"49276d206b696c6c",
"736f6d65206c656e67746879207465787420746f2073686f77207768617420776f756c642068617070656e206174206c696e6520777261700a"
})
{
std::cout << " === hex: " << test << "\n" << convertHexToBase64(test) << "\n";
}
}
Prints
=== hex: 49276d206b696c6c
SSdtIGtpbGw
=== hex: 736f6d65206c656e67746879207465787420746f2073686f77207768617420776f756c642068617070656e206174206c696e6520777261700a
c29tZSBsZW5ndGh5IHRleHQgdG8gc2hvdyB3aGF0IHdvdWxkIGhhcHBlbiBhdCBsaW5lIHdy
YXAK
I am currently starting with boost::spirit::*. I try to parse a 128 bit string into a simple c array with corresponding size. I created a short test which does the job:
boost::spirit::qi::int_parser< boost::uint8_t, 16, 2, 2 > uint8_hex;
std::string src( "00112233445566778899aabbccddeeff" );
boost::uint8_t dst[ 16 ];
bool r;
for( std::size_t i = 0; i < 16; ++i )
{
r = boost::spirit::qi::parse( src.begin( ) + 2 * i, src.begin( ) + 2 * i + 2, uint8_hex, dst[ i ] );
}
I have the feeling that this is not the smartest way to do it :) Any ideas how to define a rule so I can avoid the loop ?
Update:
In the meantime I figured out the following code which does the job very well:
using namespace boost::spirit;
using namespace boost::phoenix;
qi::int_parser< boost::uint8_t, 16, 2, 2 > uint8_hex;
std::string src( "00112233445566778899aabbccddeeff" );
boost::uint8_t dst[ 16 ];
std::size_t i = 0;
bool r = qi::parse( src.begin( ),
src.end( ),
qi::repeat( 16 )[ uint8_hex[ ref( dst )[ ref( i )++ ] = qi::_1 ] ] );
Not literally staying with the question, if you really wanted just to parse the hexadecimal representation of a 128 bit integer, you can do so portably by using uint128_t defined in Boost Multiprecision:
qi::int_parser<uint128_t, 16, 16, 16> uint128_hex;
uint128_t parsed;
bool r = qi::parse(f, l, uint128_hex, parsed);
This is bound to be the quickest way especially on platforms where 128bit types are supported in the instruction set.
Live On Coliru
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main() {
using boost::multiprecision::uint128_t;
using It = std::string::const_iterator;
qi::int_parser<uint128_t, 16, 16, 16> uint128_hex;
std::string const src("00112233445566778899aabbccddeeff");
auto f(src.begin()), l(src.end());
uint128_t parsed;
bool r = qi::parse(f, l, uint128_hex, parsed);
if (r) std::cout << "Parse succeeded: " << std::hex << std::showbase << parsed << "\n";
else std::cout << "Parse failed at '" << std::string(f,l) << "'\n";
}
There's a sad combination of factors that lead to this being a painful edge case
Boost Fusion can adapt (boost::)array<> but it it requires the parser to result in a tuple of elements, not a container
Boost Fusion can adapt these sequences, but need to be configure to allow 16 elements:
#define FUSION_MAX_VECTOR_SIZE 16
Even when you do, the qi::repeat(n)[] parser directive expects the attribute to be a container type.
You might work around all this in an ugly way (e.g. Live On Coliru). This makes everything hard to work with down the road.
I'd prefer a tiny semantic action here to make the result being assigned from qi::repeat(n)[]:
using data_t = boost::array<uint8_t, 16>;
data_t dst {};
qi::rule<It, data_t(), qi::locals<data_t::iterator> > rule =
qi::eps [ qi::_a = phx::begin(qi::_val) ]
>> qi::repeat(16) [
uint8_hex [ *qi::_a++ = qi::_1 ]
];
This works without too much noise. The idea is to take the start iterator and write to the next element each iteraton.
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
int main() {
using It = std::string::const_iterator;
qi::int_parser<uint8_t, 16, 2, 2> uint8_hex;
std::string const src("00112233445566778899aabbccddeeff");
auto f(src.begin()), l(src.end());
using data_t = boost::array<uint8_t, 16>;
data_t dst {};
qi::rule<It, data_t(), qi::locals<data_t::iterator> > rule =
qi::eps [ qi::_a = phx::begin(qi::_val) ]
>> qi::repeat(16) [
uint8_hex [ *qi::_a++ = qi::_1 ]
];
bool r = qi::parse(f, l, rule, dst);
if (r) {
std::cout << "Parse succeeded\n";
for(unsigned i : dst) std::cout << std::hex << std::showbase << i << " ";
std::cout << "\n";
} else {
std::cout << "Parse failed at '" << std::string(f,l) << "'\n";
}
}
How could I trace the position of the attribute of spirit?
A simple example
template <typename Iterator>
bool trace_numbers(Iterator first, Iterator last)
{
using boost::spirit::qi::double_;
using boost::spirit::qi::phrase_parse;
using boost::spirit::ascii::space;
bool r = phrase_parse(first, last,
// Begin grammar
(
double_ % ','
)
,
// End grammar
space);
if (first != last) // fail if we did not get a full match
return false;
return r;
}
I want to trace the position(line and column) of "double_", I found line_pos_iterator but have no idea how to use it.I also found multi-pass, but don't know it could be used to trace the positions or not(if it can, how?).
After some research, I found that using spirit::lex alone or combine it with spirit::qi is a solution.
#include <boost/config/warning_disable.hpp>
//[wcp_includes
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/spirit/include/phoenix_container.hpp>
//]
#include <iostream>
#include <string>
#include <vector>
namespace spiritParser
{
//[wcp_namespaces
using namespace boost::spirit;
using namespace boost::spirit::ascii;
//[wcp_token_ids
enum tokenids
{
IDANY = lex::min_token_id + 10
};
//]
//[wcp_token_definition
template <typename Lexer>
struct number_position_track_tokens : lex::lexer<Lexer>
{
number_position_track_tokens()
{
// define patterns (lexer macros) to be used during token definition
// below
this->self.add_pattern
("NUM", "[0-9]+")
;
number = "{NUM}"; // reference the pattern 'NUM' as defined above
this->self.add
(number) // no token id is needed here
(".", IDANY) // characters are usable as tokens as well
;
}
lex::token_def<std::string> number;
};
//]
template<typename Iterator>
struct numberGrammar : qi::grammar<Iterator>
{
template <typename TokenDef>
numberGrammar(TokenDef const &tok)
: numberGrammar::base_type(start)
, num(0), position(0)
{
using boost::phoenix::ref;
using boost::phoenix::push_back;
using boost::phoenix::size;
//"34, 44, 55, 66, 77, 88"
start = *( tok.number [++ref(num),
boost::phoenix::push_back(boost::phoenix::ref(numPosition), boost::phoenix::ref(position)),
ref(position) += size(_1)
]
| qi::token(IDANY) [++ref(position)]
)
;
}
std::size_t num, position;
std::vector<size_t> numPosition;
qi::rule<Iterator> start;
};
void lex_word_count_1()
{
using token_type = lex::lexertl::token<char const*, boost::mpl::vector<std::string> >;
number_position_track_tokens<lexer_type> word_count; // Our lexer
numberGrammar<iterator_type> g (word_count); // Our parser
// read in the file int memory
std::string str ("34, 44, 55, 66, 77, 88");
char const* first = str.c_str();
char const* last = &first[str.size()];
if (r) {
std::cout << "nums: " << g.num << ", size: " << g.position <<std::endl;
for(auto data : g.numPosition){
std::cout<<"position : "<<data<<std::endl;
}
}
else {
std::string rest(first, last);
std::cerr << "Parsing failed\n" << "stopped at: \""
<< rest << "\"\n";
}
}
}
This is the example from the document Quickstart 3 - Counting Words Using a Parser with some alternation.In my humble opinion, this is far from easy for a small task like this. If the patterns are not difficult for std::regex to descript; need faster speed or both, select spirit::lex to track the locations of simple pattern(like the example I show) is overkill.
How I can add warnings in boost spirit parser.
Edit: ... that could report the issue with position
For example if I have an integer parser:
('0' >> oct)
| int_
I would like to be able to do something like this:
('0' >> oct)
| "-0" --> trigger warning("negative octal values are not supported, it will be interpreted as negative decimal value and the leading 0 will be ignored")
| int_
Q. Can I create my own callback? How?
A. Sure. Any way you'd normally do it in C++ (or look at Boost Signal2 and/or Boost Log)
parser(std::function<bool(std::string const& s)> callback)
: parser::base_type(start),
callback(callback)
{
using namespace qi;
start %=
as_string[+graph]
[ _pass = phx::bind(callback, _1) ]
% +space
;
BOOST_SPIRIT_DEBUG_NODES((start));
}
As you can see, you can even make the handler decide whether the warning should be ignored or cause the match to fail.
UPDATE #1 I've extended the sample to show some of the unrelated challenges you mentioned in the comments (position, duplicate checking). Hope this helps
Here's a simple demonstration: see it Live on Coliru (Word)
UPDATE #2 I've even made it (a) store the source information instead of the iterators, (b) made it "work" with floats (or any other exposed attribute type, really).
Note how uncannily similar it is, s/Word/Number/, basically: Live On Coliru (Number)
#define BOOST_RESULT_OF_USE_DECLTYPE // needed for gcc 4.7, not clang++
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <functional>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
// okay, so you want position reporting (actually unrelated):
#include <boost/spirit/include/support_line_pos_iterator.hpp>
using It = boost::spirit::line_pos_iterator<std::string::const_iterator>;
// AST type that represents a Number 'token' (with source and location
// information)
struct Number
{
double value;
size_t line_pos;
std::string source;
explicit Number(double value = 0.0, boost::iterator_range<It> const& range = {})
:
value(value),
line_pos(get_line(range.begin())),
source(range.begin(), range.end())
{}
bool operator< (const Number& other) const { return (other.value - value) > 0.0001; }
};
// the exposed attribute for the parser:
using Words = std::set<Number>;
// the callback signature for our warning; you could make it more like
// `on_error` so it takes the iterators directly, but again, I'm doing the
// simple thing for the dmeo
using Callback = std::function<bool(Number const& s)>;
template <typename It>
struct parser : qi::grammar<It, Words()>
{
parser(Callback warning)
: parser::base_type(start),
warning(warning)
{
using namespace qi;
auto check_unique = phx::end(_val) == phx::find(_val, _1);
word =
raw [ double_ [ _a = _1 ] ] [ _val = phx::construct<Number>(_a, _1) ]
;
start %=
- word [ _pass = check_unique || phx::bind(warning, _1) ]
% +space
>> eoi
;
}
private:
Callback warning;
qi::rule<It, Number(), qi::locals<double> > word;
qi::rule<It, Words()> start;
};
int main(int argc, const char *argv[])
{
// parse command line arguments
const auto flags = std::set<std::string> { argv+1, argv+argc };
const bool fatal_warnings = end(flags) != flags.find("-Werror");
// test input
const std::string input("2.4 2.7 \n\n\n-inf \n\nNaN 88 -2.40001 \n3.14 240001e-5\n\ninf");
// warning handler
auto warning_handler = [&](Number const& w) {
std::cerr << (fatal_warnings?"Error":"Warning")
<< ": Near-identical entry '" << w.source << "' at L:" << w.line_pos << "\n";
return !fatal_warnings;
};
// do the parse
It f(begin(input)), l(end(input));
bool ok = qi::parse(f, l, parser<It>(warning_handler));
// report results
if (ok) std::cout << "parse success\n";
else std::cerr << "parse failed\n";
if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
// exit code
return ok? 0 : 255;
}
Prints:
Warning: Near-identical entry 'NaN' at L:6
Warning: Near-identical entry '240001e-5' at L:7
parse success