使用带有访问者的语法来计算算术表达式

时间:2015-12-01 20:39:52

标签: antlr antlr4

我们在课堂上给出了这样的语法:

grammar Calculator;

@header {
import java.util.*;
}

@parser::members {
/** "memory" for our calculator; variable/value pairs go here */
Map<String, Double> memory = new HashMap<String, Double>();
}

statlist    :   stat+   ;

stat        :   vgl NL          #printCompare                   
            |   ass NL          #printAssign                        
            |   NL              #blank
            ;

ass         : <assoc=right> VAR ('=') vgl #assign       
            ;               

vgl         :   sum(op=('<'|'>') sum)*  #compare
            ;

sum         :   prod(op=('+'|'-') prod)*    #addSub
            ;

prod        :   pot(op=('*'|'/') pot)*      #mulDiv     
            ;

pot         :<assoc=right> term(op='^' pot)?    #poten
            ;

term        :   '+' term    #add    
            |   '-' term    #subtract
            |   '(' sum ')' #parens
            |   VAR         #var
            |   INT         #int
            ;

/*Rules for the lexer */
MUL :   '*' ; 
DIV :   '/' ;
ADD :   '+' ;
SUB :   '-' ;
BIG :   '>' ;
SML :   '<' ;
POT :   '^' ;
VAR :   [a-zA-Z]+   ;
NL  :   [\n]        ;
INT :   [0-9]+      ;


WS : [ \r\t]+ -> skip ; // skip spaces, tabs

我在翻译像

这样的结构时遇到了问题
sum         :   prod(op=('+'|'-') prod)*    #addSub

进入工作代码。目前相应的方法如下所示:

    /** prod(op=('+'|'-') prod)* */
@Override
public Double visitAddSub(CalculatorParser.AddSubContext ctx) {
    double left = visit(ctx.prod(0));
    if(ctx.op == null){
        return left;
    }
    double right = visit(ctx.prod(1));
    return (ctx.op.getType() == CalculatorParser.ADD) ? left+right : left-right;
}

当前输出看起来像这样

3+3+3
6.0

这显然是假的。如何在不触及语法的情况下让访问者正确访问节点?

2 个答案:

答案 0 :(得分:0)

看看规则:

prod(op=('+'|'-') prod)*

看到*?这意味着括号内的内容可以出现0次或更多次。

您的访问者代码假定只有一个或两个孩子prod,但不会更多。这就是您看到6.0的原因:解析器将3+3+3放入上下文中,但您的访问者仅处理3+3并将最终+3退出。

因此,只需对所有whileop子项使用prod循环,并将其累积到结果中。

答案 1 :(得分:0)

好的,在Lucas的帮助和op+=的使用下,我设法解决了我的问题。它看起来很复杂但有效。

/** prod(op+=('+'|'-') prod)* */
@Override
public Double visitAddSub(CalculatorParser.AddSubContext ctx) {
    Stack<Double> temp = new Stack<Double>();
    switch(ctx.children.size()){
    case 1: return visit(ctx.prod(0));      
    default:
        Double ret = 0.0;
        for(int i = 0; i < ctx.op.size(); i++){
            if(ctx.op.get(i).getType()==CalculatorParser.ADD){
                if(temp.isEmpty()) {
                    ret = visit(ctx.prod(i)) + visit(ctx.prod(i+1));
                    temp.push(ret);
                } else {
                    ret = temp.pop() + visit(ctx.prod(i+1));
                    temp.push(ret);
                }
            } else {
                if(temp.isEmpty()) {
                    ret = visit(ctx.prod(i)) - visit(ctx.prod(i+1));
                    temp.push(ret);
                } else {
                    ret = temp.pop() - visit(ctx.prod(i+1));
                    temp.push(ret);
                }
            }
        }
    }
    return temp.pop();
}

我们使用switch-case来确定此上下文有多少个孩子。如果超过3个,我们至少有2个运营商。然后我们使用单个运算符和堆栈来确定结果。