Boost Spirit X3 and std::unordered_map - c++

I want to parse into a std::unordered_map.
Example Code:
struct Base
{
int item1;
int item2;
};
BOOST_FUSION_ADAPT_STRUCT(Base, item1, item2)
namespace grammar
{
using namespace boost::spirit::x3;
auto base_ = rule<struct base_, Base>{"base"}
= repeat(2)[ int_ ];
auto start = rule<struct start, std::unordered_map<std::int32_t, Base>>{"start"}
= (id_ >> base_) % eol;
}
With the following main:
namespace ios = boost::iostreams;
namespace fs = boost::filesystem;
namespace x3 = boost::spirit::x3;
int main()
{
std::unordered_map<std::int32_t, Base> bases;
ios::mapped_file mmf("example.dat");
auto beg = std::begin(mmf);
auto end = std::end(mmf);
auto ret = x3::phrase_parse(beg, end, grammar::start, x3::char_(','), bases);
if (ret && beg == end)
{
std::cout << "Parse ok\n";
}
mmf.close();
return 0;
}
and one example file:
1,2,3
2,3,4
3,4,5
The compiler error message is:
.... ‘class std::unordered_map<int, Base>’ has no member named ‘push_back’
What to do next, is there a way da adapt std::unordered_map?

Likee jv_ supposed, updating my boost version fixed the problem.

Related

Boost spirit x3 - how to parse nested structures?

I try to parse
list<char> fldName
I used the nested structures. But I have a trouble with parsing when one structure nested in other. Look at the sollowing minimal sample code:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <string>
#include <string_view>
using namespace std::string_view_literals;
using namespace boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
namespace client::ast
{
struct ValidType
{
std::string Name;
std::string SubName1;
std::string SubName2;
};
struct StructField
{
ValidType Type;
std::string Name;
};
} // namespace client::ast
BOOST_FUSION_ADAPT_STRUCT(client::ast::ValidType,
Name, SubName1, SubName2
)
BOOST_FUSION_ADAPT_STRUCT(client::ast::StructField,
Type, Name
)
namespace client::parser
{
using ascii::char_;
template <typename T> static auto as = [](auto p) { return rule<struct tag, T> {"as"} = p; };
#define STRING(x) as<std::string>(x)
rule<class ValidType, ast::ValidType> const ValidType = "ValidType";
rule<class StructField, ast::StructField> const StructField = "StructField";
auto const ValidName = lexeme[(alpha | char_('_')) > *(alnum | char_('_'))];
auto const ValidType_SecondPart = char('<') > STRING(ValidName) > ('>' | ',' > STRING(ValidName) > '>');
auto const ValidType_def = STRING(ValidName) > -(ValidType_SecondPart);
auto const StructField_def = ValidType_def > STRING(ValidName);
BOOST_SPIRIT_DEFINE(ValidType);
BOOST_SPIRIT_DEFINE(StructField);
} // namespace client::parser
int main()
{
using boost::spirit::x3::ascii::space;
auto theData = R"(
list<char> fldName
)"sv;
using client::parser::StructField;
client::ast::StructField fld;
bool result = phrase_parse(theData.begin(), theData.end(), StructField, space, fld);
return result;
}
I receive following error:
Error C2338 Size of the passed attribute is less than expected
But I have no idea what is wrong. Its looks like boost::spirit::x3 have a bug with parsing nested structures.
Is there exists any way how to parse nested stuctures?
Answer - ValidType_def -> ValidType :
auto const StructField_def = ValidType_def > STRING(ValidName);
->
auto const StructField_def = ValidType > STRING(ValidName);

boost spirit x3 variant and std::pair

