Программа шаблонов посетителей работает некорректно

Мне нужно создать простой компилятор для калькулятора, и я использую antlr4 и шаблон посетителя. это мой файл грамматики Simple_Calculator.g4:

grammar Simple_Calculator;
program : statements ;
statements : statement | statements statement ;
statement : identifier '=' expr ';'                         #assign
      | 'begin' statements 'end'                            #brac
      | 'if' expr 'then' statement                          #if
      | 'if' expr 'then' statement 'else' statement                 #if_elset
      | 'while' expr 'do' statement                         #while
      | 'for' identifier '=' number ':' number 'do' statement           #for
      | 'print' identifier ';'                          #print
      ;
expr    : expr binop expr                               #ope
    | '!' expr                                  #invert
    | '(' expr ')'                                  #parenthesis
    | identifier                                    #identify
    | number                                    #num
    ;
binop   : '+'
    | '-'
    | '*'
    | '/'
    | '<'
    | '>'
    | '<='
    | '>='
    | '=='
    | '!='
    | '^'
    ;
identifier : STRING+('-'|STRING|INT)* ;                     
number  : INT+('.'INT+)? ;
STRING: [a-zA-Z]+ ;
INT : [0-9]+ ;
WS  : [ \t\r\n] -> skip ;

и следуют методы visitOpe и visitAssign в классе MainVisitor extends Simple_CalculatorBaseVisitor:

public class MainVisitor extends Simple_CalculatorBaseVisitor<Object> {

@Override
    public Object visitAssign(Simple_CalculatorParser.AssignContext ctx) {
        String id = (String) (visit(ctx.identifier()));
        String value = (String)(visit(ctx.expr()));

        if (storage.containsKey(id)) {
            storage.replace(id, value);
        } else {
            storage.put(id, value);
        }
        return storage; //To change body of generated methods, choose Tools | Templates.
    } // end of visitAssign

@Override
    public Object visitOpe(Simple_CalculatorParser.OpeContext ctx) {
        String leftOperand = (String) visit(ctx.expr(0));
        String rightOperand = (String) visit(ctx.expr(1));
        /*if (rightOperand.matches("^\\d+$")) {
        return rightOperand;
        }*/
        String Operation = ctx.binop().getText();
        switch (Operation) {
            case "+": {
                leftOperand = setOperands(leftOperand);
                rightOperand = setOperands(rightOperand);
                return String.valueOf(Integer.parseInt(leftOperand) + Integer.parseInt(rightOperand));
            }
            case "-":
                leftOperand = setOperands(leftOperand);
                rightOperand = setOperands(rightOperand);
                return String.valueOf(Integer.parseInt(leftOperand) - Integer.parseInt(rightOperand));
            case "*":
                leftOperand = setOperands(leftOperand);
                rightOperand = setOperands(rightOperand);
                return String.valueOf(Integer.parseInt(leftOperand) * Integer.parseInt(rightOperand));
            case "/":
                leftOperand = setOperands(leftOperand);
                rightOperand = setOperands(rightOperand);
                return String.valueOf(Integer.parseInt(leftOperand) / Integer.parseInt(rightOperand));

// the rest possible conditions
    }

//other methods
}// end of visitOpe

проблема в том, что когда я хочу использовать оператор '-' в выражении назначения, когда программа читает строку String value = (String)(visit(ctx.expr())); в visitAssign, она не посещает visitOpe после этого, вместо этого она возвращает все выражение. например, когда я даю программу:

i=5;
i=i+2;
print i;

как ввод, он работает нормально. он сохраняет i в качестве идентификатора в HashMap, а во второй строке добавит к нему 2 единицы и, наконец, напечатает его значение. но если я изменю i=i+2; на i=i-2, он сохранит i с начальным значением 5, но во второй строке он просто не пройдет через метод visitOpe, вместо этого он вернет «i-2» и сохранит это как значение i и, наконец, напечатает Это. Операторы '*' и '/' прекрасно работают в качестве сложения. Речь идет только о «-», и я не знаю, в чем проблема. Итак, как это исправить:

С Уважением


person HMD    schedule 22.06.2017    source источник


Ответы (2)


С этим фрагментом грамматики

identifier : STRING+('-'|STRING|INT)* ;      

i-2 является допустимым идентификатором. Одним из вариантов было бы удалить '-' из идентификатора.

So:

identifier : STRING+(STRING|INT)* ;      
person k5_    schedule 22.06.2017
comment
о боже.. это должно быть подчеркивание... я такой слепой... :| спасибо много - person HMD; 22.06.2017

Другое наблюдение, но если вы делаете калькулятор или что-то, что вычисляет математические выражения, вы также можете переосмыслить свой binop приоритет. Внутри одной группы они оцениваются сверху вниз. Это не похоже на старые грамматики BNF, где люди делали трюки, а операторы с более высоким приоритетом опускались на дно. Таким образом, в вашем случае выражение типа 3+2*5 даст 25 вместо 13, потому что сложение имеет более высокий приоритет, чем умножение. Обратите внимание, как изложена эта превосходная грамматика (Mu) из книги Bart Kiers:

expr
 : expr POW<assoc=right> expr           #powExpr
 | MINUS expr                           #unaryMinusExpr
 | NOT expr                             #notExpr
 | expr op=(MULT | DIV | MOD) expr      #multiplicationExpr
 | expr op=(PLUS | MINUS) expr          #additiveExpr
 | expr op=(LTEQ | GTEQ | LT | GT) expr #relationalExpr
 | expr op=(EQ | NEQ) expr              #equalityExpr
 | expr AND expr                        #andExpr
 | expr OR expr                         #orExpr
 | atom                                 #atomExpr
 ;

Наивысший приоритет сверху.

person TomServo    schedule 22.06.2017
comment
Что ж, грамматика предоставлена ​​в качестве проекта моим университетским учителем, и нам просто нужно создать для нее компилятор. это не только калькулятор, он выполняет некоторые другие функции, такие как печать. - person HMD; 22.06.2017
comment
Понял. Однако просто имейте в виду, что приоритет операторов в основном не соответствует нормам. Это повлияет на оценку выражений и, возможно, даст непонятные результаты, если для группировки выражений не используются круглые скобки. См. этот старый ответ от одного из мастеров, Барта Кирса. - person TomServo; 22.06.2017