Нет способа реализовать строку в кавычках с настраиваемыми разделителями в Antlr4

Я пытаюсь реализовать правило лексера для механизма строк в кавычках оракула Q, где у нас есть что-то вроде q '$some string$'

Здесь вы можете использовать любой символ вместо $, кроме пробела, (, {, [,<, но строка должна начинаться и заканчиваться одним и тем же символом. Некоторые примеры принятых токенов: q '! Some string!' Q 'ssome strings' Обратите внимание на то, что s - это пользовательский разделитель, но хорошо иметь его и в строке, потому что мы заканчиваем только на s '

Вот как я пытался реализовать правило:

Q_QUOTED_LITERAL: Q_QUOTED_LITERAL_NON_TERMINATED . QUOTE-> type(QUOTED_LITERAL); 

Q_QUOTED_LITERAL_NON_TERMINATED:
    Q QUOTE ~[ ({[<'"\t\n\r] { setDelimChar( (char)_input.LA(-1) ); } 
    ( . { !isValidEndDelimChar() }? )* 
;

Я уже проверил значение, которое я получаю из! IsValidEndDelimChar(), и я получаю ложный предикат здесь в нужном месте, поэтому все должно работать, но antlr просто игнорирует этот предикат. Я также попытался переместить предикат, поместив эту часть в отдельное правило и кучу других вещей, после полутора дней исследования того же самого вопроса, я наконец поднял эту проблему.

Я также пытался реализовать его другими способами, но, похоже, нет способа реализовать пользовательскую строку с разделителями в antlr4 (версия antlr3, используемая для работы).

1 ответ

Решение

Не уверен, почему { ... } действие не вызывается, но оно не нужно. Следующая грамматика сработала для меня (поставьте предикат перед .!):

grammar Test;

@lexer::members {
  boolean isValidEndDelimChar() {
    return (_input.LA(1) == getText().charAt(2)) && (_input.LA(2) == '\'');
  }
}

parse
 : .*? EOF
 ;

Q_QUOTED_LITERAL
 : 'q\'' ~[ ({[<'"\t\n\r] ( {!isValidEndDelimChar()}? . )* . '\''
 ;

SPACE
 : [ \t\f\r\n] -> skip
 ;

Если вы запускаете класс:

import org.antlr.v4.runtime.*;

public class Main {

  public static void main(String[] args) {

    Lexer lexer = new TestLexer(CharStreams.fromString("q'ssome strings' q'!foo!'"));
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    tokens.fill();

    for (Token t : tokens.getTokens()) {
      System.out.printf("%-20s %s\n", TestLexer.VOCABULARY.getSymbolicName(t.getType()), t.getText());
    }
  }
}

будет напечатан следующий вывод:

Q_QUOTED_LITERAL     q'ssome strings'
Q_QUOTED_LITERAL     q'!foo!'
EOF                  <EOF>
Другие вопросы по тегам