Since both ID and STRING can match the input text starting with "driver", the lexer will choose the longest possible match, even though the ID rule comes first.
So, you have several choices here. The most direct is to remove the ambiguity between ID and STRING (which is how your alternative works) by requiring the string to start with the equals sign.
file : prop+ EOF ;
prop : ID STRING NEWLINE ;
ID : [a-zA-Z]+ ;
STRING : '=' (~[\r\n])+;
NEWLINE : '\r'?'\n' ;
You can then use an action to trim the equals sign from the text of the string token.
Alternately, you can use a predicate to disambiguate the rules.
file : prop+ EOF ;
prop : ID '=' STRING NEWLINE ;
ID : [a-zA-Z]+ ;
STRING : { isValue() }? (~[\r\n])+;
NEWLINE : '\r'?'\n' ;
where the isValue method looks backwards on the character stream to verify that it follows an equals sign. Something like:
@members {
public boolean isValue() {
int offset = _tokenStartCharIndex;
for (int idx = offset-1; idx >=0; idx--) {
String s = _input.getText(Interval.of(idx, idx));
if (Character.isWhitespace(s.charAt(0))) {
continue;
} else if (s.charAt(0) == '=') {
return true;
} else {
break;
}
}
return false;
}
}