Boost spirit x3 - how to parse nested structures? - c++

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);

Related

Using boost-spirit to parse string to struct of QStrings

Today, I tried to write a very simple parser using boost-spirit.
Now, I stumbled over an obstacles, that I really don't know how to resolve.
Basically, I wanted the semantic action to return a QString instead of the Stl std::string. And I also wanted to do this conversion in a very simple manner right in the semantic action, as there will be more an more QString objects to come by. The final goal is to pack all these QStrings in a common struct.
(QString is the Qt-Framework equivalent of an std::string).
This is what I got so far.
#include <QString>
#include <vector>
#include <iostream>
#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/phoenix/object/construct.hpp>
class QStringHelper {
public:
QStringHelper() {}
QStringHelper(const std::string& string) : mString(QString::fromStdString(string)){
}
// This operator might help?
QString operator()() const {
return mString;
}
QString mString;
};
template <typename Iterator, typename Skipper>
struct grammar : boost::spirit::qi::grammar<Iterator, void(), Skipper>
{
QString color;
std::string stdColor;
QStringHelper helper;
grammar() : grammar::base_type(listOfProperties)
{
namespace fusion = boost::fusion;
namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using qi::lit;
using qi::lexeme;
using ascii::char_;
using ascii::string;
using boost::phoenix::ref;
using namespace qi::labels;
listOfProperties = (colorProperty % lit(";"));
// 1. Works, but is not what I desire!
// colorProperty = lit("color") >> lit(":") >> qi::as_string[lexeme[+(char_ - ';')]][boost::phoenix::ref(stdColor) =_1];
// 2. Cannot compile, as QString has not constructor like QString(const std::string&)
// colorProperty = lit("color") >> lit(":") >> qi::as_string[lexeme[+(char_ - ';')]][boost::phoenix::ref(color) = boost::phoenix::construct<QString>(_1)];
// 3. Does compile, but is not exactly what I desire!
colorProperty = lit("color") >> lit(":") >> qi::as_string[lexeme[+(char_ - ';')]][boost::phoenix::ref(helper) = boost::phoenix::construct<QStringHelper>(_1)];
// 4. My desired solution, that I really don't know what someMagicExpression might be?
// colorProperty = lit("color") >> lit(":") >> qi::as_string[lexeme[+(char_ - ';')]][boost::phoenix::ref(color) = someMagicExpression(_1)];
// Many more complex properties will go by!
}
boost::spirit::qi::rule<Iterator, void(), Skipper> listOfProperties;
boost::spirit::qi::rule<Iterator, void(), Skipper> colorProperty;
};
int main(int argc, char**args) {
try {
std::string stringToParse = "color:#ff00ff";
boost::spirit::qi::space_type space;
grammar<std::string::const_iterator, decltype(space)> parser;
auto b = stringToParse.begin();
auto e = stringToParse.end();
bool ok = boost::spirit::qi::phrase_parse(b, e, parser, space);
if (b != e) {
return EXIT_SUCCESS;
}
else {
std::cout << "Color: " << parser.color.toStdString();
}
return EXIT_SUCCESS;
}
catch (const boost::spirit::qi::expectation_failure<std::string::iterator>&) {
return EXIT_FAILURE;
}
}

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

Synthesized attributes of partial matches from alternatives

