最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

c++ - having an optional at front in a expected sequence list disallows an empty string - Stack Overflow

matteradmin8PV0评论

i have a comma separated list of id pairs, empty list is also valid

a b, c d

my rule is a expected sequence of id > id

that works

but when i try to add an optional integer value in front like -qi::int_ > id > id

a b, 2 c d

the empty list is not allowed anymore and i get an expectation exception asking for an id

Example: Live on Coliru

// #define BOOST_SPIRIT_DEBUG
#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <optional>
namespace qi = boost::spirit::qi;

namespace Ast
{
    struct Identifier : std::string
    {
        using std::string::string;
        using std::string::operator=;
    };
    struct Test
    {
        std::optional<int> a;
        Identifier b, c;
    };

} // namespace Ast

BOOST_FUSION_ADAPT_STRUCT( Ast::Test, a, b, c )

using It = std::string_view::const_iterator;

template <typename Attr, typename Skipper>
void run_tests( qi::rule<It, Attr(), Skipper> const& rule_, std::vector<std::string> const& tests )
{
    using boost::core::demangle;
    std::cout << "======[ " << demangle( typeid( Attr ).name() ) << " ]======" << std::endl;
    for( std::string_view test : tests )
    {
        It f = test.begin(), l = test.end();
        try
        {
            std::cout << "Parsing: " << quoted( test ) << " -> ";
            Attr v;
            bool ok;

            if constexpr( std::is_same_v<Skipper, qi::unused_type> )
                ok = parse( f, l, qi::eps > rule_ > qi::eoi, v );
            else
                ok = phrase_parse( f, l, qi::eps > rule_ > qi::eoi, Skipper{}, v );

            std::cout << ( ok ? "OK" : "FAIL" )                          //
                      << " Remaining: " << quoted( std::string( f, l ) ) //
                      << "\n";
        }
        catch( qi::expectation_failure<It> const& ef )
        {
            auto p = ef.first - test.begin();
            auto bol = test.find_last_of( "\r\n", p ) + 1;
            auto line = std::count( f, f + bol, '\n' ) + 1;
            auto eol = test.find_first_of( "\r\n", p );

            std::cout << "EXPECTED " << ef.what_ << " in line:" << line << " col:" << ( p - bol ) << "\n"
                      << "    " << test.substr( bol, eol - bol ) << "\n"
                      << "    " << std::setw( p - bol ) << "" << "^--- here\n";
        }
    }
}

template <typename Attr>
using Rule = qi::rule<It, Attr(), qi::space_type>;

int main()
{
    qi::rule<It, Ast::Identifier()> id = qi::char_( "a-zA-Z_" ) >> *qi::char_( "a-zA-Z0-9_" );
    Rule<Ast::Test> test = -qi::int_ > id > id;
    Rule<std::vector<Ast::Test>> list = -( test % ',' );
    BOOST_SPIRIT_DEBUG_NODES( (id)(test)( list ) )

    run_tests( list, { "a b, c d", "" } );
}

Output:

Parsing: "a b, c d" -> OK Remaining: ""
Parsing: "" -> EXPECTED <id> in line:1 col:0

i don't see why adding the optional int breaks the rule of -( test % ',' ) that it can be also empty

i have a comma separated list of id pairs, empty list is also valid

a b, c d

my rule is a expected sequence of id > id

that works

but when i try to add an optional integer value in front like -qi::int_ > id > id

a b, 2 c d

the empty list is not allowed anymore and i get an expectation exception asking for an id

Example: Live on Coliru

// #define BOOST_SPIRIT_DEBUG
#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <optional>
namespace qi = boost::spirit::qi;

namespace Ast
{
    struct Identifier : std::string
    {
        using std::string::string;
        using std::string::operator=;
    };
    struct Test
    {
        std::optional<int> a;
        Identifier b, c;
    };

} // namespace Ast

BOOST_FUSION_ADAPT_STRUCT( Ast::Test, a, b, c )

using It = std::string_view::const_iterator;

template <typename Attr, typename Skipper>
void run_tests( qi::rule<It, Attr(), Skipper> const& rule_, std::vector<std::string> const& tests )
{
    using boost::core::demangle;
    std::cout << "======[ " << demangle( typeid( Attr ).name() ) << " ]======" << std::endl;
    for( std::string_view test : tests )
    {
        It f = test.begin(), l = test.end();
        try
        {
            std::cout << "Parsing: " << quoted( test ) << " -> ";
            Attr v;
            bool ok;

            if constexpr( std::is_same_v<Skipper, qi::unused_type> )
                ok = parse( f, l, qi::eps > rule_ > qi::eoi, v );
            else
                ok = phrase_parse( f, l, qi::eps > rule_ > qi::eoi, Skipper{}, v );

            std::cout << ( ok ? "OK" : "FAIL" )                          //
                      << " Remaining: " << quoted( std::string( f, l ) ) //
                      << "\n";
        }
        catch( qi::expectation_failure<It> const& ef )
        {
            auto p = ef.first - test.begin();
            auto bol = test.find_last_of( "\r\n", p ) + 1;
            auto line = std::count( f, f + bol, '\n' ) + 1;
            auto eol = test.find_first_of( "\r\n", p );

            std::cout << "EXPECTED " << ef.what_ << " in line:" << line << " col:" << ( p - bol ) << "\n"
                      << "    " << test.substr( bol, eol - bol ) << "\n"
                      << "    " << std::setw( p - bol ) << "" << "^--- here\n";
        }
    }
}

template <typename Attr>
using Rule = qi::rule<It, Attr(), qi::space_type>;

int main()
{
    qi::rule<It, Ast::Identifier()> id = qi::char_( "a-zA-Z_" ) >> *qi::char_( "a-zA-Z0-9_" );
    Rule<Ast::Test> test = -qi::int_ > id > id;
    Rule<std::vector<Ast::Test>> list = -( test % ',' );
    BOOST_SPIRIT_DEBUG_NODES( (id)(test)( list ) )

    run_tests( list, { "a b, c d", "" } );
}

Output:

Parsing: "a b, c d" -> OK Remaining: ""
Parsing: "" -> EXPECTED <id> in line:1 col:0

i don't see why adding the optional int breaks the rule of -( test % ',' ) that it can be also empty

Share Improve this question edited Nov 18, 2024 at 14:41 llm asked Nov 18, 2024 at 13:19 llmllm 7496 silver badges21 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

You have to tell -qi::int_ to NOT match if nothing follows:

Rule<Ast::Test>  test = (!qi::eoi >> -qi::int_) > id > id;

See Live

Printing

Parsing: "a b, 2 c d" -> OK Remaining: ""
Parsing: "" -> OK Remaining: ""

Alternatively, be less overstrict with expectations. After all, your expectation WASN'T "if no integer was matched, require an id":

Rule<Ast::Test> test = -qi::int_ >> id >> id;

Same output, Live

Post a comment

comment list (0)

  1. No comments so far