У меня есть грамматика запросов, над которой я работаю, и нашел один случай, который оказывается трудно решить. Ниже представлена ​​минимальная версия грамматики для ее воспроизведения.

grammar scratch;

query : command* ; // input rule

RANGE: '..';
NUMBER: ([0-9]+ | (([0-9]+)? '.' [0-9]+));
STRING: ~([ \t\r\n] | '(' | ')' | ':' | '|' | ',' | '.' )+ ;
WS: [ \t\r\n]+ -> skip ;

command
    : 'foo:' number_range                  # FooCommand
    | 'bar:' item_list                     # BarCommand
 ;

number_range: NUMBER RANGE NUMBER # NumberRange;

item_list: '(' (NUMBER | STRING)+ ((',' | '|') (NUMBER | STRING)+)* ')' # ItemList;

При использовании этого вы можете без проблем сопоставить такие вещи, как bar:(bob, blah, 57, 4.5) foo:2..4.3. Но если вы вставите bar:(bob.smith, blah, 57, 4.5) foo:2..4, он пожалуется line 1:8 token recognition error at: '.s' и разделит его на «боб» и «мит». Имеет смысл, . игнорируется как часть строки. Хотя не уверен, почему он ест 's'.

Поэтому замените строку на STRING: ~([ \t\r\n] | '(' | ')' | ':' | '|' | ',' )+ ; без точки. И теперь он распознает 2..4.3 как строку вместо диапазона_числов.

Я считаю, что это связано с тем, что строка соответствует большему количеству символов на одном отрезке, чем другие варианты. Но есть ли способ заставить STRING соответствовать только в том случае, если он еще не сопоставил элементы выше в грамматике? Это означает, что это только STRING, если он не содержит RANGE или NUMBER?

Я знаю, что могу добавить TERM: '"' .*? '"';, а затем добавить TERM в item_list, но я надеялся избежать необходимости цитировать, если это возможно. Но, похоже, это единственный способ сохранить диапазон .., который я нашел.

1
eseglem 29 Июл 2020 в 17:14

1 ответ

Лучший ответ

Вы можете разрешить только одиночные точки внутри строк, например:

STRING : ATOM+ ( '.' ATOM+ )*;

fragment ATOM : ~[ \t\r\n():|,.];

О, и NUMBER: ([0-9]+ | (([0-9]+)? '.' [0-9]+)); довольно многословен. Это делает то же самое: NUMBER : ( [0-9]* '.' )? [0-9]+;

1
Bart Kiers 29 Июл 2020 в 20:16