Antlr парсер для и / или логики - как получить выражения между логическими операторами? - PullRequest
1 голос
/ 01 марта 2012

Я использую ANTLR для создания и / или анализатора + оценщик. Выражения будут иметь следующий формат:

  • x eq 1 && y eq 10
  • (x lt 10 && x gt 1) OR x eq -1

Я читал этот пост о логических выражениях в ANTLR Ищите советы по проекту. Разбор логического выражения и я нашел размещенную там грамматику хорошим началом:

grammar Logic;

parse
  :  expression EOF
  ;

expression
  :  implication
  ;

implication
  :  or ('->' or)*
  ;

or
  :  and ('&&' and)*
  ;

and
  :  not ('||' not)*
  ;

not
  :  '~' atom
  |  atom
  ;

atom
  :  ID
  |  '(' expression ')'
  ;

ID    : ('a'..'z' | 'A'..'Z')+;
Space : (' ' | '\t' | '\r' | '\n')+ {$channel=HIDDEN;};

Однако, хотя получение дерева из синтаксического анализатора работает для выражений, где переменные представляют собой только один символ (т. Е. "(A || B) AND C", мне трудно приспособить это к моему случаю (в примере "x eq 1 && y eq 10" ожидайте одного "AND" родителя и двух детей, "x eq 1" и "y eq 10", см. тестовый пример ниже).

@Test
public void simpleAndEvaluation() throws RecognitionException{
    String src = "1 eq 1 && B";

    LogicLexer lexer = new LogicLexer(new ANTLRStringStream(src));
    LogicParser parser = new LogicParser(new CommonTokenStream(lexer));


    CommonTree tree = (CommonTree)parser.parse().getTree();

    assertEquals("&&",tree.getText());
    assertEquals("1 eq 1",tree.getChild(0).getText());
    assertEquals("a neq a",tree.getChild(1).getText());
}

Я считаю, что это связано с "ID". Какой будет правильный синтаксис?

Ответы [ 2 ]

2 голосов
/ 02 марта 2012

Для тех, кто заинтересован, я сделал некоторые улучшения в моем файле грамматики (см. Ниже)

Текущие ограничения:

  • работает только с && / ||, а не AND/ ИЛИ (не очень проблематично)

  • между скобками и && / || не должно быть пробелов(Я решаю это путем замены "(" на ")" и ")" на ")" в исходной строке перед тем, как кормить лексер)

    грамматика Логика;

    options {
      output = AST;
    }
    
    tokens {
      AND = '&&';
      OR  = '||';
      NOT = '~';
    }
    
    // parser/production rules start with a lower case letter
    parse
      :  expression EOF!    // omit the EOF token
      ;
    
    expression
      :  or
      ;
    
    or
      :  and (OR^ and)*    // make `||` the root
      ;
    
    and
      :  not (AND^ not)*      // make `&&` the root
      ;
    
    not
      :  NOT^ atom    // make `~` the root
      |  atom
      ;
    
    atom
      :  ID
      |  '('! expression ')'!    // omit both `(` and `)`
      ;
    
    // lexer/terminal rules start with an upper case letter
    ID
      :
        (
        'a'..'z'
        | 'A'..'Z'
        | '0'..'9' | ' '
        | SYMBOL
      )+ 
      ;
    
    SYMBOL
      :
        ('+'|'-'|'*'|'/'|'_')
     ;
    
0 голосов
/ 01 марта 2012
ID    : ('a'..'z' | 'A'..'Z')+;

указывает, что идентификатор представляет собой последовательность из одной или нескольких букв, но не допускает никаких цифр. Попробуйте

ID    : ('a'..'z' | 'A'..'Z' | '0'..'9')+;

, что позволит, например, abc, 123, 12ab и ab12. Если вы не хотите использовать последние типы, вам придется немного реструктурировать правило (оставленное как вызов ...)

Чтобы принять произвольно много идентификаторов, вы можете определить atom как ID+ вместо ID.

Кроме того, вам, вероятно, потребуется указать AND, OR, -> и ~ в качестве токенов, чтобы, как говорит @Bart Kiers, первые два не классифицировались как ID и так, что последние два будут вообще признаны.

...