I tried to run some simple parser that will parse [ 1, 11, 3, 6-4]. Basically, integer list with range notation.
I want to put everything into AST without semantic action. So I use x3::variant. My code 'seems' very similar to the expression example. However, it can't compile under g++ 6.2. It indeed compile ok with clang++ 6.0 but yield wrong result.
The boost version is 1.63.
It seems that I have some 'move' or initialization issue.
#include <iostream>
#include <list>
#include <vector>
#include <utility>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/include/io.hpp>
namespace ns
{
namespace ast
{
namespace x3 = boost::spirit::x3;
// forward definition
class uintObj;
struct varVec;
// define type
using uintPair_t = std::pair<unsigned int, unsigned int>;
using uintVec_t = std::vector<uintObj>;
// general token value:
class uintObj : public x3::variant <
unsigned int,
uintPair_t
>
{
public:
using base_type::base_type;
using base_type::operator=;
};
struct varVec
{
uintVec_t valVector;
};
}
}
BOOST_FUSION_ADAPT_STRUCT(
ns::ast::varVec,
valVector
)
namespace ns
{
namespace parser
{
// namespace x3 = boost::spirit::x3;
// using namespace x3;
using namespace boost::spirit::x3;
// definition of the range pair:
rule<class uintPair, ast::uintPair_t> const uintPair = "uintPair";
auto const uintPair_def =
uint_
>> '-'
>> uint_
;
rule<class uintObj, ast::uintObj> const uintObj = "uintObj";
auto const uintObj_def =
uint_
| uintPair
;
// define rule definition : rule<ID, attrib>
// more terse definition :
// struct varVec_class;
// using varVec_rule_t = x3::rule<varVec_class, ast::varVec>;
// varVec_rule_t const varVec = "varVec";
// varVec is the rule, "varVec" is the string name of the rule.
rule<class varVec, ast::varVec> const varVec = "varVec";
auto const varVec_def =
'['
>> uintObj % ','
>> ']'
;
BOOST_SPIRIT_DEFINE(
varVec,
uintObj,
uintPair
);
}
}
int main()
{
std::string input ("[1, 11, 3, 6-4]\n");
std::string::const_iterator begin = input.begin();
std::string::const_iterator end = input.end();
ns::ast::varVec result; // ast tree
using ns::parser::varVec; // grammar
using boost::spirit::x3::ascii::space;
bool success = phrase_parse(begin, end, varVec, space, result);
if (success && begin == end)
std::cout << "good" << std::endl;
else
std::cout << "bad" << std::endl;
return 0;
}
Swap the alternative order for the uintObj_def
auto const uintObj_def =
uintPair
| uint_
;
The formulation you have now will always match on a uint_ because the uintPair begins with a valid uint_.
mjcaisse's answer calls out the main problem I think you had. There were a few missing pieces, so I decided to make a simplified version that shows parsing results:
Live On Wandbox
#include <iostream>
#include <iomanip>
//#include <boost/fusion/adapted.hpp>
//#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
namespace x3 = boost::spirit::x3;
namespace ns { namespace ast {
// forward definition
struct uintObj;
//struct varVec;
// define type
using uintPair_t = std::pair<unsigned int, unsigned int>;
using uintVec_t = std::vector<uintObj>;
// general token value:
struct uintObj : x3::variant<unsigned int, uintPair_t> {
using base_type::base_type;
using base_type::operator=;
friend std::ostream& operator<<(std::ostream& os, uintObj const& This) {
struct {
std::ostream& os;
void operator()(unsigned int v) const { os << v; }
void operator()(uintPair_t v) const { os << v.first << "-" << v.second; }
} vis { os };
boost::apply_visitor(vis, This);
return os;
}
};
using varVec = uintVec_t;
} }
namespace ns { namespace parser {
using namespace boost::spirit::x3;
template <typename T> auto as = [](auto p) { return rule<struct _, T> {} = p; };
auto const uintPair = as<ast::uintPair_t> ( uint_ >> '-' >> uint_ );
auto const uintObj = as<ast::uintObj> ( uintPair | uint_ );
auto const varVec = as<ast::varVec> ( '[' >> uintObj % ',' >> ']' );
} }
int main() {
using namespace ns;
std::string const input("[1, 11, 3, 6-4]\n");
auto begin = input.begin(), end = input.end();
ast::varVec result; // ast tree
bool success = phrase_parse(begin, end, parser::varVec, x3::ascii::space, result);
if (success) {
std::cout << "good\n";
for (auto& r : result)
std::cout << r << "\n";
}
else
std::cout << "bad\n";
if (begin != end)
std::cout << "Remaining unparsed: " << std::quoted(std::string(begin, end)) << std::endl;
}
Prints
good
1
11
3
6-4

Templating Spirit X3 parser

