I have the following code can not compile, however I do not know where is the problem:
#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_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
void print (std::string& s)
{
std::cout << "Get string [" << s << ']' << std::endl;
}
namespace client
{
namespace fusion = boost::fusion;
namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
template <typename Iterator>
struct my_grammar
: qi::grammar<Iterator, std::string(), ascii::space_type>
{
my_grammar()
: my_grammar::base_type(start)
{
using qi::lit;
using qi::int_;
using qi::lexeme;
using ascii::char_;
using ascii::string;
using ascii::space_type;
using namespace qi::labels;
// Here you can see the rule oct_char with attribute type as char
// +(char) would be vector<char> which is compactable with string type
// Why the compiler still told me the type of the attribute is not right?
start %= lexeme[+(oct_char)] [&print]
;
oct_char =
lexeme["//"
>> char_('0','3') [ _a = ( _1 - '0' ) ]
|| char_('0','7') [ _a = _a << 3 + ( _1 - '0' ) ]
|| char_('0','7') [ _a = _a << 3 + ( _1 - '0' ) ]]
[ _val = _a ]
;
}
qi::rule<Iterator, std::string(), ascii::space_type> start;
qi::rule<Iterator, char(), qi::locals<char>, ascii::space_type> oct_char;
};
}
int main(int argc, char **argv)
{
typedef client::my_grammar<std::string::const_iterator> Grammer;
Grammer grm;
std::string str;
using boost::spirit::ascii::space;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
if (phrase_parse(str.begin(), str.end(), grm, space))
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "got: " << str << std::endl;
std::cout << "\n-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}
Here are the error messages:
Error 2 error C2664: 'void (std::basic_string<_Elem,_Traits,_Ax> )' : cannot convert parameter 1 from 'std::vector<_Ty>' to 'std::basic_string<_Elem,_Traits,_Ax> ' d:\code\boost_1_46_1\boost\spirit\home\support\action_dispatch.hpp 70
Is there anyone can give some suggestions?
Thanks
This has been discussed at length elsewhere (for instance here). If you change your print function to take a vector<char> it will work:
void print (std::vector<char> const& s)
{
std::cout << "Get string ["
<< std::string(s.begin(), s.end()) << ']'
<< std::endl;
}
There is one more error in your code, though. The rule oct_char does not need to have a skipper as it is used inside lexeme[]:
qi::rule<Iterator, char(), qi::locals<char> > oct_char;
Related
I started to learn Boost.Spirit and finish reading Qi - Writing Parsers section. When reading, everything is easy and understandable. But when I try to do something, there are a lot of errors, because there are too many includes and namespaces and I need to know when to include/use them. As the practice, I want to write simple INI parser.
Here is the code (includes are from one of examples inside Spirit lib as almost everything else):
#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_stl.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <map>
namespace client
{
typedef std::map<std::string, std::string> key_value_map_t;
struct mini_ini
{
std::string name;
key_value_map_t key_values_map;
};
} // client
BOOST_FUSION_ADAPT_STRUCT(
client::mini_ini,
(std::string, name)
(client::key_value_map_t, key_values_map)
)
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
template <typename Iterator>
struct ini_grammar : qi::grammar<Iterator, mini_ini(), ascii::space_type>
{
ini_grammar() : ini_grammar::base_type(section_, "section")
{
using qi::char_;
using qi::on_error;
using qi::fail;
using namespace qi::labels;
using phoenix::construct;
using phoenix::val;
key_ = +char_("a-zA-Z_0-9");
pair_ = key_ >> '=' >> *char_;
section_ = '[' >> key_ >> ']' >> '\n' >> *(pair_ >> '\n');
key_.name("key");
pair_.name("pair");
section_.name("section");
on_error<fail>
(
section_
, std::cout
<< val("Error! Expecting ")
<< _4 // what failed?
<< val(" here: \"")
<< construct<std::string>(_3, _2) // iterators to error-pos, end
<< val("\"")
<< std::endl
);
}
qi::rule<Iterator, std::string(), ascii::space_type> key_;
qi::rule<Iterator, mini_ini(), ascii::space_type> section_;
qi::rule<Iterator, std::pair<std::string, std::string>(), ascii::space_type> pair_;
};
} // client
int
main()
{
std::string storage =
"[section]\n"
"key1=val1\n"
"key2=val2\n";
client::mini_ini ini;
typedef client::ini_grammar<std::string::const_iterator> ini_grammar;
ini_grammar grammar;
using boost::spirit::ascii::space;
std::string::const_iterator iter = storage.begin();
std::string::const_iterator end = storage.end();
bool r = phrase_parse(iter, end, grammar, space, ini);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
return 0;
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
std::cout << std::string(iter, end) << "\n";
return 1;
}
return 0;
}
As u can see I want to parse next text into mini_ini struct:
"[section]"
"key1=val1"
"key2=val2";
I have the fail and std::string(iter, end) is full input string.
My questions:
Why I see fail but don't see on_error<fail> handler ?
Have you any recommendations how to learn Boost.Spirit (I have good understanding of documentation in theory, but in practice I have a lot of WHY ???) ?
Thanks
Q. Why I see fail but don't see on_error handler
The on_error handler is only fired for the registered rule (section_) and if an expectation point is failed.
Your grammar doesn't contain expectation points (only >> are used, not >).
Q. Have you any recommendations how to learn Boost.Spirit (I have good understanding of documentation in theory, but in practice I have a lot of WHY ???) ?
Just build the parsers you need. Copy good conventions from the docs and SO answers. There are a lot of them. As you have seen, quite a number contain full examples of Ini parsers with varying levels of error reporting too.
Bonus hints:
Do more detailed status reporting:
bool ok = phrase_parse(iter, end, grammar, space, ini);
if (ok) {
std::cout << "Parse success\n";
} else {
std::cout << "Parse failure\n";
}
if (iter != end) {
std::cout << "Remaining unparsed: '" << std::string(iter, end) << "'\n";
}
return ok && (iter==end)? 0 : 1;
Use BOOST_SPIRIT_DEBUG:
#define BOOST_SPIRIT_DEBUG
// and later
BOOST_SPIRIT_DEBUG_NODES((key_)(pair_)(section_))
Prints:
<section_>
<try>[section]\nkey1=val1\n</try>
<key_>
<try>section]\nkey1=val1\nk</try>
<success>]\nkey1=val1\nkey2=val</success>
<attributes>[[s, e, c, t, i, o, n]]</attributes>
</key_>
<fail/>
</section_>
Parse failure
Remaining unparsed: '[section]
key1=val1
key2=val2
'
You'll notice that the section header isn't parsed because the newline is not matched. Your skipper (space_type) skips the newline, hence it will never match: Boost spirit skipper issues
Fix skipper
When using blank_type as the skipper you'll get a successful parse:
<section_>
<try>[section]\nkey1=val1\n</try>
<key_>
<try>section]\nkey1=val1\nk</try>
<success>]\nkey1=val1\nkey2=val</success>
<attributes>[[s, e, c, t, i, o, n]]</attributes>
</key_>
<pair_>
<try>key1=val1\nkey2=val2\n</try>
<key_>
<try>key1=val1\nkey2=val2\n</try>
<success>=val1\nkey2=val2\n</success>
<attributes>[[k, e, y, 1]]</attributes>
</key_>
<success></success>
<attributes>[[[k, e, y, 1], [v, a, l, 1,
, k, e, y, 2, =, v, a, l, 2,
]]]</attributes>
</pair_>
<success>key1=val1\nkey2=val2\n</success>
<attributes>[[[s, e, c, t, i, o, n], []]]</attributes>
</section_>
Parse success
Remaining unparsed: 'key1=val1
key2=val2
NOTE: The parse succeeds but doesn't do what you want. This is because *char_ includes newlines. So make that
pair_ = key_ >> '=' >> *(char_ - qi::eol); // or
pair_ = key_ >> '=' >> *~char_("\r\n"); // etc
Full code
Live On Coliru
#define BOOST_SPIRIT_DEBUG
#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_stl.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <map>
namespace client
{
typedef std::map<std::string, std::string> key_value_map_t;
struct mini_ini
{
std::string name;
key_value_map_t key_values_map;
};
} // client
BOOST_FUSION_ADAPT_STRUCT(
client::mini_ini,
(std::string, name)
(client::key_value_map_t, key_values_map)
)
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
template <typename Iterator>
struct ini_grammar : qi::grammar<Iterator, mini_ini(), ascii::blank_type>
{
ini_grammar() : ini_grammar::base_type(section_, "section")
{
using qi::char_;
using qi::on_error;
using qi::fail;
using namespace qi::labels;
using phoenix::construct;
using phoenix::val;
key_ = +char_("a-zA-Z_0-9");
pair_ = key_ >> '=' >> *char_;
section_ = '[' >> key_ >> ']' >> '\n' >> *(pair_ >> '\n');
BOOST_SPIRIT_DEBUG_NODES((key_)(pair_)(section_))
on_error<fail>
(
section_
, std::cout
<< val("Error! Expecting ")
<< _4 // what failed?
<< val(" here: \"")
<< construct<std::string>(_3, _2) // iterators to error-pos, end
<< val("\"")
<< std::endl
);
}
qi::rule<Iterator, std::string(), ascii::blank_type> key_;
qi::rule<Iterator, mini_ini(), ascii::blank_type> section_;
qi::rule<Iterator, std::pair<std::string, std::string>(), ascii::blank_type> pair_;
};
} // client
int
main()
{
std::string storage =
"[section]\n"
"key1=val1\n"
"key2=val2\n";
client::mini_ini ini;
typedef client::ini_grammar<std::string::const_iterator> ini_grammar;
ini_grammar grammar;
using boost::spirit::ascii::blank;
std::string::const_iterator iter = storage.begin();
std::string::const_iterator end = storage.end();
bool ok = phrase_parse(iter, end, grammar, blank, ini);
if (ok) {
std::cout << "Parse success\n";
} else {
std::cout << "Parse failure\n";
}
if (iter != end) {
std::cout << "Remaining unparsed: '" << std::string(iter, end) << "'\n";
}
return ok && (iter==end)? 0 : 1;
}
Writing Qi grammar with Phoenix bind I got a compilation error like
boost/spirit/home/support/context.hpp(180): error C2338: index_is_out_of_bounds
here
>> ruleHandId_[phx::bind(&parseContext::handId_, qi::_r1) = qi::_1];
I just havent too much expirience with phoenix binding but perv bind in the line
ruleStart_ = ruleEncoding_[phx::bind(&parseContext::encoding_, qi::_r1) = qi::_1]
works good without compilation errors
It's all under MSVC from VS2013 with boost 1.56 x86
Whats I do wrong under code with the compilation error?
Source Code
#include <boost/spirit/include/qi.hpp>
#include <boost/phoenix/phoenix.hpp>
#include <boost/shared_ptr.hpp>
#include <sstream>
namespace sp = boost::spirit;
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
struct parseContext {
std::string encoding_;
uint64_t handId_;
};
typedef boost::shared_ptr<parseContext> parseContextShPtr;
template <typename Iterator>
struct parseGrammar : qi::grammar<Iterator, void(parseContext&)> {
parseGrammar() : parseGrammar::base_type(ruleStart_)
{
ruleStart_ = ruleEncoding_[phx::bind(&parseContext::encoding_, qi::_r1) = qi::_1]
>> ruleHandHeader_;
ruleEncoding_ = qi::lit("ABC");
ruleHandHeader_ = qi::lit("DEF") >> qi::space
>> qi::lit("XYZ #")
>> ruleHandId_[phx::bind(&parseContext::handId_, qi::_r1) = qi::_1];
ruleHandId_ = qi::long_long;
}
// Rules
qi::rule<Iterator, void(parseContext&)> ruleStart_;
qi::rule<Iterator, std::string()> ruleEncoding_;
qi::rule<Iterator> ruleHandHeader_;
qi::rule<Iterator, uint64_t> ruleHandId_;
};
void test()
{
std::string s("ABCDEF XYZ #555: PQI #777");
std::stringstream sb;
sb.unsetf(std::ios::skipws);
sb << s;
const parseGrammar<sp::istream_iterator> p;
sp::istream_iterator b(sb);
sp::istream_iterator e;
parseContextShPtr ctx(new parseContext);
bool r = qi::parse(b, e, p(phx::ref(*ctx.get())));
if (r) {
std::cout << "Success" << std::endl;
}
else {
std::cout << "Failure" << std::endl;
}
std::cout << std::string(b, e).substr(0, 32) << std::endl;
}
Some of the placeholders cannot be bound.
This could be because ruleEncoding_ doesn't expose an attribute (for _1) (unlikely) or ruleStart_ doesn't have the inherited attribute (_r1).
That's all I can tell you right now.
Edit It was the latter. ruleHandHeader doesn't declare any attributes, let alone an inherited attribute to bind to _r1
Update To the comment.
Here are some suggestions. Much in the vein of my oft-repeated advice to avoid semantic actions (Boost Spirit: "Semantic actions are evil"?), I'd adapt the structure as a fusion sequence:
And use much simplified grammar rules:
ruleStart_ = ruleEncoding_ >> ruleHandHeader_;
ruleEncoding_ = "ABC";
ruleHandId_ = qi::long_long;
ruleHandHeader_ = "DEF XYZ #" >> ruleHandId_;
Now, adding in BOOST_SPIRIT_DEBUG macros and fixing uint64_t to uint64_t() in the rule definition:
Live On Coliru
#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/shared_ptr.hpp>
#include <sstream>
namespace qi = boost::spirit::qi;
struct parseContext {
std::string encoding_;
uint64_t handId_;
};
BOOST_FUSION_ADAPT_STRUCT(parseContext, (std::string, encoding_)(uint64_t,handId_))
typedef boost::shared_ptr<parseContext> parseContextShPtr;
template <typename Iterator>
struct parseGrammar : qi::grammar<Iterator, parseContext()> {
parseGrammar() : parseGrammar::base_type(ruleStart_)
{
ruleStart_ = ruleEncoding_ >> ruleHandHeader_;
ruleEncoding_ = "ABC";
ruleHandId_ = qi::long_long;
ruleHandHeader_ = "DEF XYZ #" >> ruleHandId_;
BOOST_SPIRIT_DEBUG_NODES((ruleStart_)(ruleEncoding_)(ruleHandId_)(ruleHandHeader_))
}
// Rules
qi::rule<Iterator, parseContext()> ruleStart_;
qi::rule<Iterator, std::string()> ruleEncoding_;
qi::rule<Iterator, uint64_t()> ruleHandId_, ruleHandHeader_;
};
void test()
{
std::stringstream sb("ABCDEF XYZ #555: PQI #777");
sb.unsetf(std::ios::skipws);
typedef boost::spirit::istream_iterator It;
const parseGrammar<It> p;
It b(sb), e;
parseContextShPtr ctx(new parseContext);
bool r = qi::parse(b, e, p, *ctx);
if (r) {
std::cout << "Success: " << ctx->encoding_ << ", " << ctx->handId_ << std::endl;
}
else {
std::cout << "Failure" << std::endl;
}
if (b!=e)
std::cout << "Remaining: '" << std::string(b, e).substr(0, 32) << "'...\n";
}
int main()
{
test();
}
Prints
Success: ABC, 555
Remaining: ': PQI #777'...
I have a chunk of Spirit code which correctly parses std::string input = "RED.MAGIC( 1, 2, 3 )[9].GREEN" into a simple std::vector<std::string>, by using std::vector<std::string> as the primary attribute.
I would like to replace the std::vector<std::string> into a struct my_rec which contains a std::vector<std::string>, but continue using the auto generator, if possible.
When I compile with -DUSE_MY_REC, I get a wall of impenetrable compile errors.
sample compile and run
/tmp$ g++ -g -std=c++11 sandbox.cpp -o sandbox && ./sandbox
Finished.
MATCHED
/tmp$ g++ -DUSE_MY_REC -g -std=c++11 sandbox.cpp -o sandbox && ./sandbox
WALL OF COMPILE ERRORS --------------------------------------------
sandbox.cpp
// #define BOOST_SPIRIT_DEBUG
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <string>
#include <iostream>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
#ifdef USE_MY_REC
struct my_rec
{
std::vector<std::string> m_pieces;
};
BOOST_FUSION_ADAPT_STRUCT(
my_rec,
(std::vector<std::string>, m_pieces)
)
typedef struct my_rec MY_TYPE;
#else
typedef std::vector<std::string> MY_TYPE;
#endif
template <typename ITERATOR>
struct my_parser :
qi::grammar<ITERATOR, MY_TYPE(), ascii::space_type>
{
my_parser() :
my_parser::base_type( start )
{
start %= ( color | fun_call ) % '.'
;
color %=
qi::string( "RED" )
| qi::string( "GREEN" )
| qi::string( "BLUE" )
;
fun_call %=
qi::string( "MAGIC" )
>> '('
>> +qi::char_("0-9") % ','
>> ')'
>> '['
>> +qi::char_("0-9")
>> ']'
;
}
qi::rule<ITERATOR, MY_TYPE(), ascii::space_type> start, fun_call;
qi::rule<ITERATOR, std::string(), ascii::space_type> color;
};
int
main( int argc, char* argv[] )
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
MY_TYPE v;
std::string str = "RED.MAGIC( 1, 2, 3 )[9].GREEN";
std::vector<std::string> exp = {{ "RED", "MAGIC", "1", "2", "3", "9", "GREEN" }};
auto it = str.begin(), end = str.end();
my_parser<decltype(it)> g;
if( qi::phrase_parse( it, end, g, ascii::space, v ) && it==end )
{
std::cout << "Finished." << std::endl;
#ifndef USE_MY_REC
if ( !std::equal( v.begin(), v.end(), exp.begin() ))
{
std::cout << "MISMATCH" << std::endl;
for( const auto& x : v )
std::cout << x << std::endl;
} else {
std::cout << "MATCHED" << std::endl;
}
#endif
} else
std::cout << "Error." << std::endl;
return 0;
}
As shown in the question linked in the comments in order to assign to an adapted struct with a single member you need to use qi::eps in a sequence. Also you need to change the attribute of your intermediate fun_call rule to std::vector<std::string>().
// #define BOOST_SPIRIT_DEBUG
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <string>
#include <iostream>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
struct my_rec
{
std::vector<std::string> m_pieces;
};
BOOST_FUSION_ADAPT_STRUCT(
my_rec,
(std::vector<std::string>, m_pieces)
)
typedef struct my_rec MY_TYPE;
template <typename ITERATOR>
struct my_parser :
qi::grammar<ITERATOR, MY_TYPE(), ascii::space_type>
{
my_parser() :
my_parser::base_type( start )
{
start %= qi::eps >>( color | fun_call ) % '.' //add qi::eps to make the adaptation of the single member struct work
;
color %=
qi::string( "RED" )
| qi::string( "GREEN" )
| qi::string( "BLUE" )
;
fun_call %=
qi::string( "MAGIC" )
>> '('
>> +qi::char_("0-9") % ','
>> ')'
>> '['
>> +qi::char_("0-9")
>> ']'
;
}
qi::rule<ITERATOR, MY_TYPE(), ascii::space_type> start;
qi::rule<ITERATOR, std::vector<std::string>(), ascii::space_type> fun_call; //changed this rule's attribute to vector<string>
qi::rule<ITERATOR, std::string(), ascii::space_type> color;
};
int
main( int argc, char* argv[] )
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
MY_TYPE v;
std::string str = "RED.MAGIC( 1, 2, 3 )[9].GREEN";
std::vector<std::string> exp;
exp.push_back("RED");
exp.push_back("MAGIC");
exp.push_back("1");
exp.push_back("2");
exp.push_back("3");
exp.push_back("9");
exp.push_back("GREEN");
auto it = str.begin(), end = str.end();
my_parser<decltype(it)> g;
if( qi::phrase_parse( it, end, g, ascii::space, v ) && it==end )
{
std::cout << "Finished." << std::endl;
if ( !std::equal( v.m_pieces.begin(), v.m_pieces.end(), exp.begin() ))
{
std::cout << "MISMATCH" << std::endl;
for( const auto& x : v.m_pieces )
std::cout << x << std::endl;
} else {
std::cout << "MATCHED" << std::endl;
}
} else
std::cout << "Error." << std::endl;
return 0;
}
You may use boost::spirit::qi::as to convert parsed data to a vector and then put it into your structure.
fun_call %= as<std::vector<std::string>>()[...];
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;
}
I'm newbie in Boost.Spirit.Lex.
Some strange error appears every time I try to use lex::_val in semantics actions in my simple lexer:
#ifndef _TOKENS_H_
#define _TOKENS_H_
#include <iostream>
#include <string>
#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>
namespace lex = boost::spirit::lex;
namespace phx = boost::phoenix;
enum tokenids
{
ID_IDENTIFICATOR = 1,
ID_CONSTANT,
ID_OPERATION,
ID_BRACKET,
ID_WHITESPACES
};
template <typename Lexer>
struct mega_tokens
: lex::lexer<Lexer>
{
mega_tokens()
: identifier(L"[a-zA-Z_][a-zA-Z0-9_]*", ID_IDENTIFICATOR)
, constant (L"[0-9]+(\\.[0-9]+)?", ID_CONSTANT )
, operation (L"[\\+\\-\\*/]", ID_OPERATION )
, bracket (L"[\\(\\)\\[\\]]", ID_BRACKET )
{
using lex::_tokenid;
using lex::_val;
using phx::val;
this->self
= operation [ std::wcout
<< val(L'<') << _tokenid
// << val(L':') << lex::_val
<< val(L'>')
]
| identifier [ std::wcout
<< val(L'<') << _tokenid
<< val(L':') << _val
<< val(L'>')
]
| constant [ std::wcout
<< val(L'<') << _tokenid
// << val(L':') << _val
<< val(L'>')
]
| bracket [ std::wcout
<< val(L'<') << _tokenid
// << val(L':') << lex::_val
<< val(L'>')
]
;
}
lex::token_def<wchar_t, wchar_t> operation;
lex::token_def<std::wstring, wchar_t> identifier;
lex::token_def<double, wchar_t> constant;
lex::token_def<wchar_t, wchar_t> bracket;
};
#endif // _TOKENS_H_
and
#include <cstdlib>
#include <iostream>
#include <locale>
#include <boost/spirit/include/lex_lexertl.hpp>
#include "tokens.h"
int main()
{
setlocale(LC_ALL, "Russian");
namespace lex = boost::spirit::lex;
typedef std::wstring::iterator base_iterator;
typedef lex::lexertl::token <
base_iterator,
boost::mpl::vector<wchar_t, std::wstring, double, wchar_t>,
boost::mpl::true_
> token_type;
typedef lex::lexertl::actor_lexer<token_type> lexer_type;
typedef mega_tokens<lexer_type>::iterator_type iterator_type;
mega_tokens<lexer_type> mega_lexer;
std::wstring str = L"alfa+x1*(2.836-x2[i])";
base_iterator first = str.begin();
bool r = lex::tokenize(first, str.end(), mega_lexer);
if (r) {
std::wcout << L"Success" << std::endl;
}
else {
std::wstring rest(first, str.end());
std::wcerr << L"Lexical analysis failed\n" << L"stopped at: \""
<< rest << L"\"\n";
}
return EXIT_SUCCESS;
}
This code causes an error in Boost header 'boost/spirit/home/lex/argument.hpp' on line 167 while compiling:
return: can't convert 'const
boost::variant' to
'boost::variant &'
When I don't use lex::_val program compiles with no errors.
Obviously, I use _val in wrong way, but I do not know how to do this correctly. Help, please! :)
P.S. And sorry for my terrible English…
I believe this is a problem in the current Phoenix related to using iostreams. As a workaround I suggest to define a custom (Phoenix) function doing the actual output:
struct output_operation_impl
{
template <typename TokenId, typename Val>
struct result { typedef void type; };
template <typename TokenId, typename Val>
void operator()(T1 const& tokenid, T2 const& val) const
{
std::wcout << L'<' << tokenid << L':' << val << L'>';
}
};
boost::phoenix::function<output_operation_impl> const output_operation =
output_operation_impl();
calling it as:
this->self = operation[ output_operation(_tokenid, _val) ] ... ;
Regards Hartmut