ANTLR / Грамматика: язык калькулятора

Я пытаюсь создать логический язык выражения / грамматику для личного проекта. Пользователь сможет написать строку в синтаксисе, подобном Java, с предоставлением переменных, которые будут оцениваться позже, когда переменные будут инициализированы. Дождь Например, пользователь может ввести строку

@FOO+7 > 4*(5+@BAR);

Позже, когда переменная FOO инициализирована и равна 6, а BAR равно 1, выражение оценивается как 13> 24 и, таким образом, возвращает false.

Я использую ANTLRworks для создания грамматики, и хотя он ВЫГЛЯДИТ нормально, он неправильно интерпретирует отрицательные знаки. Вход в ANTLRworks (по какой-то причине) изменен: «(8-3)> 6» читается как «(8> 6» (который не запускается, поскольку отсутствует закрывающая скобка). Я не реализовал поиск переменных пока что, но вот грамматика пока только для целых чисел:

grammar BooleanCalculator;

@header {
package test;
}

prog    : rule+
;

rule    : boolean_expr ';' NEWLINE {System.out.println($boolean_expr.b);}
| NEWLINE
;

boolean_expr returns [boolean b]
: v1=num_statement 
('<'  v2=num_statement {$b = $v1.d <  $v2.d;}
|'<=' v2=num_statement {$b = $v1.d <= $v2.d;}
|'='  v2=num_statement {$b = $v1.d == $v2.d;}
|'!=' v2=num_statement {$b = !($v1.d == $v2.d);}
|'>=' v2=num_statement {$b = $v1.d >= $v2.d;}
|'>'  v2=num_statement {$b = $v1.d >  $v2.d;})
;

num_statement returns [double d]
: v1=mult_statement {$d = $v1.d;}
('+' v2=mult_statement {$d += $v2.d;}
|'-' v2=mult_statement {$d -= $v2.d;})* //HERE IS THE OFFENDING LINE
;

mult_statement returns [double d]
: v1=var {$d = $v1.d;}
('*' v2=var {$d *= $v2.d;}
|'/' v2=var {$d /= $v2.d;}
|'%' v2=var {$d = $d/100*$v2.d;})*
;

var returns [double d]
: NUMBER {$d = Double.parseDouble($NUMBER.text);}
| '(' v1=num_statement ')' {$d = $v1.d;}
;

NUMBER  : '0'..'9'+
;

Он работает правильно для всего, кроме знака «-». Кто-нибудь знает, как это исправить?

Также (я новичок в ANTLR): правильно ли я выполняю оценку? Или я должен просто позволить грамматике определять структуру и использовать другой метод, чтобы определить, является ли утверждение истинным / ложным?


person Trasvi    schedule 01.03.2011    source источник


Ответы (1)


Ваша грамматика:

grammar BooleanCalculator;

prog    
  :  rule+
  ;

rule
  :  boolean_expr {System.out.println($boolean_expr.b);}
  ;

boolean_expr returns [boolean b]
  : v1=num_statement ( '<'  v2=num_statement {$b = $v1.d < $v2.d;}
                     | '<=' v2=num_statement {$b = $v1.d <= $v2.d;}
                     | '='  v2=num_statement {$b = $v1.d == $v2.d;}
                     | '!=' v2=num_statement {$b = !($v1.d == $v2.d);}
                     | '>=' v2=num_statement {$b = $v1.d >= $v2.d;}
                     | '>'  v2=num_statement {$b = $v1.d > $v2.d;}  {System.out.println("v1=" + $v1.d + ", v2=" + $v2.d);}
                     )
  ;

num_statement returns [double d]
  :  v1=mult_statement {$d = $v1.d;} ( '+' v2=mult_statement {$d += $v2.d;}
                                     | '-' v2=mult_statement {$d -= $v2.d;}
                                     )* 
  ;

mult_statement returns [double d]
: v1=var {$d = $v1.d;} ( '*' v2=var {$d *= $v2.d;}
                       | '/' v2=var {$d /= $v2.d;}
                       | '%' v2=var {$d = $d/100*$v2.d;}
                       )*
;

var returns [double d]
  : NUMBER {$d = Double.parseDouble($NUMBER.text);}
  | '(' v1=num_statement ')' {$d = $v1.d;}
  ;

NUMBER  
  :  '0'..'9'+
  ;

(обратите внимание, что я ничего не менял, кроме небольшого переформатирования и добавил дополнительный println для отладки!)

произвел следующий вывод:

$ java -cp antlr-3.2.jar org.antlr.Tool BooleanCalculator.g 
$ javac -cp antlr-3.2.jar *.java
$ java -cp .:antlr-3.2.jar Main

v1=5.0, v2=6.0
false

используя тестовый класс:

import org.antlr.runtime.*;

public class Main {
    public static void main(String[] args) throws Exception {
        ANTLRStringStream in = new ANTLRStringStream("(8-3)>6");
        BooleanCalculatorLexer lexer = new BooleanCalculatorLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        BooleanCalculatorParser parser = new BooleanCalculatorParser(tokens);
        parser.prog();
    }
}

Так что вроде все идет хорошо.

Пара замечаний:

  • вы сравниваете doubles, используя == и !=. Будьте осторожны: ошибки округления приведут к неожиданному поведению (с точки зрения пользователя ...);
  • использование оператора по модулю в ваших грамматических действиях может быть выполнено путем экранирования его обратной косой чертой: \%.
person Bart Kiers    schedule 01.03.2011
comment
Хм. Это научит меня доверять инструментам автоматического тестирования. Не знаю, что происходит, но это работало на Java, но не в ANTLRworks. - person Trasvi; 01.03.2011
comment
@Trasvi, ANTLRWorks - отличный инструмент для визуализации вашей грамматики и ее редактирования, но это единственное, для чего я когда-либо его использую: никогда для тестирования (к сожалению, слишком глючит и не поддерживает предикаты). - person Bart Kiers; 01.03.2011