Сопоставление произвольного текста (как символов, так и пробелов) с ANTLR?

Как сопоставить любой текст в ANTLRv4? Я имею в виду текст, который неизвестен на момент написания грамматики?

Моя грамматика следующая:

grammar Anytext;

line :
    comment;

comment : '#' anytext;

anytext: ANY*;

WS : [ \t\r\n]+;

ANY : .;

И мой код выглядит следующим образом:

    String line = "# This_is_a_comment";

    ANTLRInputStream input = new ANTLRInputStream(line);

    AnytextLexer lexer = new AnytextLexer(input);

    CommonTokenStream tokens = new CommonTokenStream(lexer);

    AnytextParser parser = new AnytextParser(tokens);

    ParseTree tree = parser.comment();

    System.out.println(tree.toStringTree(parser)); // print LISP-style tree

Вывод следующий:

line 1:1 extraneous input ' ' expecting {<EOF>, ANY}
(comment # (anytext   T h i s _ i s _ a _ c o m m e n t))

Если я изменю ANY правило

ANY : [ \t\r\n.];

он вообще перестает распознавать любой символ.

ОБНОВЛЕНИЕ1

У меня нет символа конечной строки в конце.

ОБНОВЛЕНИЕ 2

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

Но вопрос остается.

Как тогда сопоставить все символы на уровне парсера?

Предположим, у меня есть табличные данные, и я не хочу обрабатывать одни поля и игнорировать другие. Если бы у меня было правило anytext, я бы написал

infoline :
    ( codepoint WS 'field1' WS field1Value ) |
    ( codepoint WS 'field2' WS field2Value ) |
    ( codepoint WS anytext );

здесь я анализирую строки, если 2-й столбец содержит значения field1 и field2, и игнорирую строки в противном случае.

Как осуществить этот подход?


person Suzan Cioc    schedule 11.05.2013    source источник


Ответы (2)


Важно помнить, что ANTLR разобьет весь ваш ввод на токены еще до того, как синтаксический анализатор увидит первый токен (по крайней мере, он ведет себя так). Ваша грамматика лексера выглядит следующим образом.

T__0 : '#'; // implicit token created due to the use of '#' in parser rule comment

WS : [ \t\r\n]+;

ANY : .;

Для вашего ввода токены следующие:

  1. # (тип T__0)
  2. [пробел] (введите WS)
  3. T (тип ANY)
  4. h (тип ANY)
  5. i (тип ANY)
  6. s (тип ANY)
  7. _ (тип ANY)
  8. i (тип ANY)
  9. s (тип ANY)
  10. _ (тип ANY)
  11. a (тип ANY)
  12. _ (тип ANY)
  13. c (тип ANY)
  14. o (тип ANY)
  15. m (тип ANY)
  16. m (тип ANY)
  17. e (тип ANY)
  18. n (тип ANY)
  19. t (тип ANY)

Ваша текущая грамматика не может быть проанализирована, поскольку токен WS не разрешен в правиле comment. Он проанализирует этот ввод (но может столкнуться с проблемами при расширении грамматики), если вы используете это:

// remember that '#' is its own token
anytext: (ANY | WS | '#')*;

Что вы можете сделать, так это изменить comment на правило лексера, которое использует символ # вместе со всем, что следует (в данном случае, до конца строки):

grammar Anytext;

line : COMMENT;

COMMENT : '#' ~[\r\n]*;

WS : [ \t\r\n]+;

ANY : .;
person Sam Harwell    schedule 13.05.2013
comment
Я не понимаю, почему вы написали [space] (type WS). С моей точки зрения это тоже ANY? Почему нет? - person Suzan Cioc; 13.05.2013
comment
@SuzanCioc ANTLR никогда не присваивает токену более одного типа. Символ пробела соответствует правилу WS и ANY. Чтобы устранить двусмысленность, поскольку WS появляется перед ANY в грамматике, токену присваивается тип WS. Неоднозначность разрешается, и тип токена назначается до того, как синтаксический анализатор увидит токен, поэтому синтаксический анализатор никогда не увидит токен пробела с типом ANY. - person Sam Harwell; 13.05.2013
comment
А деревья? Они тоже запрещены в лексере? Что, если я напишу WS : [ \t\r\n]; ANY : WS | .;? Будет ли пространство помечено как ANY, так и WS? - person Suzan Cioc; 13.05.2013
comment
Если это правда, то ответ такой: лексер не допускает двусмысленности и деревьев. - person Suzan Cioc; 13.05.2013

Используйте следующее правило для строковых комментариев:

LINE_COMMENT
    :   '#' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    ;

Он соответствует '#' и любому символу, пока не дойдет до конца строки (разрывы строки в unix/windows).

Отредактируйте 280Z28: вот точно такое же правило в синтаксисе ANTLR 4:

LINE_COMMENT
    :   '#' ~[\r\n]* '\r'? '\n' -> channel(HIDDEN)
    ;
person hoaz    schedule 11.05.2013
comment
Я отредактировал ваш пост, чтобы указать точно такое же правило в синтаксисе ANTLR 4. В отдельном примечании я рекомендую не включать разделитель строки '\r'? '\n' как часть самого правила LINE_COMMENT (заставить его потреблять символы до конца строки, но не включая его). Есть несколько причин, по которым я рекомендую это, но самая большая из них заключается в том, что в текущей форме LINE_COMMENT не будет соответствовать комментарию в последней строке файла, если за ним не следует явный признак конца строки. - person Sam Harwell; 11.05.2013
comment
Почему это так сложно? Можно ли написать проще? Почему мое правило не работает? - person Suzan Cioc; 11.05.2013
comment
@ 280Z28, можете ли вы дать ответ по-своему, не включая символы в конце строки? - person Suzan Cioc; 11.05.2013
comment
Когда вы используете правило .*, оно съедает разрывы строк и, таким образом, сопоставляет все до конца потока, используйте следующее, если вы не хотите включать символы конца строки: LINE_COMMENT: '#' ~[\r\n]*; - person hoaz; 12.05.2013
comment
@hoaz У меня нет символов разрыва строки в конце, смотрите код. Я разбираю строковую переменную. - person Suzan Cioc; 13.05.2013
comment
@hoaz Вы имеете в виду, что невозможно сопоставить какой-либо символ, кроме отрицательного класса? Что не так с [ \t\r\n.]? Будет ли только . соответствовать пробелам? - person Suzan Cioc; 13.05.2013
comment
Вам не нужно смешивать \t\r\n и ., потому что . все равно подходит ко всему. Если вы хотите все после фунта, используйте это: LINE_COMMENT: '#' .*; - person hoaz; 13.05.2013