使用带有访问者的语法来计算算术表达式
Using a grammar with a visitor to calculate arithmetic expressions
我们在 class 中得到的语法如下所示:
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
这显然是错误的。如何让访问者在不触及语法的情况下正确访问节点?
看看规则:
prod(op=('+'|'-') prod)*
看到 *
了吗?这意味着括号内的内容可以出现 0 次或更多次。
您的访客代码假定只有一个或两个 child prod
,但不会更多。这就是您看到 6.0
的原因:解析器将 3+3+3
放入上下文中,但您的访问者只处理了 3+3
并留下了最后的 +3
。
所以只需对所有 op
和 prod
children 使用 while
循环,并将它们累加到结果中。
好的,在 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 来确定此上下文有多少 children。如果超过 3 个,我们至少有 2 个操作员。然后我们使用单个运算符和堆栈来确定结果。
我们在 class 中得到的语法如下所示:
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
这显然是错误的。如何让访问者在不触及语法的情况下正确访问节点?
看看规则:
prod(op=('+'|'-') prod)*
看到 *
了吗?这意味着括号内的内容可以出现 0 次或更多次。
您的访客代码假定只有一个或两个 child prod
,但不会更多。这就是您看到 6.0
的原因:解析器将 3+3+3
放入上下文中,但您的访问者只处理了 3+3
并留下了最后的 +3
。
所以只需对所有 op
和 prod
children 使用 while
循环,并将它们累加到结果中。
好的,在 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 来确定此上下文有多少 children。如果超过 3 个,我们至少有 2 个操作员。然后我们使用单个运算符和堆栈来确定结果。