When I remove x3::eps in the below rule, the string result from the first partial match is still in the second match, resulting in a string with duplicated content.
If I add another case in between I still only get 1 duplicate instead of two.
Why do I need to use x3::eps, or, what am I misunderstanding in the evaluation of rules and synthesized attributes?
Should I use lookahead instead?
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace x3 = boost::spirit::x3;
namespace ascii = x3::ascii;
struct AstChannel {
std::string label;
bool complement;
};
x3::rule<class AstLabel, std::string> const astLabel = "astLabel";
auto const astLabel_def = ascii::lower >> *(ascii::alnum);
BOOST_SPIRIT_DEFINE(astLabel)
x3::rule<class AstChannel, AstChannel> const astChannel = "astChannel";
auto const astChannel_def = astLabel >> '!' >> x3::attr(true)
| astLabel >> x3::eps >> x3::attr(false) ;
BOOST_SPIRIT_DEFINE(astChannel)
BOOST_FUSION_ADAPT_STRUCT(
AstChannel,
(std::string, label)
(bool, complement)
)
int main() {
std::string str("hello");
auto iter = str.begin();
auto end = str.end();
AstChannel channel;
bool r = phrase_parse(iter, end, astChannel, ascii::space, channel);
if (r) {
std::cout << channel.label << ',' << channel.complement << std::endl;
}
return 0;
}
The real answer is: force atomic attribute propagation of container attributes (e.g. with x3::hold or semantic actions).
The better answer is: use x3::matches:
Live On Coliru
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
using namespace std::string_literals;
struct AstChannel {
std::string label;
bool complement;
};
BOOST_FUSION_ADAPT_STRUCT(AstChannel, label, complement)
namespace Grammar {
namespace x3 = boost::spirit::x3;
namespace ascii = x3::ascii;
auto const label = x3::rule<struct labelRule, std::string> {"label" }
= x3::lexeme[ascii::lower >> *(ascii::alnum)];
auto const channel = label >> x3::matches['!'];
auto const entry = x3::skip(ascii::space) [ channel ];
}
int main() {
auto const str = "hello"s;
AstChannel channel;
if (parse(str.begin(), str.end(), Grammar::entry, channel)) {
std::cout << channel.label << ',' << std::boolalpha << channel.complement << "\n";
}
}
Prints
hello,false

Boost spirit x3 parser throws std::logic_error while parsing

