How to round a number during QI parsing with boost? - c++

With a simple Boost qi grammar, how can I make it rounding my number?
This is the parser:
factor =
float_ [_val = _1]
| ('-' >> factor [_val = -_1])
| ('+' >> factor [_val = _1])
;
This can parse a float and it can be also negative.
I want to round the float, so I would add something like this to the grammar:
| ('~' >> factor [_val = round(_1)])
But this results a compile-time error:
no type named ‘__type’ in ‘struct __gnu_cxx::__enable_if<false, double>’
This error is not too informative for me, can you please help? I want to be able to round a number, ie:
~1.8 -> 2
~1.2 -> 1
Note: I'm parsing with phrase_parse.

Semantic actions require Phoenix Actors, which are deferred functions.
Options:
Adaptation macros https://www.boost.org/doc/libs/1_73_0/libs/phoenix/doc/html/phoenix/modules/function/adapting_functions.html
phoenix::function<>
phoenix::bind
write your own, see for more details https://www.boost.org/doc/libs/1_68_0/libs/spirit/doc/html/spirit/qi/tutorials/semantic_actions.html#spirit.qi.tutorials.semantic_actions.examples_of_semantic_actions
Simplified Test Bed
Just parsing a number:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
int main() {
std::string s = "1.75";
double v;
if (qi::parse(begin(s), end(s), qi::double_, v)) {
std::cout << "Parsed: " << v << "\n";
}
}
Prints Live On Coliru:
Parsed: 1.75
Adaptation
Using the macros:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <cmath>
namespace qi = boost::spirit::qi;
int main() {
std::string s = "1.75";
double v;
if (qi::parse(begin(s), end(s), qi::double_, v)) {
std::cout << "Parsed: " << v << "\n";
std::cout << "Rounded: " << round(v) << "\n";
}
}
Prints Live On Coliru:
Parsed: 2
function<>
You can get away with hardcoding a signature here:
boost::phoenix::function<double(*)(double)> round_(::round);
However the real power comes with polymorphic calleables:
struct round_f {
template <typename T> auto operator()(T const& v) const {
using std::round; // activate ADL
return round(v);
}
};
boost::phoenix::function<round_f> round_{};
Now you can use the round_ actor on any type that has a free-function overload round overload that is compatible. Handy if tomorrow you decide to parse long double, float or boost::multiprecision::cpp_dec_float.
See it Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <cmath>
namespace qi = boost::spirit::qi;
struct round_f {
template <typename T> auto operator()(T const& v) const {
using std::round; // activate ADL
return round(v);
}
};
boost::phoenix::function<round_f> round_{};
int main() {
std::string s = "1.75";
double v;
using namespace qi::labels;
if (qi::parse(begin(s), end(s), qi::double_ [ _val = round_(_1) ], v)) {
std::cout << "Parsed: " << v << "\n";
}
}
Prints
Parsed: 2
Using phoenix::bind
As a lower-level building block, you can bind unwrapped calleables:
if (qi::parse(begin(s), end(s), qi::double_
[ _val = phoenix::bind(round_f{}, _1) ], v))
{
std::cout << "Parsed: " << v << "\n";
}
If you don't mind ugly:
if (qi::parse(begin(s), end(s), qi::double_
[ _val = phoenix::bind(static_cast<double(&)(double)>(std::round), _1) ], v))
{
std::cout << "Parsed: " << v << "\n";
}
See both Live On Coliru

Related

boost::spirit qi::uint_ valid number range