In Boost Spirit QI it was easy to template the parser so that it could be instantiated for various attribute types. It is unclear to me how to do this with X3. Consider this stripped down version of the roman numerals parser example:
#include <iostream>
#include <iterator>
#include <string>
#include <boost/spirit/home/x3.hpp>
namespace parser {
namespace x3 = boost::spirit::x3;
struct numbers_ : x3::symbols<unsigned> {
numbers_() {
add
("I" , 1)
("II" , 2)
("III" , 3)
("IV" , 4)
("V" , 5)
("VI" , 6)
("VII" , 7)
("VIII" , 8)
("IX" , 9)
;
}
} numbers;
x3::rule<class roman, unsigned> const roman = "roman";
auto init = [](auto &x) { x3::_val(x) = 0; };
auto add = [](auto &x) { x3::_val(x) += x3::_attr(x); };
auto const roman_def = x3::eps [init] >> numbers [add];
BOOST_SPIRIT_DEFINE(roman);
}
int main()
{
std::string input = "V";
auto iter = input.begin();
auto end = input.end();
unsigned result;
bool r = parse(iter, end, parser::roman, result);
if (r && iter == end) {
std::cout << "Success :) Result = " << result << '\n';
} else {
std::cout << "Failed :(\n";
}
}
I'd like to template the parser on the attribute type which is currently hardcoded as unsigned. My first guess was to replace
namespace parser {
// ...
}
with
template < typename int_t >
struct parser {
// ...
};
which is obviously too naïve. How to do this correctly?
In X3 there's not so much pain in combining parsers dynamically. So I'd write your sample as:
template <typename Attribute>
auto make_roman() {
using namespace boost::spirit::x3;
struct numbers_ : symbols<Attribute> {
numbers_() { this-> add
("I", Attribute{1}) ("II", Attribute{2}) ("III", Attribute{3}) ("IV", Attribute{4})
("V", Attribute{5}) ("VI", Attribute{6}) ("VII", Attribute{7}) ("VIII", Attribute{8})
("IX", Attribute{9}) ;
}
} numbers;
return rule<class roman, Attribute> {"roman"} =
eps [([](auto &x) { _val(x) = 0; })]
>> numbers [([](auto &x) { _val(x) += _attr(x); })];
}
See it Live On Coliru

Segmentation fault for nested boost::variant

The following program has been reduced from the original. I get a segmentation fault when it runs. If I remove line 24 with ArithmeticUnaryExpression then the program no longer crashes. How do I get rid of the segmentation fault?
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/spirit/include/qi_expect.hpp>
#include <boost/spirit/home/x3/directive/expect.hpp>
#include <iostream>
#include <string>
namespace wctl_parser {
namespace x3 = boost::spirit::x3;
namespace ascii = x3::ascii;
namespace qi = boost::spirit::qi;
using x3::ulong_;
using x3::lexeme;
//--- Ast structures
struct ArithmeticUnaryExpression;
using AtomicProp = std::string;
using ArithmeticExpression = x3::variant<
x3::forward_ast<ArithmeticUnaryExpression>,
unsigned long
>;
struct ArithmeticUnaryExpression {
std::string op;
ArithmeticExpression operand;
};
using Expression = x3::variant<
ArithmeticExpression
>;
template <typename T> auto rule = [](const char* name = typeid(T).name()) {
struct _{};
return x3::rule<_, T> {name};
};
template <typename T> auto as = [](auto p) { return rule<T>() = p; };
//--- Rules
x3::rule<struct aTrivRule, ArithmeticExpression> aTriv("aTriv");
x3::rule<struct exprRule, Expression> expr("expression");
auto const aTriv_def = rule<ArithmeticExpression>("aTriv")
= ulong_
// | '(' > expr > ')'
;
auto const primitive = rule<Expression>("primitive")
= aTriv
;
auto const expr_def
= primitive
;
BOOST_SPIRIT_DEFINE(aTriv)
BOOST_SPIRIT_DEFINE(expr)
auto const entry = x3::skip(ascii::space) [expr];
} //End namespace
int main() {
std::string str("prop");
namespace x3 = boost::spirit::x3;
wctl_parser::Expression root;
auto iter = str.begin();
auto end = str.end();
bool r = false;
r = parse(iter, end, wctl_parser::entry, root);
if (r) {
std::cout << "Parses OK:" << std::endl << str << std::endl;
if (iter != end) std::cout << "Partial match" << std::endl;
std::cout << std::endl << "----------------------------\n";
}
else {
std::cout << "!! Parsing failed:" << std::endl << str << std::endl << std::endl << "----------------------------\n";
}
return 0;
}
Your variant
using ArithmeticExpression = x3::variant<
x3::forward_ast<ArithmeticUnaryExpression>,
unsigned long
>;
will default-construct to the first element type. The first element type contains ArithmeticExpression which is also default constructed. Can you see the problem already?
Just make sure the default constructed state doesn't lead to infinite recursion:
using ArithmeticExpression = x3::variant<
unsigned long,
x3::forward_ast<ArithmeticUnaryExpression>
>;

