Antlr4 / Java: как создать семантический предикат, который пропускает токен (лексер) в соответствии с правилом синтаксического анализатора, которое его вызывает

#java #python #antlr4

#java #python #antlr4

Вопрос:

Я хотел бы использовать свое правило лексера

 NEW_LINE : 'n' -> skip;
  

Как обычное правило. Понимание под этим: я хочу игнорировать новые строки, за исключением случаев, когда они обязательны, чтобы создать синтаксис, аналогичный Python. Например, здесь новые строки игнорируются:

 cook("banana",
     "potatoe)
  

но невозможно пропустить новую строку для нового оператора, подобного этому:

 cook("banana", "potatoe") varA = 12.4
  

между cook() и присваиванием должна быть новая строка. Вот почему мне иногда приходится пропускать новые строки, но все равно заставлять их где-то в другом месте.

Вот почему у меня возникла эта идея:

 start
    : line*
    ;

line
    : line_expression (NEW_LINE | EOF)
    ;

line_expression
    : expression
    | assignment
    ;

expression
    : Decimal
    | Integer
    | Text
    | Boolean
    ;
  

И создайте семантический предикат типа «если вызывающее правило синтаксического анализатора не является строкой, skip(); это».
Теперь мне просто нужна помощь, чтобы сделать это.

Я надеюсь, что я был ясен!

PS: Я использую Java в качестве основного языка, если это было непонятно

Ответ №1:

Вы могли бы отслеживать количество ( встречаемых вами (и уменьшать это число, если вы сталкиваетесь с ) ). Затем вы создаете NL токены только в том случае, если это число равно нулю.

Вот краткая демонстрация:

 grammar T;

@lexer::members {
  int parensLevel = 0;
}

parse
 : .*? EOF
 ;

OPAR    : '(' {parensLevel  ;};
CPAR    : ')' {parensLevel--;};
NUMBER  : [0-9]  ( '.' [0-9] )?;
STRING  : '"' ~'"'* '"';
ASSIGN  : '=';
COMMA   : ',';
ID      : [a-zA-Z] ;
SPACES  : [ t]  -> skip;
NL      : {parensLevel == 0}? [rn] ;
NL_SKIP : [rn]  -> skip;
  

Если вы передадите лексеру следующие входные данные:

 cook("banana",
     "potatoe")
  varA = 12.4
  

будут созданы следующие токены:

 ID                        `cook`
'('                       `(`
STRING                    `"banana"`
','                       `,`
STRING                    `"potatoe"`
')'                       `)`
NL                        `n`
ID                        `varA`
'='                       `=`
NUMBER                    `12.4`
  

Как вы можете видеть, NL внутри скобок пропущен, а тот, что после ) , — нет.