Exagon
Exagon

Reputation: 5088

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.

Upvotes: 1

Views: 321

Answers (1)

sehe
sehe

Reputation: 392833

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
    

Upvotes: 2

Related Questions