boost::spirit append a vector

I have a problem with inserting data into a vector using phoenix::insert.
The code should parse input such as "(move x y z - loc r - robot item)" into a struct Predicate with name "move" and 3 variables of type loc, 1 variable of type robot and 1 variable with default type object. All those symbols are just strings not really relevant to the problem (I believe). The problem is using phoenix::insert in the definition of the rule for predicate.
Here is the code I have:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.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/spirit/include/phoenix_object.hpp>
#include <boost/spirit/home/phoenix/container.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace client {
namespace fusion = boost::fusion;
namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
struct Variable {
std::string name;
std::string type;
};
struct Predicate {
std::string name;
std::vector<Variable> vars;
};
struct TermList {
std::vector<Variable> vars;
TermList() = default;
TermList(std::vector<std::string> names, std::string type)
{
for (auto& n : names)
{
Variable t;
t.name = n;
t.type = type;
vars.push_back(t);
}
}
TermList& operator=(const TermList& rhs) = default;
TermList(const TermList& from) = default;
TermList(TermList&& from) = default;
};
}
BOOST_FUSION_ADAPT_STRUCT(
client::Variable,
(std::string, name)
(std::string, type)
)
BOOST_FUSION_ADAPT_STRUCT(
client::Predicate,
(std::string, name)
(std::vector<client::Variable>, vars)
)
BOOST_FUSION_ADAPT_STRUCT(
client::TermList,
(std::vector<client::Variable>, vars)
)
namespace client {
template <typename Iterator, typename Skipper = ascii::space_type>
struct strips_domain_grammar
: qi::grammar<Iterator, Predicate(),
qi::locals<std::vector<Variable>>, Skipper>
{
strips_domain_grammar()
: strips_domain_grammar::base_type(predicate, "predicate")
{
using qi::eps;
using qi::lit;
using qi::lexeme;
using qi::raw;
using qi::on_error;
using qi::fail;
using phoenix::at_c;
using phoenix::push_back;
using phoenix::insert;
using phoenix::begin;
using phoenix::end;
using phoenix::construct;
using phoenix::val;
using ascii::char_;
using ascii::string;
using ascii::alpha;
using ascii::alnum;
using namespace qi::labels;
// identifier such as move or ?from
identifier %= raw[lexeme[((alpha | char_('_') | char_('?'))
>> *(alnum | char_('_') | char_('-')))]];
// x | x y | x - type | x y z - type
term_list =
+(identifier [push_back(_a, _1)])
>>
(
('-' >
identifier [qi::_val = phoenix::construct<TermList>(qi::_a, qi::_1)])
|
eps [qi::_val = phoenix::construct<TermList>(qi::_a, "object")]
)
;
// (move x y z - loc r - robot item) // item is detault type - object
predicate =
char_('(')
> identifier [at_c<0>(_val) = _1]
> +(term_list [insert(at_c<1>(_val), end(at_c<1>(_val)), // <- ERROR
begin(at_c<0>(_1)), end(at_c<0>(_1)))])
> ')'
;
predicate.name("predicate");
term_list.name("term list");
identifier.name("id");
// on_error is called only when an expectation fails (> instead of >>)
on_error<fail>
(
predicate
, 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(), Skipper> identifier;
qi::rule<Iterator, TermList(),
qi::locals<std::vector<std::string>>, Skipper> term_list;
qi::rule<Iterator, Predicate(),
qi::locals<std::vector<Variable>>, Skipper> predicate;
};
} // namespace client
int main(int argc, const char** argv)
{
typedef std::string::const_iterator iterator_type;
typedef client::strips_domain_grammar<iterator_type> domain_grammar;
domain_grammar g;
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
using boost::spirit::ascii::space;
client::Predicate predicate;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
bool r = phrase_parse(iter, end, g, space, predicate);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "got: " << predicate.name;
std::cout << "\n-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
}
but the code leads to the following error (clang3.3 with libc++ and c++11; mac os x 10.8):
boost/spirit/home/phoenix/stl/container/container.hpp:416:16: error: void function 'operator()' should not return a value [-Wreturn-type]
return c.insert(arg1, arg2, arg3);
As mentioned above, I believe the error is the result of using phoenix::insert in an action in the predicate rule.
I "fixed" the problem by editing the boost header and removing the return statement, but given my limited understanding of this library I would like to avoid that...
Can someone please explain the problem or suggest a different solution?