Adding syntactic predicates does not solve the ambiguities in your grammar. The problem is that the token stream which is provided by the lexer does not contain any information whether one group is complete or not. In your conrete case there is the rule As: 'a'+;. It describes a list of at least one or more letters a. Each upcomming a is consumed but so called Whitespaces (e.g. simple whitespace, tab or linebreak) between these a's will be ignored. The parser will continue to put the next upcomming a to this rule until an other letter (the b) will follow in the token stream.
The problem with your grammar is that more then one list of a's can be parsed in your grammar. Example:
a
aa
aaa
is a valid input for your grammar, but the parser does not know that the first As rule ends after the a in the first line. This is because the linebreak is a whitespace that is ignored! For the parser this input of three As bloks looks equal to one As block aaaaaa.
Introducing a syntactic predicate does not solve this problem! For my opinion there are only two resolutions for your problem.
- Provide some 'syntactic sugar' e.g. by closing an
As rule with a semicolon or any other sign. The rule would look like this: As: 'a'+ ';';
Take care that you prevent whitespaces between a word of a's and ensure that at least one whitespace occure after a word:
Group: content = (As | Bs) WS+;
As hidden(): 'a'+;
Bs hidden(): 'b'+;
Then, your grammar will compile and behave like intended.