TL; DR:递归下降为基础的计算器堆栈溢出
我的计算器语法依赖于递归下降巢括号内彼此群体,但太多的嵌套的括号(约20)导致堆栈溢出。我怎样才能解决这个问题?有没有办法让问题更加平坦?
长篇:
这是不久前 - 我的头卡住深入到小型嵌入式系统 - 任何人动动脑子应该运行到堆栈溢出。现在被一个更抽象的任务所迷惑,我来这里寻求建议。
激励项目是Android的计算器。目前的计算器在很多方面明显不足,但是我今天没有带上我的soapbox,所以我会直接解决我遇到的问题:堆栈溢出!
具体来说,当用户创建了太多的嵌套括号组,包括函数等。这是因为我的计算器依赖于一个ANTLR
语法,这意味着它使用递归下降。方便地,它可以通过PEMDAS连续运行,从而可以轻松计算嵌套函数和括号。但!我发现 - 根据手机的不同,按下parens按钮20次左右会导致崩溃,这是由堆栈溢出导致的,该堆栈溢出源自调用堆栈大约100次函数调用深度,这是递归下降方法的自然结果。我知道,扁平比嵌套更好,但其中4个层次(函数)是完全必要的,而其他几个层次使我的生活更加容易。即使删除这些级别也不能解决问题:用户仍然能够在几分钟内使系统崩溃。有一个“太多parens!”错误信息是不好的(它是其他计算器中的一个)。另外,我使用AST输出来格式化输入字符串,使它变得非常像,所以对parens组进行预先计算会让整个系统太复杂。
所以,问题:
即使问这个问题似乎很傻,但:有没有实现在ANTLR语法,可以分析和解释复杂和深嵌套表达式没有爆炸的调用堆栈的方式?
语法:
grammar doubleCalc;
options {
language = Java;
output = AST;
// k=2;
}
// Calculation function.
prog returns [double value]
: e=addsub EOF {$value = $e.value;}
;
addsub returns [double value]
: e=muldiv {$value = $e.value;}
( PLUS^ e=muldiv {$value += $e.value;}
| MINUS^ e=muldiv {$value -= $e.value;}
)*
;
muldiv returns [double value]
: e=power {$value = $e.value;}
( MUL^ e=power {$value *= $e.value;}
| DIV^ e=power {$value /= $e.value;}
)*
;
power returns [double value]
: e = negate {$value = $e.value;}
( POW^ f=power {$value = java.lang.Math.pow($value, $f.value);}
)?
;
negate returns [double value]
: ( MINUS^ neg = atom {$value = -$neg.value;}
| neg = atom {$value = $neg.value;}
)
;
atom returns [double value]
: LOG10^ '(' e=addsub ')' {$value = java.lang.Math.log10($e.value);}
| LOG8^ '(' e=addsub ')' {$value = java.lang.Math.log10($e.value)/java.lang.Math.log10(8.0);}
| LOG2^ '(' e=addsub ')' {$value = java.lang.Math.log10($e.value)/java.lang.Math.log10(2.0);}
| LN^ '(' e=addsub ')' {$value = java.lang.Math.log($e.value);}
| ASIN^ '(' e=addsub ')' {$value = Math.asin(Math.PI*(($e.value/Math.PI) \% 1));}//com.brogramming.HoloCalc.Trig.asin($e.value);}
| ACOS^ '(' e=addsub ')' {$value = Math.acos(Math.PI*(($e.value/Math.PI) \% 1));}
| ATAN^ '(' e=addsub ')' {$value = Math.atan(Math.PI*(($e.value/Math.PI) \% 1));}
| SIN^ '(' e=addsub ')' {$value = Math.sin(Math.PI*(($e.value/Math.PI) \% 1));}
| COS^ '(' e=addsub ')' {$value = Math.cos(Math.PI*(($e.value/Math.PI) \% 1));}
| TAN^ '(' e=addsub ')' {$value = Math.tan(Math.PI*(($e.value/Math.PI) \% 1));}
| ASIND^ '(' e=addsub ')' {$value = Math.asin(Math.PI*(($e.value/180f) \% 1));}//com.brogramming.HoloCalc.Trig.asin($e.value);}
| ACOSD^ '(' e=addsub ')' {$value = Math.acos(Math.PI*(($e.value/180f) \% 1));}
| ATAND^ '(' e=addsub ')' {$value = Math.atan(Math.PI*(($e.value/180f) \% 1));}
| SIND^ '(' e=addsub ')' {$value = Math.sin(Math.PI*(($e.value/180f) \% 1));}
| COSD^ '(' e=addsub ')' {$value = Math.cos(Math.PI*(($e.value/180f) \% 1));}
| TAND^ '(' e=addsub ')' {$value = Math.tan(Math.PI*(($e.value/180f) \% 1));}
| SQRT^ '(' e=addsub ')' {$value = (double) java.lang.Math.pow($e.value, 0.5);}
| CBRT^ '(' e=addsub ')' {$value = (double) java.lang.Math.pow($e.value, 1.0/3.0);}
| ABS^ '(' e=addsub ')' {$value = (double) java.lang.Math.abs($e.value);}
// Numbers
| n = number {$value = $n.value;}
| '(' e=addsub ')' {$value = $e.value;}
;
number returns [double value]
: PI {$value = java.lang.Math.PI;}
| EXP {$value = java.lang.Math.E;}
| INT {$value = (double) Double.parseDouble($INT.text.replaceAll(",", ""));}
| DOUBLE {$value = Double.parseDouble($DOUBLE.text.replaceAll(",", ""));}
;
LN : 'ln';
LOG10 : 'log10';
LOG8 : 'log8';
LOG2 : 'log2';
SIN : 'sin';
COS : 'cos';
TAN : 'tan';
ASIN : 'asin';
ACOS : 'acos';
ATAN : 'atan';
SINH : 'sinh';
COSH : 'cosh';
TANH : 'tanh';
ASINH : 'asinh';
ACOSH : 'acosh';
ATANH : 'atanh';
SIND : 'sind';
COSD : 'cosd';
TAND : 'tand';
ASIND : 'asind';
ACOSD : 'acosd';
ATAND : 'atand';
SINHD : 'sinhd';
COSHD : 'coshd';
TANHD : 'tanhd';
ASINHD : 'asinhd';
ACOSHD : 'acoshd';
ATANHD : 'atanhd';
PI : 'pi';
IM : 'i';
EXP : 'e';
ABS : 'abs';
FACT : 'fact';
SQRE : 'sqre';
CUBE : 'cube';
SQRT : 'sqrt';
CBRT : 'cbrt';
POW : '^';
PLUS : '+';
MINUS : '-';
MUL : ('*');
DIV : '/';
BANG : '!';
DOUBLE: ('0'..'9' | ',')+ '.'('0'..'9')* ;
INT : ('0'..'9' | ',')+ ;
NEWLINE:'\r'? '\n' ;
PERCENT
: '%';
EOF : '<EOF>' {$channel=HIDDEN;};
我认为这是我第一次看到堆栈溢出堆栈溢出相关的问题。 :) – vijay