I want to parse string which consists of CC[n], where 1 <= n <= 4 or from SERVICE[k], where 1 <= k <= 63.
Valid strings: "CC1", "CC2", "CC3", "CC4", "SERVICE1", "SERVICE2", ..., "SERVICE63".
I wrote the next expression:
( '"' >> (qi::raw["CC" >> qi::uint_] | qi::raw["SERVICE" >> qi::uint_]) >> '"' >> qi::eoi)
But how I can limit n and k?
In output I need to got full string CC1, CC2, ... SERVICE63
The simplest way would be to use symbols<>.
The elaborate way is to validate the numbers in semantic actions.
My recommendation is is either symbols OR separate semantic validation from parsing (i.e. parse the numbers raw and validate the AST after the parse)
Symbols
This is likely the more flexible, most efficient, and allows you to be strongtyped in your AST domain. It sidesteps the compilation overhead and complexity of semantic actions: Boost Spirit: "Semantic actions are evil"?
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
int main() {
qi::symbols<char> cc, service;
cc += "CC1", "CC2", "CC3", "CC4";
service += "SERVICE1", "SERVICE2", "SERVICE3", "SERVICE4", "SERVICE5",
"SERVICE6", "SERVICE7", "SERVICE8", "SERVICE9", "SERVICE10",
"SERVICE11", "SERVICE12", "SERVICE13", "SERVICE14", "SERVICE15",
"SERVICE16", "SERVICE17", "SERVICE18", "SERVICE19", "SERVICE20",
"SERVICE21", "SERVICE22", "SERVICE23", "SERVICE24", "SERVICE25",
"SERVICE26", "SERVICE27", "SERVICE28", "SERVICE29", "SERVICE30",
"SERVICE31", "SERVICE32", "SERVICE33", "SERVICE34", "SERVICE35",
"SERVICE36", "SERVICE37", "SERVICE38", "SERVICE39", "SERVICE40",
"SERVICE41", "SERVICE42", "SERVICE43", "SERVICE44", "SERVICE45",
"SERVICE46", "SERVICE47", "SERVICE48", "SERVICE49", "SERVICE50",
"SERVICE51", "SERVICE52", "SERVICE53", "SERVICE54", "SERVICE55",
"SERVICE56", "SERVICE57", "SERVICE58", "SERVICE59", "SERVICE60",
"SERVICE61", "SERVICE62", "SERVICE63";
for (std::string const input : {
// valid:
"CC1",
"CC2",
"CC3",
"CC4",
"SERVICE1",
"SERVICE2",
"SERVICE63",
// invalid:
"CC0",
"CC5",
"SERVICE0",
"SERVICE64",
}) {
bool valid = parse(begin(input), end(input), service|cc);
std::cout << std::quoted(input) << " -> "
<< (valid ? "valid" : "invalid") << "\n";
}
}
Prints
"CC1" -> valid
"CC2" -> valid
"CC3" -> valid
"CC4" -> valid
"SERVICE1" -> valid
"SERVICE2" -> valid
"SERVICE63" -> valid
"CC0" -> invalid
"CC5" -> invalid
"SERVICE0" -> invalid
"SERVICE64" -> invalid
Bonus: the strongtyped idea: http://coliru.stacked-crooked.com/a/2cb07d4da9aad39e
Semantic Actions
In a nutshell:
qi::rule<It, intmax_t(intmax_t min, intmax_t max)> constrained_num =
qi::uint_[_pass = (_1 >= _r1 && _1 <= _r2)];
qi::rule<It> cc = "CC" >> constrained_num(1, 4),
service = "SERVICE" >> constrained_num(1, 63);
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
using It = std::string::const_iterator;
int main() {
using namespace qi::labels;
qi::rule<It, intmax_t(intmax_t min, intmax_t max)> constrained_num =
qi::uint_[_pass = (_1 >= _r1 && _1 <= _r2)];
qi::rule<It> cc = "CC" >> constrained_num(1, 4),
service = "SERVICE" >> constrained_num(1, 63);
for (std::string const input : {
// valid:
"CC1",
"CC2",
"CC3",
"CC4",
"SERVICE1",
"SERVICE2",
"SERVICE63",
// invalid:
"CC0",
"CC5",
"SERVICE0",
"SERVICE64",
}) {
bool valid = parse(begin(input), end(input), service|cc);
std::cout << std::quoted(input) << " -> "
<< (valid ? "valid" : "invalid") << "\n";
}
}
Prints the same as above
To limit uint_ range, you can perform a range-checking in a semantic action. It can be implemented, for example, as lambda or, more concisely, as a Boost.Phenix expression.
The following code parses these numbers into a vector (omitting the strings):
#include <iostream>
#include <string>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
int main()
{
std::string input = "CC1 CC2 CC3 CC4 SERVICE1 SERVICE2";
std::vector<unsigned int> out;
using namespace boost::spirit::qi;
phrase_parse(
input.begin(),
input.end(),
*(lexeme[lit("CC") >> uint_ [ _pass = (_1>=1 && _1<=4) ]] |
lexeme[lit("SERVICE") >> uint_ [ _pass = (_1>=1 && _1<=63) ]]),
ascii::space,
out
);
for (auto i : out)
std::cout << i << std::endl;
}

