2015-12-01 3 views
0

Мы были даны грамматику в классе, который выглядит следующим образом:Использование грамматики с посетителем для вычисления арифметических выражений

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 

, который, очевидно, неверно. Как заставить посетителя правильно посещать узлы, не касаясь грамматики?

ответ

0

Хорошо, с помощью Лукаса и использования 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(); 
} 

Мы используем коммутационный футляр, чтобы определить, сколько детей имеет этот контекст. Если его более 3, то мы имеем по крайней мере 2 оператора. Затем мы используем отдельный оператор и стек для определения результата.

0

Посмотрите на правила:

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

Смотри, что *? Это означает, что то, что находится внутри круглых скобок, может достигать 0 или более раз.

Ваш код посетителя предполагает наличие только одного или двух детей prod, но не более того. Вот почему вы видите 6.0: парсер положил 3+3+3 в контекст, но ваш посетитель обработал только 3+3 и оставил окончательный +3.

Итак, просто используйте петлю while над всеми детьми op и prod и аккумулируйте их в результат.

+0

Это делает много смысла @ Лукас. Однако я не уверен, как перебирать детей. op сам не имеет никакого отношения, и дети из prod (1), например, выглядят так: 'Вход: 3 + 3 + 3' ' System.out.println (ctx.children); [[42 34 21 16], +, [44 34 21 16], +, [44 34 21 16]] ' ' System.out.println (ctx.prod (0) .children); [[50 42 34] 21 16]] ' ' System.out.println (ctx.prod (1) .children); [[50 44 34 21 16]] ' – Xianahru

+0

Похоже, вы можете получить всю необходимую информацию из коллекции' children' , –

+0

по какой-то причине ctx.op всегда является последним оператором. Например, с учетом ввода '1 + 2-3'ctx.op заканчивается' -'. Я раздражен, потому что 1 + 2 * 3 возвращает 7 с 'visit (ctx.prod (1))' return '6-0'. Там должно быть что-то, чего я пропускаю, что делает это проще, чем повторение всех детей и отбрасывание логики, которую дает конечный посетитель. – Xianahru