I created a grammar using boost spirit x3. While testing my resulting parser, i recognized that there is a case where the parser throws the following exeption:
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_M_construct null not valid
I didnt know this could happen using boost spirit x3, I thought the parse method eighter returns false or throws a boost::spirit::x3::expectation_failure, what am I doing wrong in my grammar, because this should not happen here.
I uploaded my mcve HERE, if you execute the binary the std::logic error will bw thrown.
example.cpp
#include "example_def.hpp"
#include "config.hpp"
namespace client { namespace parser {
BOOST_SPIRIT_INSTANTIATE(var_dec_type, iterator_type, context_type)
}}
main.cpp
#include <iostream>
//#define SINGLE_TU
#if defined(SINGLE_TU)
#pragma message "yesdef(SINGLE_TU)"
#else
#pragma message "notdef(SINGLE_TU)"
#endif
#ifdef SINGLE_TU
#include "types_def.hpp"
#include "example_def.hpp"
#else
#include "example.hpp"
#endif
template<typename Parser, typename Attribute>
bool test(const std::string& str, Parser&& p, Attribute&& attr)
{
using iterator_type = std::string::const_iterator;
iterator_type in = str.begin();
iterator_type end = str.end();
bool ret = boost::spirit::x3::phrase_parse(in, end, p, boost::spirit::x3::ascii::space, attr);
ret &= (in == end);
return ret;
}
int main()
{
client::ast::VariableDec attr;
std::cout << test("var foo : foo<bar, baz<bahama>>", client::var_dec() , attr);
return 0;
}
types.cpp
#include "config.hpp"
#include "types_def.hpp"
#include <iostream>
namespace client {namespace parser {
BOOST_SPIRIT_INSTANTIATE(type_type, iterator_type, context_type);
BOOST_SPIRIT_INSTANTIATE(class_type_type, iterator_type, context_type);
#define PARSE_RULE_BODY
#if defined(PARSE_RULE_BODY)
#pragma message "yesdef(PARSE_RULE_BODY)"
//This causes the parse_rule generated by
//BOOST_SPIRIT_DEFINE for type
//to *not* be used.
#include <boost/fusion/iterator/deref.hpp>
#else
#pragma message "notdef(PARSE_RULE_BODY)"
//This causes the parse_rule generated by
//BOOST_SPIRIT_DEFINE for type
//to be used.
#endif
#define SEHE_TYPES_DEF_HPP_PASTE
#define EXPLICT_SPECIALIZATION_FROM_LINKER_ERROR_MSG
#if defined(SEHE_TYPES_DEF_HPP_PASTE)
#pragma message "yesdef(SEHE_TYPES_DEF_HPP_PASTE)"
//Source:
// The following code was copy&pasted&reformatted from lines 38-53 of:
// https://github.com/sehe/linker_error_example/blob/explicit_instantiation/types_def.hpp
// on about 2016-11-15.
// with minor modifications:
// * reformatting
// * Added body with:
// * printing first..last.
//=======================
namespace {
template <typename Seq, size_t N>
using field_at =
boost::fusion::basic_iterator
< boost::fusion::struct_iterator_tag
, boost::fusion::random_access_traversal_tag
, Seq
, N
>;
template <typename Seq, size_t N, size_t M>
using fields =
boost::fusion::iterator_range<field_at<Seq, N>, field_at<Seq, M> >;
using Attributes =
fields<client::ast::VariableDec, 1, 2>;
using Context =
x3::context
< x3::skipper_tag
, x3::char_class<boost::spirit::char_encoding::ascii, x3::space_tag> const
, x3::unused_type
>;
}
template
#ifdef PARSE_RULE_BODY
<>
#endif
bool parse_rule
< std::string::const_iterator
, Context
, Attributes
>(
decltype(type) rule,
std::string::const_iterator &first,
std::string::const_iterator const &last,
Context const &context,
Attributes &attr)
#ifndef PARSE_RULE_BODY
;
#else
{
std::cout<<"***in function="<<__func__<<"\n";
std::string::const_iterator beg=first;
std::cout<<":input=\n{";
for(; beg!=last; ++beg) std::cout<<*beg;
std::cout<<"}\n";
using required_t=type_type::attribute_type;
required_t required_v=boost::fusion::deref(attr.first);
//attr type required by parse_rule generated by:
// BOOST_SPIRIT_INSTANTIATE(type_type, iterator_type, context_type)
;
bool result=parse_rule(rule,first,last,context,required_v)
//This should call the parse_rule generated by:
// BOOST_SPIRIT_INSTANTIATE(type_type, iterator_type, context_type)
;
std::cout<<":result="<<result<<"\n";
return result;
}
#endif
#elif defined(EXPLICT_SPECIALIZATION_FROM_LINKER_ERROR_MSG)
#pragma message "yesdef(EXPLICT_SPECIALIZATION_FROM_LINKER_ERROR_MSG)"
template
#ifdef PARSE_RULE_BODY
<>
#endif
//The following simply copied&pasted from an earlier linker error
//message, and then reformatted to clarify what
//was being specialized.
bool
parse_rule
< __gnu_cxx::__normal_iterator
< char const*
, std::__cxx11::basic_string
< char
, std::char_traits<char>
, std::allocator<char>
>
>
, boost::spirit::x3::context
< boost::spirit::x3::skipper_tag
, boost::spirit::x3::char_class
< boost::spirit::char_encoding::ascii
, boost::spirit::x3::space_tag
> const
, boost::spirit::x3::unused_type
>
, boost::fusion::iterator_range
< boost::fusion::basic_iterator
< boost::fusion::struct_iterator_tag
, boost::fusion::random_access_traversal_tag
, client::ast::VariableDec
, 1
>
, boost::fusion::basic_iterator
< boost::fusion::struct_iterator_tag
, boost::fusion::random_access_traversal_tag
, client::ast::VariableDec
, 2
>
>
>
( boost::spirit::x3::rule//==decltype(type) where types from types_def.hpp:16
< client::parser::type_class//types.hpp:15
, boost::spirit::x3::variant
< client::ast::nil
, boost::spirit::x3::forward_ast
< client::ast::LambdaType
>
, boost::spirit::x3::forward_ast
< client::ast::ClassType
>
>
, false
>
, __gnu_cxx::__normal_iterator//==std::string::const_iterator
< char const*
, std::__cxx11::basic_string
< char
, std::char_traits<char>
, std::allocator<char>
>
>& first
, __gnu_cxx::__normal_iterator//==std::string::const_iterator
< char const*
, std::__cxx11::basic_string
< char
, std::char_traits<char>
, std::allocator<char>
>
> const& last
, boost::spirit::x3::context//=?Context from #if defined(SEHE_TYPES_DEF_HPP_PASTE)
< boost::spirit::x3::skipper_tag
, boost::spirit::x3::char_class
< boost::spirit::char_encoding::ascii
, boost::spirit::x3::space_tag
> const
, boost::spirit::x3::unused_type
> const& context
, boost::fusion::iterator_range//=?Attributes from #if defined(SEHE_TYPES_DEF_HPP_PASTE)
< boost::fusion::basic_iterator
< boost::fusion::struct_iterator_tag
, boost::fusion::random_access_traversal_tag
, client::ast::VariableDec
, 1
>
, boost::fusion::basic_iterator
< boost::fusion::struct_iterator_tag
, boost::fusion::random_access_traversal_tag
, client::ast::VariableDec
, 2
>
>& attr
)
#ifndef PARSE_RULE_BODY
;
#else
{
std::cout<<"***in function="<<__func__<<"\n";
std::string::const_iterator beg=first;
std::cout<<":input=\n{";
for(; beg!=last; ++beg) std::cout<<*beg;
std::cout<<"}\n";
return false;
}
#endif//PARSE_RULE_BODY
#endif//(SEHE_TYPES_DEF_HPP_PASTE)
}}
ast.hpp
//
// Created by lukas on 11.11.16.
//
#ifndef LINKER_ERROR_EXAMPLE_AST_HPP_HPP
#define LINKER_ERROR_EXAMPLE_AST_HPP_HPP
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <vector>
#include <string>
namespace client { namespace ast {
namespace x3 = boost::spirit::x3;
struct LambdaType;
struct ClassType;
struct nil{};
typedef x3::variant <
nil,
x3::forward_ast<LambdaType>,
x3::forward_ast<ClassType>
> Type;
struct LambdaType {
std::vector<Type> parameters_;
Type return_type_;
};
struct ClassType {
std::vector<std::string> name_;
std::vector<Type> template_args_;
};
struct VariableDec {
std::string _name;
Type _type;
};
}}
BOOST_FUSION_ADAPT_STRUCT(client::ast::LambdaType, parameters_, return_type_)
BOOST_FUSION_ADAPT_STRUCT(client::ast::ClassType, name_, template_args_)
BOOST_FUSION_ADAPT_STRUCT(client::ast::VariableDec, _name, _type)
#endif //LINKER_ERROR_EXAMPLE_AST_HPP_HPP
config.hpp
#ifndef LINKER_ERROR_EXAMPLE_CONFIG_HPP
#define LINKER_ERROR_EXAMPLE_CONFIG_HPP
#include <boost/spirit/home/x3.hpp>
namespace client{
namespace parser{
namespace x3 = boost::spirit::x3;
typedef std::string::const_iterator iterator_type;
typedef x3::phrase_parse_context<x3::ascii::space_type>::type context_type;
}
}
#endif //LINKER_ERROR_EXAMPLE_CONFIG_HPP
example_def.hpp
#ifndef LINKER_ERROR_EXAMPLE_EXAMPLE_DEF_HPP
#define LINKER_ERROR_EXAMPLE_EXAMPLE_DEF_HPP
#include "ast.hpp"
#include "example.hpp"
#include "types.hpp"
namespace client { namespace parser {
#ifndef SINGLE_TU
namespace { const auto& type = client::type(); }
#endif
const var_dec_type var_dec = "var_dec";
#define EXAMPLE_DEF_LINK_ERR
#if defined(EXAMPLE_DEF_LINK_ERR)
#pragma message "yesdef(EXAMPLE_DEF_LINK_ERR)"
#else
#pragma message "notdef(EXAMPLE_DEF_LINK_ERR)"
#endif
auto const var_dec_def = x3::lexeme["var "]
> +x3::alnum
> ":"
#ifdef EXAMPLE_DEF_LINK_ERR
>> type //<- this gets linker error.
#else
> type //<- This links.
#endif
> ";";
BOOST_SPIRIT_DEFINE(
var_dec
)
}}
namespace client {
const parser::var_dec_type& var_dec()
{
return parser::var_dec;
}
}
#endif //LINKER_ERROR_EXAMPLE_EXAMPLE_DEF_HPP
example.hpp
#ifndef LINKER_ERROR_EXAMPLE_EXAMPLE_HPP
#define LINKER_ERROR_EXAMPLE_EXAMPLE_HPP
#include <boost/spirit/home/x3.hpp>
#include "ast.hpp"
namespace client { namespace parser {
namespace x3 = boost::spirit::x3;
class var_dec_class {};
typedef x3::rule<var_dec_class, ast::VariableDec> var_dec_type;
BOOST_SPIRIT_DECLARE(var_dec_type)
}}
namespace client {
const parser::var_dec_type& var_dec();
}
#endif //LINKER_ERROR_EXAMPLE_EXAMPLE_HPP
types_def.hpp
#ifndef KYLE_TYPES_DEF_HPP
#define KYLE_TYPES_DEF_HPP
#include "types.hpp"
namespace client { namespace parser {
namespace x3 = boost::spirit::x3;
typedef x3::rule<struct lambda_type_class, ast::LambdaType> lambda_type_type;
const class_type_type class_type = "class_type";
const lambda_type_type lambda_type = "lambda_type";
const type_type type = "type";
auto const identifier = +x3::alnum;
auto const type_def =
(lambda_type | class_type);
auto const lambda_type_def =
("(" > -(type % ",") > ")" > "=>" > type)
| (x3::repeat(1)[class_type] >> "=>" > type);
auto const class_type_def =
(identifier % "::") >> -("<" > type % "," > ">");
BOOST_SPIRIT_DEFINE(
lambda_type,
class_type,
type
)
}}
namespace client {
const parser::class_type_type& class_type()
{
return parser::class_type;
}
const parser::type_type& type()
{
return parser::type;
}
}
#endif //KYLE_TYPES_DEF_HPP
types.hpp
#ifndef KYLE_PARSER_TYPES_HPP
#define KYLE_PARSER_TYPES_HPP
#include <boost/spirit/home/x3.hpp>
#include "ast.hpp"
namespace client { namespace parser {
namespace x3 = boost::spirit::x3;
struct class_type_class;
struct type_class;
typedef x3::rule<class_type_class, ast::ClassType> class_type_type;
typedef x3::rule<type_class, ast::Type> type_type;
BOOST_SPIRIT_DECLARE(class_type_type,
type_type)
}}
namespace client {
const parser::class_type_type& class_type();
const parser::type_type& type();
}
#endif
It throws an expectation_failed exception.
Quite obviously it's because you have a missing ;.
After cleaning out the horrendous kludges that were related to the old linker-error workarounds, I simply changed the example_def expression to:
auto const var_dec_def = x3::lexeme["var "] > +x3::alnum >> (":" > type) > ";";
Now, add proper error handling:
int main()
{
client::ast::VariableDec attr;
std::string const input("var foo : foo<bar, baz<bahama>>;");
try {
std::cout << test(input, client::var_dec() , attr);
} catch(boost::spirit::x3::expectation_failure<std::string::const_iterator> const& e) {
std::cout << "Expected: " << e.which() << " at '" << std::string(e.where(), input.end()) << "'\n";
return 255;
}
}
Prints:
1
Leaving out the trailing ; prints:
Expected: ';' at ''
Full demo: error-handling branch
CMakeLists.txt | 6 +-
example.cpp | 9 +++
example_def.hpp | 33 +--------
main.cpp | 20 ++----
types.cpp | 203 ++++----------------------------------------------------
types_def.hpp | 20 ++----
6 files changed, 38 insertions(+), 253 deletions(-)
example.cpp
#include "example_def.hpp"
#include "config.hpp"
namespace client { namespace parser {
BOOST_SPIRIT_INSTANTIATE(var_dec_type, iterator_type, context_type)
}}
namespace client {
const parser::var_dec_type& var_dec()
{
return parser::var_dec;
}
}
main.cpp
#include <iostream>
#include "example.hpp"
template<typename Parser, typename Attribute>
bool test(const std::string& str, Parser&& p, Attribute&& attr)
{
using iterator_type = std::string::const_iterator;
iterator_type in = str.begin();
iterator_type end = str.end();
bool ret = boost::spirit::x3::phrase_parse(in, end, p, boost::spirit::x3::ascii::space, attr);
ret &= (in == end);
return ret;
}
int main()
{
client::ast::VariableDec attr;
std::string const input("var foo : foo<bar, baz<bahama>>");
try {
std::cout << test(input, client::var_dec() , attr);
} catch(boost::spirit::x3::expectation_failure<std::string::const_iterator> const& e) {
std::cout << "Expected: " << e.which() << " at '" << std::string(e.where(), input.end()) << "'\n";
return 255;
}
}
types.cpp
#include "config.hpp"
#include "types_def.hpp"
namespace client {namespace parser {
BOOST_SPIRIT_INSTANTIATE(type_type, iterator_type, context_type);
BOOST_SPIRIT_INSTANTIATE(class_type_type, iterator_type, context_type);
}}
namespace client {
const parser::class_type_type& class_type()
{
return parser::class_type;
}
const parser::type_type& type()
{
return parser::type;
}
}
ast.hpp
//
// Created by lukas on 11.11.16.
//
#ifndef LINKER_ERROR_EXAMPLE_AST_HPP_HPP
#define LINKER_ERROR_EXAMPLE_AST_HPP_HPP
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <vector>
#include <string>
namespace client { namespace ast {
namespace x3 = boost::spirit::x3;
struct LambdaType;
struct ClassType;
struct nil{};
typedef x3::variant <
nil,
x3::forward_ast<LambdaType>,
x3::forward_ast<ClassType>
> Type;
struct LambdaType {
std::vector<Type> parameters_;
Type return_type_;
};
struct ClassType {
std::vector<std::string> name_;
std::vector<Type> template_args_;
};
struct VariableDec {
std::string _name;
Type _type;
};
}}
BOOST_FUSION_ADAPT_STRUCT(client::ast::LambdaType, parameters_, return_type_)
BOOST_FUSION_ADAPT_STRUCT(client::ast::ClassType, name_, template_args_)
BOOST_FUSION_ADAPT_STRUCT(client::ast::VariableDec, _name, _type)
#endif //LINKER_ERROR_EXAMPLE_AST_HPP_HPP
config.hpp
#ifndef LINKER_ERROR_EXAMPLE_CONFIG_HPP
#define LINKER_ERROR_EXAMPLE_CONFIG_HPP
#include <boost/spirit/home/x3.hpp>
namespace client{
namespace parser{
namespace x3 = boost::spirit::x3;
typedef std::string::const_iterator iterator_type;
typedef x3::phrase_parse_context<x3::ascii::space_type>::type context_type;
}
}
#endif //LINKER_ERROR_EXAMPLE_CONFIG_HPP
example_def.hpp
#ifndef LINKER_ERROR_EXAMPLE_EXAMPLE_DEF_HPP
#define LINKER_ERROR_EXAMPLE_EXAMPLE_DEF_HPP
#include "ast.hpp"
#include "example.hpp"
#include "types.hpp"
namespace client { namespace parser {
namespace { const auto& type = client::type(); }
const var_dec_type var_dec = "var_dec";
auto const var_dec_def = x3::lexeme["var "] > +x3::alnum >> (":" > type) > ";";
BOOST_SPIRIT_DEFINE(var_dec)
}}
#endif //LINKER_ERROR_EXAMPLE_EXAMPLE_DEF_HPP
example.hpp
#ifndef LINKER_ERROR_EXAMPLE_EXAMPLE_HPP
#define LINKER_ERROR_EXAMPLE_EXAMPLE_HPP
#include <boost/spirit/home/x3.hpp>
#include "ast.hpp"
namespace client { namespace parser {
namespace x3 = boost::spirit::x3;
class var_dec_class {};
typedef x3::rule<var_dec_class, ast::VariableDec> var_dec_type;
BOOST_SPIRIT_DECLARE(var_dec_type)
}}
namespace client {
const parser::var_dec_type& var_dec();
}
#endif //LINKER_ERROR_EXAMPLE_EXAMPLE_HPP
types_def.hpp
#ifndef KYLE_TYPES_DEF_HPP
#define KYLE_TYPES_DEF_HPP
#include "types.hpp"
namespace client { namespace parser {
namespace x3 = boost::spirit::x3;
typedef x3::rule<struct lambda_type_class, ast::LambdaType> lambda_type_type;
const class_type_type class_type = "class_type";
const lambda_type_type lambda_type = "lambda_type";
const type_type type = "type";
auto const identifier = +x3::alnum;
auto const type_def =
(lambda_type | class_type);
auto const lambda_type_def =
("(" > -(type % ",") > ")" > "=>" > type)
| x3::repeat(1)[class_type] >> "=>" > type;
auto const class_type_def =
(identifier % "::") >> -("<" > type % "," > ">");
BOOST_SPIRIT_DEFINE(
lambda_type,
class_type,
type
)
}}
#endif //KYLE_TYPES_DEF_HPP
types.hpp
#ifndef KYLE_PARSER_TYPES_HPP
#define KYLE_PARSER_TYPES_HPP
#include <boost/spirit/home/x3.hpp>
#include "ast.hpp"
namespace client { namespace parser {
namespace x3 = boost::spirit::x3;
struct class_type_class;
struct type_class;
typedef x3::rule<class_type_class, ast::ClassType> class_type_type;
typedef x3::rule<type_class, ast::Type> type_type;
BOOST_SPIRIT_DECLARE(class_type_type,
type_type)
}}
namespace client {
const parser::class_type_type& class_type();
const parser::type_type& type();
}
#endif

Boost Spirit X3 and std::unordered_map

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.