c++boostc++17boost-spiritboost-spirit-x3

enable the correct compilation of this Spirit x3 c++project


I am practicing using Spirit x3 to parse C language and store it in a custom data structure.

But I encountered huge difficulties and still couldn't compile after 5 days.

Most of the errors prompted are incomplete structures and cannot be assigned.

But I carefully checked that there were no incomplete data structures.

For example, the following structure cannot be converted: enter image description here

Who accepts this challenge to help modify the code and make it compile。 The code of the git repository can be cloned and modified at will:

git code

C language BNF


Solution

  • But I carefully checked that there were no incomplete data structures.

    That's not what the complaints are about. It says "incompatible", not "incomplete". There are things that do not convert.

    Using this adhoc CMakeLists.txt

    project(cc)
    cmake_minimum_required(VERSION 3.22.0)
    set(BOOST_ROOT /home/sehe/custom/superboost)
    find_package(Boost 1.82.0 REQUIRED)
    
    set(CMAKE_CXX_COMPILER g++-13)
    set(CMAKE_CXX_STANDARD 23)
    add_executable(cc main.cpp)
    target_link_libraries(cc Boost::boost)
    

    After a few cleanup/review commits I ended up with the first propagation related error for

    auto const init_declarator_def = ((declarator >> '=' >> initializer) | declarator);
    

    You can tell from the rule tag in the error message here:

    rule_def.hpp|214 col 5| required from ‘bool parse::parse_rule(boost::spirit::x3::detail::rule_id<init_declarator_class>, Iterator&, const Iterator&, const Context&, boost::spirit::x3::rule<?>::attribute_type&) [with Iterator = const char*; Context = boost::spirit::x3::context<?>; boost::spirit::x3::rule<?>::attribute_type = ast::test::init_declarator]’

    The AST being:

    struct init_declarator_1 {
        x3::forward_ast<declarator> pre;
        initializer                 last;
    };
    struct init_declarator : x3::variant<init_declarator_1, x3::forward_ast<declarator>> {
        using base_type::base_type;
        using base_type::operator=;
    };
    

    I can see how you expect that to match, but Spirit can't:

    /home/sehe/custom/superboost/libs/spirit/include/boost/spirit/home/x3/support/ast/variant.hpp|184 col 17| error: no match for ‘operator=’ (operand types are ‘boost::spirit::x3::variant<ast::test::init_declarator_1, boost::spirit::x3::forward_ast<ast::test::declarator> >::variant_type’ and ‘boost::fusion::deque<ast::test::declarator, ast::test::initializer>’)

    How can you assign fusion::deque<...> to init_declarator_1? I know you adapted it, but X3 has no contextual clue what to synthesize. Make the rule explicit:

    auto const init_declarator_def = ( //
        (x3::rule<struct _, ast::test::init_declarator_1>{"init_declarator_1"} =
             declarator >> '=' >> initializer) //
        | declarator                           //
    );
    

    Now it compiles, and the next hiccup is in selection_statement_class. Rinse and repeat from here.

    Summary

    Debug your attribute compatibility. Ideally, start with SMALL subgrammars, only preceed to the higher level productions IFF the lower-level productions all compile and propagate correctly.

    You may note that this is a basic tenet of test-driven programming. Really you cannot expect to grind your way through the error novels if you don't limit the scope to look for context.

    BONUS

    Note that I tend to use a type-coercion facility to make the above easier:

    namespace {
        template <typename T>
        struct as_type {
            template <typename Expr>
                auto operator[](Expr&& expr) const {
                    return x3::rule<struct _, T>{"as"} = x3::as_parser(std::forward<Expr>(expr));
                }
        };
    
        template <typename T> static const as_type<T> as = {};
    }
    

    Now you can write the above anonymous rule like

    auto const init_declarator_def                                           //
        = as<ast::test::init_declarator_1>[declarator >> '=' >> initializer] //
        | declarator;
    

    When you go and fix the selection_statement please do yourself a favour and stop using inscrutable names like selection_statement_1. We are humans, not code generators.

    Really what you need is rules like:

    auto selection_statement = if_statement | switch_statement;
    
    auto if_statement = "id" >> '(' >> expression >> ')' >> statement >> -("else" >> statement); 
    

    And the AST to match:

    struct if_statement {
         expression          cond;
         statement           true_branch;
         optional<statement> false_branch;
    };
    

    PEG grammar isn't BNF. Also, Spirit is an eDSL for parser specification. Your goal is not a machine translation but a maintainable, productive parser generator.