passing a spirit parsed struct to a function using phoenix::bind

I'm updating some old code which worked under older (~1.38 or so) versions of boost. I've updated to boost 1.63 and can not seem to figure out how to get the old version to compile.
This is the relevant parts
void print (StateStruct const& ss)
{
std::cout << ss.Name;
}
struct StateStruct
{
std::string Name;
float avalue;
}
BOOST_FUSION_ADAPT_STRUCT (
StateStruct,
(std::string, NAME)
(float, avalue)
)
and the parser rule that used to work
state %=
( qi::lexeme[qi::char_(a-zA-Z) >> +qi::char("-a-zA-Z0-9_")]
>> qi::float_
)
[phoenix::bind(&print, qi::_val)]
;
qi::rule (Iterator, StateStruct(), skipper_type) state;
With g++ 6.3.0 and boost 1.63 I get and error message along the lines of
invalid initialization of type const StateStruct::StateStruct& from expression of type boost::fusion::vector<boost::fusion::vector<char, std::vector<char, std::allocator<char> > >, float>
without the semantic action the code compiles and debugging the state rule shows the expected results. What do I need to do to correctly initialize the struct?
I agree this looks like a regression, you may want to report it to the maintainers at the [spirit-general] mailing list.
On a hunch I remembered an obscure define that got introduced as some kind of a compatibility hack¹. Lo and behold, the thing compiles with BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT defined:
Live On Coliru:
#define BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
struct StateStruct { std::string Name; float avalue; };
BOOST_FUSION_ADAPT_STRUCT(StateStruct, Name, avalue)
void print(StateStruct const& ss) { std::cout << "print: " << ss.Name << "\n"; }
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
int main() {
using Iterator = std::string::const_iterator;
using skipper_type = qi::space_type;
qi::rule<Iterator, StateStruct(), skipper_type> state;
state %=
( qi::as_string [ qi::lexeme[qi::char_("a-zA-Z") >> +qi::char_("-a-zA-Z0-9_")] ] >> qi::float_
)
[ px::bind(&print, qi::_val) ]
;
std::string const input("identifier31415 3.1415");
Iterator f = input.begin(), l = input.end();
StateStruct parsed;
bool ok = qi::phrase_parse(f, l, state, qi::space, parsed);
if (ok)
std::cout << "Parsed: " << boost::fusion::as_vector(parsed) << "\n";
else
std::cout << "Parse failed\n";
if (f != l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
Printing:
print: identifier31415
Parsed: (identifier31415 3.1415)
¹ (I think it got introduced to give some people a feature that could not be generally enabled because it would break existing code)

boost:spirit::qi parser using multiple grammars and phoenix::construct

I'm having trouble writing a Qi grammar which utilizes another Qi grammar. A similar question was asked here, but I'm also trying to use phoenix::construct and having compilation difficulties.
Here's a simplified version of what I'm trying to do. I realize that this example could probably be done easily using BOOST_FUSION_ADAPT_STRUCT, but my actual code deals with more complex object types so I'm hoping there's a way to accomplish this using semantic actions.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_container.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <cstdlib>
#include <iostream>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
// grammar for real numbers
template <typename Iterator>
struct Real : qi::grammar<Iterator, long double()>
{
qi::rule<Iterator, long double()> r;
Real() : Real::base_type(r)
{
r %= qi::long_double;
}
};
// grammar for complex numbers of the form a+bi
template <typename Iterator>
struct Complex : qi::grammar<Iterator, std::complex<long double>()>
{
qi::rule<Iterator, std::complex<long double>()> r;
Real<Iterator> real;
Complex() : Complex::base_type(r)
{
r = real [qi::_a = qi::_1] >> (qi::lit("+") | qi::lit("-"))
>> real [qi::_b = qi::_1] >> -qi::lit("*") >> qi::lit("i")
[
qi::_val = phx::construct<std::complex<long double> >(qi::_a, qi::_b)
];
}
};
int main()
{
// test real parsing
std::cout << "Parsing '3'" << std::endl;
std::string toParse = "3";
Real<std::string::iterator> real_parser;
long double real_val;
std::string::iterator beginIt = toParse.begin();
std::string::iterator endIt = toParse.end();
bool r = qi::parse(beginIt, endIt, real_parser, real_val);
if(r && beginIt == endIt)
std::cout << "Successful parse: " << real_val << std::endl;
else
std::cout << "Could not parse" << std::endl;
// test complex parsing
std::cout << "Parsing '3+4i'" << std::endl;
toParse = "3+4i";
Complex<std::string::iterator> complex_parser;
std::complex<long double> complex_val;
beginIt = toParse.begin();
endIt = toParse.end();
r = qi::parse(beginIt, endIt, complex_parser, complex_val);
if(r && beginIt == endIt)
std::cout << "Successful parse: " << real_val << std::endl;
else
std::cout << "Could not parse" << std::endl;
}
I'm able to parse a Complex using the phrase_parse approach demonstrated in Spirit's documentation, but I'd like to be able to easily integrate the Complex grammar into other parsers (an expression parser, for instance). Is there something I'm missing that would allow me to parse Real and Complex objects as distinct entities while still being able to effectively use them in other rules/grammars?
qi::_a and qi::_b represent the first and second local variables for a rule. These variables are only available if you add qi::locals<long double, long double> as a template parameter in the declaration of rule r (and in this case also to qi::grammar... since the start rule passed to the constructor of the grammar needs to be compatible with the grammar, ie have the same template parameters).
Below you can see another alternative without the need for the local variables:
// grammar for complex numbers of the form a+bi
template <typename Iterator>
struct Complex : qi::grammar<Iterator, std::complex<long double>()>
{
qi::rule<Iterator, std::complex<long double>()> r;
Real<Iterator> real;
Complex() : Complex::base_type(r)
{
r = (
real >> (qi::lit("+") | qi::lit("-"))
>> real >> -qi::lit("*") >> qi::lit("i")
)
[
qi::_val = phx::construct<std::complex<long double> >(qi::_1, qi::_2)
];
}
};
In this case the semantic action is attached to the whole parser sequence and we can get the attributes we need with the _N placeholders. Here, qi::_1 refers to the attribute matched by the first Real parser, and qi::_2 to the second one.
Using any of the alternatives we can then use those grammars normally:
//using complex_parser, real_parser, complex_val and real_val declared in your code
std::cout << "Parsing 'variable=3+4i-2'" << std::endl;
toParse = "variable=3+4i-2";
beginIt = toParse.begin();
endIt = toParse.end();
std::string identifier;
r = qi::parse(beginIt, endIt, *qi::char_("a-z") >> '=' >> complex_parser >> '-' >> real_parser, identifier, complex_val, real_val);
if(r && beginIt == endIt)
std::cout << "Successful parse: " << identifier << complex_val.real() << " " << complex_val.imag() << " " << real_val << std::endl;
else
std::cout << "Could not parse" << std::endl;

How do you output the original unparsed code (as a comment) from a spirit parser

Given the input string: A = 23; B = 5, I currently get the (expected) output:
Output: 0xa0000023
Output: 0xa0010005
-------------------------
I would like to see this instead:
Output: 0xa0000023 // A = 23
Output: 0xa0010005 // B = 5
-------------------------
The core line of code is:
statement = eps[_val = 0x50000000] >> identifier[_val += _1<<16] >>
"=" >> hex[_val += (_1 & 0x0000FFFF)];
Where identifier is a qi::symbols table lookup.
The rest of my code looks like this:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <iostream>
#include <iomanip>
#include <ios>
#include <string>
#include <complex>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
struct reg16_ : qi::symbols<char,unsigned> {
reg16_() {
add ("A", 0) ("B", 1) ("C", 2) ("D", 3) ;
}
} reg16;
template <typename Iterator>
struct dash_script_parser : qi::grammar<Iterator, std::vector<unsigned>(), ascii::space_type> {
dash_script_parser() : dash_script_parser::base_type(start) {
using qi::hex;
using qi::_val;
using qi::_1;
using qi::eps;
identifier %= reg16;
start %= (statement % ";" );
statement = eps[_val = 0x50000000] >> identifier[_val += _1<<16]>> "=" >> hex[_val += (_1 & 0x0000FFFF)];
}
qi::rule<Iterator, std::vector<unsigned>(), ascii::space_type> start;
qi::rule<Iterator, unsigned(), ascii::space_type> statement;
qi::rule<Iterator, unsigned()> identifier;
};
int
main()
{
std::cout << "\t\tA parser for Spirit...\n\n" << "Type [q or Q] to quit\n\n";
dash_script_parser<std::string::const_iterator> g;
std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q') break;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
std::vector<unsigned> strs;
bool r = phrase_parse(iter, end, g, boost::spirit::ascii::space, strs);
if (r && iter == end) {
for(std::vector<unsigned>::const_iterator it=strs.begin(); it<strs.end(); ++it)
std::cout << "Output: 0x" << std::setw(8) << std::setfill('0') << std::hex <<*it << "\n";
} else
std::cout << "Parsing failed\n";
}
return 0;
}
Update A newer answer brought iter_pos to my attention (from Boost Spirit Repository):
How do I capture the original input into the synthesized output from a spirit grammar?
This basically does the same as below, but without 'abusing' semantic actions (making it a much better fit, especially with automatic attribute propagation.
My gut feeling says that it will probably be easier to isolate statements into raw source iterator ranges first, and then parse the statements in isolation. That way, you'll have the corresponding source text at the start.
With that out of the way, here is an approach I tested to work without subverting your sample code too much:
1. Make the attribute type a struct
Replace the primitive unsigned with a struct that also contains the source snippet, verbatim, as a string:
struct statement_t
{
unsigned value;
std::string source;
};
BOOST_FUSION_ADAPT_STRUCT(statement_t, (unsigned, value)(std::string, source));
2. Make the parser fill both fields
The good thing is, you were already using semantic actions, so it is merely building onto that. Note that the result is not very pretty, and would benefit hugely from being converted into a (fused) functor. But it shows the technique very clearly:
start %= (statement % ";" );
statement = qi::raw [
raw[eps] [ at_c<0>(_val) = 0x50000000 ]
>> identifier [ at_c<0>(_val) += _1<<16 ]
>> "=" >> hex [ at_c<0>(_val) += (_1 & 0x0000FFFF) ]
]
[ at_c<1>(_val) = construct<std::string>(begin(_1), end(_1)) ]
;
3. Print
So, at_c<0>(_val) corresponds to statement::value, and at_c<1>(_val) corresponds to statement::source. This slightly modified output loop:
for(std::vector<statement_t>::const_iterator it=strs.begin(); it<strs.end(); ++it)
std::cout << "Output: 0x" << std::setw(8) << std::setfill('0') << std::hex << it->value << " // " << it->source << "\n";
outputs:
Output: 0x50000023 // A = 23
Output: 0x50010005 // B = 5
Full sample
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <iostream>
#include <iomanip>
#include <ios>
#include <string>
#include <complex>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
namespace phx = boost::phoenix;
struct reg16_ : qi::symbols<char,unsigned> {
reg16_() {
add ("A", 0) ("B", 1) ("C", 2) ("D", 3) ;
}
} reg16;
struct statement_t
{
unsigned value;
std::string source;
};
BOOST_FUSION_ADAPT_STRUCT(statement_t, (unsigned, value)(std::string, source));
template <typename Iterator>
struct dash_script_parser : qi::grammar<Iterator, std::vector<statement_t>(), ascii::space_type> {
dash_script_parser() : dash_script_parser::base_type(start) {
using qi::hex;
using qi::_val;
using qi::_1;
using qi::eps;
using qi::raw;
identifier %= reg16;
using phx::begin;
using phx::end;
using phx::at_c;
using phx::construct;
start %= (statement % ";" );
statement = raw [
raw[eps] [ at_c<0>(_val) = 0x50000000 ]
>> identifier [ at_c<0>(_val) += _1<<16 ]
>> "=" >> hex [ at_c<0>(_val) += (_1 & 0x0000FFFF) ]
]
[ at_c<1>(_val) = construct<std::string>(begin(_1), end(_1)) ]
;
}
qi::rule<Iterator, std::vector<statement_t>(), ascii::space_type> start;
qi::rule<Iterator, statement_t(), ascii::space_type> statement;
qi::rule<Iterator, unsigned()> identifier;
};
int
main()
{
std::cout << "\t\tA parser for Spirit...\n\n" << "Type [q or Q] to quit\n\n";
dash_script_parser<std::string::const_iterator> g;
std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q') break;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
std::vector<statement_t> strs;
bool r = phrase_parse(iter, end, g, boost::spirit::ascii::space, strs);
if (r && iter == end) {
for(std::vector<statement_t>::const_iterator it=strs.begin(); it<strs.end(); ++it)
std::cout << "Output: 0x" << std::setw(8) << std::setfill('0') << std::hex << it->value << " // " << it->source << "\n";
} else
std::cout << "Parsing failed\n";
}
return 0;
}

how to parse and verify an ordered list of integers using qi

I'm parsing a text file, possibly several GB in size, consisting of lines as follows:
11 0.1
14 0.78
532 -3.5
Basically, one int and one float per line. The ints should be ordered and non-negative. I'd like to verify the data are as described, and have returned to me the min and max int in the range. This is what I've come up with:
#include <iostream>
#include <string>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/std_pair.hpp>
namespace px = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace my_parsers
{
using namespace qi;
using px::at_c;
using px::val;
template <typename Iterator>
struct verify_data : grammar<Iterator, locals<int>, std::pair<int, int>()>
{
verify_data() : verify_data::base_type(section)
{
section
= line(val(0)) [ at_c<0>(_val) = _1]
>> +line(_a) [ _a = _1]
>> eps [ at_c<1>(_val) = _a]
;
line
%= (int_ >> other) [
if_(_r1 >= _1)
[
std::cout << _r1 << " and "
<< _1 << val(" out of order\n")
]
]
;
other
= omit[(lit(' ') | '\t') >> float_ >> eol];
}
rule<Iterator, locals<int>, std::pair<int, int>() > section;
rule<Iterator, int(int)> line;
rule<Iterator> other;
};
}
using namespace std;
int main(int argc, char** argv)
{
string input("11 0.1\n"
"14 0.78\n"
"532 -3.6\n");
my_parsers::verify_data<string::iterator> verifier;
pair<int, int> p;
std::string::iterator begin(input.begin()), end(input.end());
cout << "parse result: " << boolalpha
<< qi::parse(begin, end, verifier, p) << endl;
cout << "p.first: " << p.first << "\np.second: " << p.second << endl;
return 0;
}
What I'd like to know is the following:
Is there a better way of going about this? I have used inherited and synthesised attributes, local variables and a bit of phoenix voodoo. This is great; learning the tools is good but I can't help thinking there might be a much simpler way of achieving the same thing :/ (within a PEG parser that is...)
How could it be done without the local variable for instance?
More info: I have other data formats that are being parsed at the same time and so I'd like to keep the return value as a parser attribute. At the moment this is a std::pair, the other data formats when parsed, will expose their own std::pairs for instance and it's these that I'd like to stuff in a std::vector.
This is at least a lot shorter already:
down to 28 LOC
no more locals
no more fusion vector at<> wizardry
no more inherited attributes
no more grammar class
no more manual iteration
using expectation points (see other) to enhance parse error reporting
this parser expressions synthesizes neatly into a vector<int> if you choose to assign it with %= (but it will cost performance, besides potentially allocating a largish array)
.
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
namespace px = boost::phoenix;
namespace qi = boost::spirit::qi;
typedef std::string::iterator It;
int main(int argc, char** argv)
{
std::string input("11 0.1\n"
"14 0.78\n"
"532 -3.6\n");
int min=-1, max=0;
{
using namespace qi;
using px::val;
using px::ref;
It begin(input.begin()), end(input.end());
rule<It> index = int_
[
if_(ref(max) < _1) [ ref(max) = _1 ] .else_ [ std::cout << _1 << val(" out of order\n") ],
if_(ref(min) < 0) [ ref(min) = _1 ]
] ;
rule<It> other = char_(" \t") > float_ > eol;
std::cout << "parse result: " << std::boolalpha
<< qi::parse(begin, end, index % other) << std::endl;
}
std::cout << "min: " << min << "\nmax: " << max << std::endl;
return 0;
}
Bonus
I might suggest taking the validation out of the expression and make it a free-standing function; of course, this makes things more verbose (and... legible) and my braindead sample uses global variables... -- but I trust you know how to use boost::bind or px::bind to make it more real-life
In addition to the above
down to 27 LOC even with the free function
no more phoenix, no more phoenix includes (yay compile times)
no more phoenix expression types in debug builds ballooning the binary and slowing it down
no more var, ref, if_, .else_ and the wretched operator, (which had major bug risk (at some time) due to the overload not being included with phoenix.hpp)
(easily ported to c++0x lambda's - immediately removing the need for global variables)
.
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
namespace px = boost::phoenix;
namespace qi = boost::spirit::qi;
typedef std::string::iterator It;
int min=-1, max=0, linenumber=0;
void validate_index(int index)
{
linenumber++;
if (min < 0) min = index;
if (max < index) max = index;
else std::cout << index << " out of order at line " << linenumber << std::endl;
}
int main(int argc, char** argv)
{
std::string input("11 0.1\n"
"14 0.78\n"
"532 -3.6\n");
It begin(input.begin()), end(input.end());
{
using namespace qi;
rule<It> index = int_ [ validate_index ] ;
rule<It> other = char_(" \t") > float_ > eol;
std::cout << "parse result: " << std::boolalpha
<< qi::parse(begin, end, index % other) << std::endl;
}
std::cout << "min: " << min << "\nmax: " << max << std::endl;
return 0;
}
I guess a much simpler way would be to parse the file using standard stream operations and then check the ordering in a loop. First, the input:
typedef std::pair<int, float> value_pair;
bool greater(const value_pair & left, const value_pair & right) {
return left.first > right.first;
}
std::istream & operator>>(std::istream & stream, value_pair & value) {
stream >> value.first >> value.second;
return stream;
}
The use it like this:
std::ifstream file("your_file.txt");
std::istream_iterator<value_pair> it(file);
std::istream_iterator<value_pair> eof;
if(std::adjacent_find(it, eof, greater) != eof) {
std::cout << "The values are not ordered" << std::endl;
}
I find this a lot simpler.