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

Я использую следующую грамматику ANTLR для определения функции.

definition_function
    : DEFINE FUNCTION function_name '[' language_name ']'
      RETURN attribute_type '{' function_body '}'
    ;

function_name
    : id
    ;

language_name
    : id
    ;

function_body
    : SCRIPT
    ;

SCRIPT
    :   '{' ('\u0020'..'\u007e' | ~( '{' | '}' ) )* '}' 
        { setText(getText().substring(1, getText().length()-1)); }
    ;

Но когда я пытаюсь разобрать две функции, как показано ниже,

define function concat[Scala] return string {
  var concatenatedString = ""
  for(i <- 0 until data.length) {
     concatenatedString += data(i).toString
  }
  concatenatedString
};
define function concat[JavaScript] return string {
  var str1 = data[0];
  var str2 = data[1];
  var str3 = data[2];
  var res = str1.concat(str2,str3);
  return res;
};

Тогда ANTLR анализирует это не как два определения функции, а как одну функцию со следующим телом:

  var concatenatedString = ""
  for(i <- 0 until data.length) {
     concatenatedString += data(i).toString
  }
  concatenatedString
};
define function concat[JavaScript] return string {
  var str1 = data[0];
  var str2 = data[1];
  var str3 = data[2];
  var res = str1.concat(str2,str3);
  return res;

Вы можете объяснить такое поведение? В теле функции может быть что угодно. Как я могу правильно определить эту грамматику?


person Ayash    schedule 13.02.2015    source источник


Ответы (2)


Ваше правило соответствует такому значению, потому что '\u0020'..'\u007e' из правила '{' ('\u0020'..'\u007e' | ~( '{' | '}' ) )* '}' соответствует как {, так и }.

Ваше правило должно работать, если вы определите его так:

SCRIPT
    :   '{' ( SCRIPT | ~( '{' | '}' ) )* '}' 
    ;

Однако это не удастся, если блок сценария содержит, говорит, строки или комментарии, содержащие { или }. Вот способ сопоставления токена SCRIPT, включая комментарии и строковые литералы, которые могут содержать { и '}':

SCRIPT
 : '{' SCRIPT_ATOM* '}'
 ;

fragment SCRIPT_ATOM
 : ~[{}]
 | '"' ~["]* '"'
 | '//' ~[\r\n]*
 | SCRIPT
 ;

Полная грамматика, которая правильно анализирует ваш ввод, будет выглядеть так:

grammar T;

parse
 : definition_function* EOF
 ;

definition_function
 : DEFINE FUNCTION function_name '[' language_name ']' RETURN attribute_type SCRIPT ';'
 ;

function_name
 : ID
 ;

language_name
 : ID
 ;

attribute_type
 : ID
 ;

DEFINE
 : 'define'
 ;

FUNCTION
 : 'function'
 ;

RETURN
 : 'return'
 ;

ID
 : [a-zA-Z_] [a-zA-Z_0-9]*
 ;

SCRIPT
 : '{' SCRIPT_ATOM* '}'
 ;

SPACES
 : [ \t\r\n]+ -> skip
 ;

fragment SCRIPT_ATOM
 : ~[{}]
 | '"' ~["]* '"'
 | '//' ~[\r\n]*
 | SCRIPT
 ;

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

define function concat[JavaScript] return string {
  for (;;) { 
    while (true) { } 
  }
  var s = "}"
  // }
  return s 
};
person Bart Kiers    schedule 14.02.2015

Если вам абсолютно не нужно, чтобы SCRIPT был токеном (распознаваемым правилом lexer), вы можете использовать правило parser, которое распознает вложенные блоки (block правило ниже). Приведенная здесь грамматика должна анализировать ваш пример как два различных определения функций.

DEFINE : 'define';
FUNCTION : 'function';
RETURN : 'return';
ID : [A-Za-z]+;
ANY : . ;
WS : [ \r\t\n]+ -> skip ;

test : definition_function* ;

definition_function
    : DEFINE FUNCTION function_name '[' language_name ']'
      RETURN attribute_type block ';'
    ;

function_name : id ;
language_name : id ;
attribute_type : 'string' ;
id : ID;

block
    : '{' ( ( ~('{'|'}') )+ | block)* '}'
    ;
person Marc Q.    schedule 13.02.2015
comment
Обратите внимание, что правила лексера могут также содержать рекурсивные (лексические) правила. Обратите внимание, что внутри правила синтаксического анализатора ~('{'|'}') не соответствует никакому символу, кроме { и }, а скорее любому токену, кроме токенов, которые соответствуют { и }. - person Bart Kiers; 14.02.2015