TL;DR:
私の電卓の文法は、再帰降下に依存して括弧グループを相互にネストしますが、ネストされた括弧が多すぎる (約 20) と、スタック オーバーフローが発生します。どうすればこれを修正できますか? 問題をより平坦にする方法はありますか?
長い形式:
それほど前のことではありませんが、私の頭は小規模な組み込みシステムに深く突き刺さっていましたが、脳の半分しかスタック オーバーフローに陥るはずがありませんでした。今はもっと抽象的な仕事に頭を悩ませているので、アドバイスを求めてここに来ました。
やる気を起こさせるプロジェクトは、Android 用の電卓です。現在の電卓は多くの点で明らかに不十分ですが、今日はソープボックスを持ってこなかったので、私が直面している問題に直行します: スタック オーバーフローです!
具体的には、ユーザーが関数などを含むネストされた括弧グループを作成しすぎた場合です。ANTLR
これは、計算機が文法に依存しているために発生します。つまり、再帰降下を使用しています。便利なことに、これにより PEMDAS を介して継続的に実行できるため、ネストされた関数と括弧の計算が簡単になります。しかし!電話によっては、かっこボタンを 20 回ほど押すとクラッシュが発生することがわかりました。これは、約 100 の関数呼び出しの深さから発生するスタック オーバーフローが原因で、再帰的降下法の自然な結果です。
フラットはネストされたものよりも優れていることはわかっていますが、それが通過する 4 つのレベル (関数) は完全に必要であり、他のいくつかのレベルは私の人生を対数的に容易にします。これらのレベルを削除しても問題は解決しません。ユーザーは数分以内にシステムをクラッシュさせることができます。「かっこが多すぎる!」エラーメッセージが悪いです (他の計算機の 1 つが行うことです)。また、私は AST 出力を使用して入力文字列を書式設定し、きれいなようにします。そのため、括弧グループの事前計算により、システム全体が少し複雑になりすぎます。
だから、質問:
この質問をすることさえばかげているように思えますが、コール スタックを爆発させることなく、複雑で深くネストされた式を解析および解釈できる文法を 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;};