Its seems that Expectations are only relevant when Spirit already detected that a rule is applicable (from the start) or else the parsing will just fail
It's a simple C-Identifier rule and a Identfier:Identifier rule skipping spaces to show my misunderstanding
// #define BOOST_SPIRIT_DEBUG
#include <boost/fusion/include/io.hpp>
#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace Ast
{
struct Identifier : std::string
{
};
struct A_B
{
Identifier a;
Identifier b;
};
} // namespace Ast
BOOST_FUSION_ADAPT_STRUCT( Ast::A_B, a, b )
using Iter = std::string_view::const_iterator;
template <typename ValueType, typename RuleType>
void printing_parse_test( const RuleType& rule_, const std::vector<std::string>& tests )
{
for( std::string_view test : tests )
{
try
{
std::cout << "Parsing: '" << test << "'\n";
Iter f = test.begin(), l = test.end();
// ">> qi::omit[*qi::space]" at end to eat in-between rest spaces ...
ValueType v;
const bool ok = qi::parse( f, l, rule_ >> qi::omit[*qi::space], v );
std::cout << "parse-result: " << ( ok ? "ok" : "failed" ) << "\n";
const std::string left( f, l );
if( ok )
{
if( !left.empty() )
{
std::cout << "FAIL: parsed someway but left this '" << left << "'\n ";
}
else
{
std::cout << "OK: all parsed\n";
}
}
else
{
if( !left.empty() )
{
std::cout << "FAIL: parse failed left this: '" << left << "'\n";
}
}
}
catch( qi::expectation_failure<Iter> const& ef )
{
auto f = begin( test );
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" << std::endl;
}
std::cout << "--------------------\n";
}
std::cout << "============\n";
}
int main()
{
qi::rule<Iter, Ast::Identifier()> identifier_rule = qi::char_( "a-zA-Z_" ) > *qi::char_( "a-zA-Z0-9_" );
identifier_rule.name( "identifier_rule" );
printing_parse_test<Ast::Identifier>( identifier_rule, { " test blib ", "", "23434", "a$", "test blub" } );
qi::rule<Iter, Ast::A_B()> A_B_rule = qi::skip( qi::space )[( identifier_rule > ":" > identifier_rule )];
A_B_rule.name( "A_B_rule" );
printing_parse_test<Ast::A_B>( A_B_rule, { "e$:bd", "a", "", "23434", "a$", "a:$" } );
return 0;
}
Output:
Parsing: ' test blib '
parse-result: failed
FAIL: parse failed left this: ' test blib '
--------------------
Parsing: ''
parse-result: failed
--------------------
Parsing: '23434'
parse-result: failed
FAIL: parse failed left this: '23434'
--------------------
Parsing: 'a$'
parse-result: ok
FAIL: parsed someway but left this '$'
--------------------
Parsing: 'test blub'
parse-result: ok
FAIL: parsed someway but left this 'blub'
--------------------
============
Parsing: 'e$:bd'
-> EXPECTED ":" in line:1 col:1
e$:bd
^--- here
--------------------
Parsing: 'a'
-> EXPECTED ":" in line:1 col:1
a
^--- here
--------------------
Parsing: ''
parse-result: failed
--------------------
Parsing: '23434'
parse-result: failed
FAIL: parse failed left this: '23434'
--------------------
Parsing: 'a$'
-> EXPECTED ":" in line:1 col:1
a$
^--- here
--------------------
Parsing: 'a:$'
-> EXPECTED <identifier_rule> in line:1 col:2
a:$
^--- here
--------------------
============
Expectation:
why do i not get an EXPECTED <identifier_rule> when parsing '23434', or empty string etc. with both rules? isn't my expectation that the string starts with an identifier? how can i archive that or is that something that will be a problem when combining my rules later?
and an additional question is why "a$" results only in ':' expected not the additional information that also identifier literals are ok (or is that more of an hint?)
You already correctly found the way to cause an expectation point.
You can further simplify by using a skipper and qi::eoi
to check that all input is consumed. I'd suggest the simplified listing:
// #define BOOST_SPIRIT_DEBUG
#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace Ast {
struct Identifier : std::string {};
struct QualifiedId { Identifier a, b; };
} // namespace Ast
BOOST_FUSION_ADAPT_STRUCT(Ast::QualifiedId, a, b)
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) {
std::cout << "======[ " << boost::core::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 = qi::parse(f, l, qi::eps > rule_ > qi::eoi, v);
else
ok = qi::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";
}
std::cout << "--------------------\n";
}
}
int main() {
qi::rule<It, Ast::Identifier()> id = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z0-9_");
qi::rule<It, Ast::QualifiedId(), qi::space_type> qualId = qi::eps > id > ":" > id;
BOOST_SPIRIT_DEBUG_NODES((id)(qualId))
run_tests(id, {" test blib ", "", "23434", "a$", "test blub"});
run_tests(qualId, {"e$:bd", "a", "", "23434", "a$", "a:$"});
}
Which prints
======[ Ast::Identifier ]======
Parsing: " test blib " -> EXPECTED <id> in line:1 col:0
test blib
^--- here
--------------------
Parsing: "" -> EXPECTED <id> in line:1 col:0
^--- here
--------------------
Parsing: "23434" -> EXPECTED <id> in line:1 col:0
23434
^--- here
--------------------
Parsing: "a$" -> EXPECTED <eoi> in line:1 col:1
a$
^--- here
--------------------
Parsing: "test blub" -> EXPECTED <eoi> in line:1 col:4
test blub
^--- here
--------------------
======[ Ast::QualifiedId ]======
Parsing: "e$:bd" -> EXPECTED ":" in line:1 col:1
e$:bd
^--- here
--------------------
Parsing: "a" -> EXPECTED ":" in line:1 col:1
a
^--- here
--------------------
Parsing: "" -> EXPECTED <id> in line:1 col:0
^--- here
--------------------
Parsing: "23434" -> EXPECTED <id> in line:1 col:0
23434
^--- here
--------------------
Parsing: "a$" -> EXPECTED ":" in line:1 col:1
a$
^--- here
--------------------
Parsing: "a:$" -> EXPECTED <id> in line:1 col:2
a:$
^--- here
--------------------