So many() being “zero or more” essentially means this is an optional match. Your goal seems to be making it clear that either parsing ?d was the issue if many() was indeed meant to be empty, or parsing the many() step failed for some reason. So how about:
either(
sequence([
many(
sequence([
char(?b),
char(?c),
])
),
char(?d)
]),
char(?d)
)
Which will communicate the two different states explicitly to the parser. If both branches fail the error can communicate both expectations instead of just one.






















