♻️ New calculator pending to replace math_expressions
This commit is contained in:
		
							
								
								
									
										539
									
								
								lib/calculator.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										539
									
								
								lib/calculator.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,539 @@ | |||||||
|  | // === 在 abstract class Expr 中添加声明 === | ||||||
|  | import 'dart:math' show sqrt, cos, sin, tan; | ||||||
|  |  | ||||||
|  | abstract class Expr { | ||||||
|  |   Expr simplify(); | ||||||
|  |  | ||||||
|  |   /// 新增:对表达式进行“求值/数值化”——尽可能把可算的部分算出来 | ||||||
|  |   Expr evaluate(); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString(); | ||||||
|  |  | ||||||
|  |   MulExpr operator *(Expr other) => MulExpr(this, other); | ||||||
|  |   AddExpr operator +(Expr other) => AddExpr(this, other); | ||||||
|  |   SubExpr operator -(Expr other) => SubExpr(this, other); | ||||||
|  |   DivExpr operator /(Expr other) => DivExpr(this, other); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // === IntExpr === | ||||||
|  | class IntExpr extends Expr { | ||||||
|  |   final int value; | ||||||
|  |   IntExpr(this.value); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr simplify() => this; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr evaluate() => this; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() => value.toString(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // === DoubleExpr === | ||||||
|  | class DoubleExpr extends Expr { | ||||||
|  |   final double value; | ||||||
|  |   DoubleExpr(this.value); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr simplify() => this; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr evaluate() => this; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() => value.toString(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // === FractionExpr.evaluate === | ||||||
|  | class FractionExpr extends Expr { | ||||||
|  |   final int numerator; | ||||||
|  |   final int denominator; | ||||||
|  |  | ||||||
|  |   FractionExpr(this.numerator, this.denominator) { | ||||||
|  |     if (denominator == 0) throw Exception("分母不能为0"); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr simplify() { | ||||||
|  |     int g = _gcd(numerator.abs(), denominator.abs()); | ||||||
|  |     int n = numerator ~/ g; | ||||||
|  |     int d = denominator ~/ g; | ||||||
|  |  | ||||||
|  |     // 分母负数转移到分子 | ||||||
|  |     if (d < 0) { | ||||||
|  |       n = -n; | ||||||
|  |       d = -d; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (d == 1) return IntExpr(n); // 化简成整数 | ||||||
|  |     return FractionExpr(n, d); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr evaluate() => simplify(); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() => "$numerator/$denominator"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // === AddExpr.evaluate: 把可算的合并(整数、分数、以及同类 sqrt 项) === | ||||||
|  | class AddExpr extends Expr { | ||||||
|  |   final Expr left, right; | ||||||
|  |   AddExpr(this.left, this.right); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr simplify() { | ||||||
|  |     var l = left.simplify(); | ||||||
|  |     var r = right.simplify(); | ||||||
|  |  | ||||||
|  |     if (l is FractionExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.numerator * r.denominator + r.numerator * l.denominator, | ||||||
|  |         l.denominator * r.denominator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is IntExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.value * r.denominator + r.numerator, | ||||||
|  |         r.denominator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is FractionExpr && r is IntExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.numerator + r.value * l.denominator, | ||||||
|  |         l.denominator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return AddExpr(l, r); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr evaluate() { | ||||||
|  |     var l = left.evaluate(); | ||||||
|  |     var r = right.evaluate(); | ||||||
|  |  | ||||||
|  |     // 纯整数相加 -> 整数 | ||||||
|  |     if (l is IntExpr && r is IntExpr) { | ||||||
|  |       return IntExpr(l.value + r.value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 分数相加 / 分数与整数相加 | ||||||
|  |     if (l is FractionExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.numerator * r.denominator + r.numerator * l.denominator, | ||||||
|  |         l.denominator * r.denominator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is IntExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.value * r.denominator + r.numerator, | ||||||
|  |         r.denominator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is FractionExpr && r is IntExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.numerator + r.value * l.denominator, | ||||||
|  |         l.denominator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 合并同类的 sqrt 项: a*sqrt(X) + b*sqrt(X) = (a+b)*sqrt(X) | ||||||
|  |     var a = _asSqrtTerm(l); | ||||||
|  |     var b = _asSqrtTerm(r); | ||||||
|  |     if (a != null && b != null && a.inner.toString() == b.inner.toString()) { | ||||||
|  |       return MulExpr(IntExpr(a.coef + b.coef), SqrtExpr(a.inner)).simplify(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return AddExpr(l, r); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() => "($left + $right)"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // === SubExpr.evaluate 类似 AddExpr,但做减法 === | ||||||
|  | class SubExpr extends Expr { | ||||||
|  |   final Expr left, right; | ||||||
|  |   SubExpr(this.left, this.right); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr simplify() { | ||||||
|  |     var l = left.simplify(); | ||||||
|  |     var r = right.simplify(); | ||||||
|  |  | ||||||
|  |     if (l is IntExpr && r is IntExpr) { | ||||||
|  |       return IntExpr(l.value - r.value); | ||||||
|  |     } | ||||||
|  |     if (l is FractionExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.numerator * r.denominator - r.numerator * l.denominator, | ||||||
|  |         l.denominator * r.denominator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |     return SubExpr(l, r); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr evaluate() { | ||||||
|  |     var l = left.evaluate(); | ||||||
|  |     var r = right.evaluate(); | ||||||
|  |  | ||||||
|  |     if (l is IntExpr && r is IntExpr) { | ||||||
|  |       return IntExpr(l.value - r.value); | ||||||
|  |     } | ||||||
|  |     if (l is FractionExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.numerator * r.denominator - r.numerator * l.denominator, | ||||||
|  |         l.denominator * r.denominator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is IntExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.value * r.denominator - r.numerator, | ||||||
|  |         r.denominator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is FractionExpr && r is IntExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.numerator - r.value * l.denominator, | ||||||
|  |         l.denominator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 处理同类 sqrt 项: a*sqrt(X) - b*sqrt(X) = (a-b)*sqrt(X) | ||||||
|  |     var a = _asSqrtTerm(l); | ||||||
|  |     var b = _asSqrtTerm(r); | ||||||
|  |     if (a != null && b != null && a.inner.toString() == b.inner.toString()) { | ||||||
|  |       return MulExpr(IntExpr(a.coef - b.coef), SqrtExpr(a.inner)).simplify(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return SubExpr(l, r); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() => "($left - $right)"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // === MulExpr.evaluate === | ||||||
|  | class MulExpr extends Expr { | ||||||
|  |   final Expr left, right; | ||||||
|  |   MulExpr(this.left, this.right); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr simplify() { | ||||||
|  |     var l = left.simplify(); | ||||||
|  |     var r = right.simplify(); | ||||||
|  |  | ||||||
|  |     if (l is IntExpr && l.value == 1) return r; | ||||||
|  |     if (r is IntExpr && r.value == 1) return l; | ||||||
|  |     if (l is IntExpr && l.value == -1) return SubExpr(IntExpr(0), r).simplify(); | ||||||
|  |     if (r is IntExpr && r.value == -1) return SubExpr(IntExpr(0), l).simplify(); | ||||||
|  |  | ||||||
|  |     if (l is IntExpr && r is IntExpr) { | ||||||
|  |       return IntExpr(l.value * r.value); | ||||||
|  |     } | ||||||
|  |     if (l is FractionExpr && r is IntExpr) { | ||||||
|  |       return FractionExpr(l.numerator * r.value, l.denominator).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is IntExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr(l.value * r.numerator, r.denominator).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is FractionExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.numerator * r.numerator, | ||||||
|  |         l.denominator * r.denominator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is SqrtExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr(1, r.denominator).simplify() * | ||||||
|  |           MulExpr(l, IntExpr(r.numerator)).simplify(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return MulExpr(l, r); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr evaluate() { | ||||||
|  |     var l = left.evaluate(); | ||||||
|  |     var r = right.evaluate(); | ||||||
|  |  | ||||||
|  |     if (l is IntExpr && l.value == 1) return r; | ||||||
|  |     if (r is IntExpr && r.value == 1) return l; | ||||||
|  |     if (l is IntExpr && l.value == -1) return SubExpr(IntExpr(0), r).simplify(); | ||||||
|  |     if (r is IntExpr && r.value == -1) return SubExpr(IntExpr(0), l).simplify(); | ||||||
|  |  | ||||||
|  |     if (l is IntExpr && r is IntExpr) { | ||||||
|  |       return IntExpr(l.value * r.value); | ||||||
|  |     } | ||||||
|  |     if (l is FractionExpr && r is IntExpr) { | ||||||
|  |       return FractionExpr(l.numerator * r.value, l.denominator).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is IntExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr(l.value * r.numerator, r.denominator).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is FractionExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.numerator * r.numerator, | ||||||
|  |         l.denominator * r.denominator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // sqrt * sqrt: sqrt(a)*sqrt(a) = a | ||||||
|  |     if (l is SqrtExpr && | ||||||
|  |         r is SqrtExpr && | ||||||
|  |         l.inner.toString() == r.inner.toString()) { | ||||||
|  |       return l.inner.simplify(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // int * sqrt -> 保留形式,之后 simplify() 再处理约分 | ||||||
|  |     if ((l is IntExpr && r is SqrtExpr) || (l is SqrtExpr && r is IntExpr)) { | ||||||
|  |       return MulExpr(l, r).simplify(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return MulExpr(l, r); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() => "($left * $right)"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // === DivExpr.evaluate === | ||||||
|  | class DivExpr extends Expr { | ||||||
|  |   final Expr left, right; | ||||||
|  |   DivExpr(this.left, this.right); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr simplify() { | ||||||
|  |     var l = left.simplify(); | ||||||
|  |     var r = right.simplify(); | ||||||
|  |  | ||||||
|  |     if (l is IntExpr && r is IntExpr) { | ||||||
|  |       return FractionExpr(l.value, r.value).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is FractionExpr && r is IntExpr) { | ||||||
|  |       return FractionExpr(l.numerator, l.denominator * r.value).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is IntExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr(l.value * r.denominator, r.numerator).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is FractionExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.numerator * r.denominator, | ||||||
|  |         l.denominator * r.numerator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is MulExpr && | ||||||
|  |         l.left is IntExpr && | ||||||
|  |         l.right is SqrtExpr && | ||||||
|  |         r is IntExpr) { | ||||||
|  |       int coeff = (l.left as IntExpr).value; | ||||||
|  |       int denom = r.value; | ||||||
|  |       int g = _gcd(coeff.abs(), denom.abs()); | ||||||
|  |       return MulExpr( | ||||||
|  |         IntExpr(coeff ~/ g), | ||||||
|  |         DivExpr(l.right, IntExpr(denom ~/ g)).simplify(), | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |     return DivExpr(l, r); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr evaluate() { | ||||||
|  |     var l = left.evaluate(); | ||||||
|  |     var r = right.evaluate(); | ||||||
|  |  | ||||||
|  |     if (l is IntExpr && r is IntExpr) { | ||||||
|  |       return FractionExpr(l.value, r.value).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is FractionExpr && r is IntExpr) { | ||||||
|  |       return FractionExpr(l.numerator, l.denominator * r.value).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is IntExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr(l.value * r.denominator, r.numerator).simplify(); | ||||||
|  |     } | ||||||
|  |     if (l is FractionExpr && r is FractionExpr) { | ||||||
|  |       return FractionExpr( | ||||||
|  |         l.numerator * r.denominator, | ||||||
|  |         l.denominator * r.numerator, | ||||||
|  |       ).simplify(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // handle (k * sqrt(X)) / d 约分 | ||||||
|  |     if (l is MulExpr && | ||||||
|  |         l.left is IntExpr && | ||||||
|  |         l.right is SqrtExpr && | ||||||
|  |         r is IntExpr) { | ||||||
|  |       int coeff = (l.left as IntExpr).value; | ||||||
|  |       int denom = r.value; | ||||||
|  |       int g = _gcd(coeff.abs(), denom.abs()); | ||||||
|  |       return MulExpr( | ||||||
|  |         IntExpr(coeff ~/ g), | ||||||
|  |         DivExpr(l.right, IntExpr(denom ~/ g)).evaluate(), | ||||||
|  |       ).evaluate(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return DivExpr(l, r); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() => "($left / $right)"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // === SqrtExpr.evaluate === | ||||||
|  | class SqrtExpr extends Expr { | ||||||
|  |   final Expr inner; | ||||||
|  |   SqrtExpr(this.inner); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr simplify() { | ||||||
|  |     var i = inner.simplify(); | ||||||
|  |     if (i is IntExpr) { | ||||||
|  |       int n = i.value; | ||||||
|  |       int root = sqrt(n).floor(); | ||||||
|  |       if (root * root == n) { | ||||||
|  |         return IntExpr(root); // 完全平方数 | ||||||
|  |       } | ||||||
|  |       // 尝试拆分 sqrt,比如 sqrt(8) = 2*sqrt(2) | ||||||
|  |       for (int k = root; k > 1; k--) { | ||||||
|  |         if (n % (k * k) == 0) { | ||||||
|  |           return MulExpr( | ||||||
|  |             IntExpr(k), | ||||||
|  |             SqrtExpr(IntExpr(n ~/ (k * k))), | ||||||
|  |           ).simplify(); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return SqrtExpr(i); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr evaluate() { | ||||||
|  |     var i = inner.evaluate(); | ||||||
|  |     if (i is IntExpr) { | ||||||
|  |       int n = i.value; | ||||||
|  |       int root = sqrt(n).floor(); | ||||||
|  |       if (root * root == n) return IntExpr(root); | ||||||
|  |       // 拆平方因子并返回 k * sqrt(remain) | ||||||
|  |       for (int k = root; k > 1; k--) { | ||||||
|  |         if (n % (k * k) == 0) { | ||||||
|  |           return MulExpr( | ||||||
|  |             IntExpr(k), | ||||||
|  |             SqrtExpr(IntExpr(n ~/ (k * k))), | ||||||
|  |           ).evaluate(); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return SqrtExpr(i); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() => "sqrt($inner)"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // === CosExpr === | ||||||
|  | class CosExpr extends Expr { | ||||||
|  |   final Expr inner; | ||||||
|  |   CosExpr(this.inner); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr simplify() => CosExpr(inner.simplify()); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr evaluate() { | ||||||
|  |     var i = inner.evaluate(); | ||||||
|  |     if (i is IntExpr) { | ||||||
|  |       return DoubleExpr(cos(i.value.toDouble())); | ||||||
|  |     } | ||||||
|  |     if (i is FractionExpr) { | ||||||
|  |       return DoubleExpr(cos(i.numerator / i.denominator)); | ||||||
|  |     } | ||||||
|  |     if (i is DoubleExpr) { | ||||||
|  |       return DoubleExpr(cos(i.value)); | ||||||
|  |     } | ||||||
|  |     return CosExpr(i); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() => "cos($inner)"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // === SinExpr === | ||||||
|  | class SinExpr extends Expr { | ||||||
|  |   final Expr inner; | ||||||
|  |   SinExpr(this.inner); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr simplify() => SinExpr(inner.simplify()); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr evaluate() { | ||||||
|  |     var i = inner.evaluate(); | ||||||
|  |     if (i is IntExpr) { | ||||||
|  |       return DoubleExpr(sin(i.value.toDouble())); | ||||||
|  |     } | ||||||
|  |     if (i is FractionExpr) { | ||||||
|  |       return DoubleExpr(sin(i.numerator / i.denominator)); | ||||||
|  |     } | ||||||
|  |     if (i is DoubleExpr) { | ||||||
|  |       return DoubleExpr(sin(i.value)); | ||||||
|  |     } | ||||||
|  |     return SinExpr(i); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() => "sin($inner)"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // === TanExpr === | ||||||
|  | class TanExpr extends Expr { | ||||||
|  |   final Expr inner; | ||||||
|  |   TanExpr(this.inner); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr simplify() => TanExpr(inner.simplify()); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Expr evaluate() { | ||||||
|  |     var i = inner.evaluate(); | ||||||
|  |     if (i is IntExpr) { | ||||||
|  |       return DoubleExpr(tan(i.value.toDouble())); | ||||||
|  |     } | ||||||
|  |     if (i is FractionExpr) { | ||||||
|  |       return DoubleExpr(tan(i.numerator / i.denominator)); | ||||||
|  |     } | ||||||
|  |     if (i is DoubleExpr) { | ||||||
|  |       return DoubleExpr(tan(i.value)); | ||||||
|  |     } | ||||||
|  |     return TanExpr(i); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() => "tan($inner)"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // === 辅助:识别 a * sqrt(X) 形式 === | ||||||
|  | class _SqrtTerm { | ||||||
|  |   final int coef; | ||||||
|  |   final Expr inner; | ||||||
|  |   _SqrtTerm(this.coef, this.inner); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | _SqrtTerm? _asSqrtTerm(Expr e) { | ||||||
|  |   if (e is SqrtExpr) return _SqrtTerm(1, e.inner); | ||||||
|  |   if (e is MulExpr) { | ||||||
|  |     // 可能为 Int * Sqrt or Sqrt * Int | ||||||
|  |     if (e.left is IntExpr && e.right is SqrtExpr) { | ||||||
|  |       return _SqrtTerm((e.left as IntExpr).value, (e.right as SqrtExpr).inner); | ||||||
|  |     } | ||||||
|  |     if (e.right is IntExpr && e.left is SqrtExpr) { | ||||||
|  |       return _SqrtTerm((e.right as IntExpr).value, (e.left as SqrtExpr).inner); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return null; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// 辗转相除法求 gcd | ||||||
|  | int _gcd(int a, int b) => b == 0 ? a : _gcd(b, a % b); | ||||||
							
								
								
									
										111
									
								
								lib/parser.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								lib/parser.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | import 'package:simple_math_calc/calculator.dart'; | ||||||
|  |  | ||||||
|  | class Parser { | ||||||
|  |   final String input; | ||||||
|  |   int pos = 0; | ||||||
|  |  | ||||||
|  |   Parser(this.input); | ||||||
|  |  | ||||||
|  |   bool get isEnd => pos >= input.length; | ||||||
|  |   String get current => isEnd ? '' : input[pos]; | ||||||
|  |  | ||||||
|  |   void eat() => pos++; | ||||||
|  |  | ||||||
|  |   void skipSpaces() { | ||||||
|  |     while (!isEnd && input[pos] == ' ') { | ||||||
|  |       eat(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Expr parse() => parseAdd(); | ||||||
|  |  | ||||||
|  |   Expr parseAdd() { | ||||||
|  |     var expr = parseMul(); | ||||||
|  |     skipSpaces(); | ||||||
|  |     while (!isEnd && (current == '+' || current == '-')) { | ||||||
|  |       var op = current; | ||||||
|  |       eat(); | ||||||
|  |       var right = parseMul(); | ||||||
|  |       expr = op == '+' ? AddExpr(expr, right) : SubExpr(expr, right); | ||||||
|  |       skipSpaces(); | ||||||
|  |     } | ||||||
|  |     return expr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Expr parseMul() { | ||||||
|  |     var expr = parseAtom(); | ||||||
|  |     skipSpaces(); | ||||||
|  |     while (!isEnd && (current == '*' || current == '/')) { | ||||||
|  |       var op = current; | ||||||
|  |       eat(); | ||||||
|  |       var right = parseAtom(); | ||||||
|  |       if (op == '*') { | ||||||
|  |         expr = MulExpr(expr, right); | ||||||
|  |       } else { | ||||||
|  |         expr = DivExpr(expr, right); | ||||||
|  |       } | ||||||
|  |       skipSpaces(); | ||||||
|  |     } | ||||||
|  |     return expr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Expr parseAtom() { | ||||||
|  |     skipSpaces(); | ||||||
|  |     if (current == '(') { | ||||||
|  |       eat(); | ||||||
|  |       var expr = parse(); | ||||||
|  |       if (current != ')') throw Exception("缺少 )"); | ||||||
|  |       eat(); | ||||||
|  |       return expr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (input.startsWith("sqrt", pos)) { | ||||||
|  |       pos += 4; | ||||||
|  |       if (current != '(') throw Exception("sqrt 缺少 ("); | ||||||
|  |       eat(); | ||||||
|  |       var inner = parse(); | ||||||
|  |       if (current != ')') throw Exception("sqrt 缺少 )"); | ||||||
|  |       eat(); | ||||||
|  |       return SqrtExpr(inner); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (input.startsWith("cos", pos)) { | ||||||
|  |       pos += 3; | ||||||
|  |       if (current != '(') throw Exception("cos 缺少 ("); | ||||||
|  |       eat(); | ||||||
|  |       var inner = parse(); | ||||||
|  |       if (current != ')') throw Exception("cos 缺少 )"); | ||||||
|  |       eat(); | ||||||
|  |       return CosExpr(inner); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (input.startsWith("sin", pos)) { | ||||||
|  |       pos += 3; | ||||||
|  |       if (current != '(') throw Exception("sin 缺少 ("); | ||||||
|  |       eat(); | ||||||
|  |       var inner = parse(); | ||||||
|  |       if (current != ')') throw Exception("sin 缺少 )"); | ||||||
|  |       eat(); | ||||||
|  |       return SinExpr(inner); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (input.startsWith("tan", pos)) { | ||||||
|  |       pos += 3; | ||||||
|  |       if (current != '(') throw Exception("tan 缺少 ("); | ||||||
|  |       eat(); | ||||||
|  |       var inner = parse(); | ||||||
|  |       if (current != ')') throw Exception("tan 缺少 )"); | ||||||
|  |       eat(); | ||||||
|  |       return TanExpr(inner); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 解析整数 | ||||||
|  |     var buf = ''; | ||||||
|  |     while (!isEnd && RegExp(r'\d').hasMatch(current)) { | ||||||
|  |       buf += current; | ||||||
|  |       eat(); | ||||||
|  |     } | ||||||
|  |     if (buf.isEmpty) throw Exception("无法解析: $current"); | ||||||
|  |     return IntExpr(int.parse(buf)); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										228
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										228
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -1,6 +1,22 @@ | |||||||
| # Generated by pub | # Generated by pub | ||||||
| # See https://dart.dev/tools/pub/glossary#lockfile | # See https://dart.dev/tools/pub/glossary#lockfile | ||||||
| packages: | packages: | ||||||
|  |   _fe_analyzer_shared: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: _fe_analyzer_shared | ||||||
|  |       sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "85.0.0" | ||||||
|  |   analyzer: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: analyzer | ||||||
|  |       sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "7.7.1" | ||||||
|   ansicolor: |   ansicolor: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -57,6 +73,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "2.0.4" |     version: "2.0.4" | ||||||
|  |   cli_config: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: cli_config | ||||||
|  |       sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "0.2.0" | ||||||
|   cli_util: |   cli_util: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -81,6 +105,22 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "1.19.1" |     version: "1.19.1" | ||||||
|  |   convert: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: convert | ||||||
|  |       sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "3.1.2" | ||||||
|  |   coverage: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: coverage | ||||||
|  |       sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "1.15.0" | ||||||
|   crypto: |   crypto: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -121,6 +161,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "2.1.4" |     version: "2.1.4" | ||||||
|  |   file: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: file | ||||||
|  |       sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "7.0.1" | ||||||
|   flutter: |   flutter: | ||||||
|     dependency: "direct main" |     dependency: "direct main" | ||||||
|     description: flutter |     description: flutter | ||||||
| @@ -176,14 +224,30 @@ packages: | |||||||
|     description: flutter |     description: flutter | ||||||
|     source: sdk |     source: sdk | ||||||
|     version: "0.0.0" |     version: "0.0.0" | ||||||
|  |   frontend_server_client: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: frontend_server_client | ||||||
|  |       sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "4.0.0" | ||||||
|  |   glob: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: glob | ||||||
|  |       sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "2.1.3" | ||||||
|   go_router: |   go_router: | ||||||
|     dependency: "direct main" |     dependency: "direct main" | ||||||
|     description: |     description: | ||||||
|       name: go_router |       name: go_router | ||||||
|       sha256: f02fd7d2a4dc512fec615529824fdd217fecb3a3d3de68360293a551f21634b3 |       sha256: eb059dfe59f08546e9787f895bd01652076f996bcbf485a8609ef990419ad227 | ||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "14.8.1" |     version: "16.2.1" | ||||||
|   google_fonts: |   google_fonts: | ||||||
|     dependency: "direct main" |     dependency: "direct main" | ||||||
|     description: |     description: | ||||||
| @@ -208,6 +272,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "1.5.0" |     version: "1.5.0" | ||||||
|  |   http_multi_server: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: http_multi_server | ||||||
|  |       sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "3.2.2" | ||||||
|   http_parser: |   http_parser: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -224,6 +296,22 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "4.5.4" |     version: "4.5.4" | ||||||
|  |   io: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: io | ||||||
|  |       sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "1.0.5" | ||||||
|  |   js: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: js | ||||||
|  |       sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "0.7.2" | ||||||
|   json_annotation: |   json_annotation: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -312,6 +400,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "1.16.0" |     version: "1.16.0" | ||||||
|  |   mime: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: mime | ||||||
|  |       sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "2.0.0" | ||||||
|   nested: |   nested: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -320,6 +416,22 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "1.0.0" |     version: "1.0.0" | ||||||
|  |   node_preamble: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: node_preamble | ||||||
|  |       sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "2.0.2" | ||||||
|  |   package_config: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: package_config | ||||||
|  |       sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "2.2.0" | ||||||
|   path: |   path: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -408,6 +520,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "2.1.8" |     version: "2.1.8" | ||||||
|  |   pool: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: pool | ||||||
|  |       sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "1.5.1" | ||||||
|   posix: |   posix: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -424,6 +544,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "6.1.5+1" |     version: "6.1.5+1" | ||||||
|  |   pub_semver: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: pub_semver | ||||||
|  |       sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "2.2.0" | ||||||
|   rational: |   rational: | ||||||
|     dependency: "direct main" |     dependency: "direct main" | ||||||
|     description: |     description: | ||||||
| @@ -432,11 +560,59 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "2.2.3" |     version: "2.2.3" | ||||||
|  |   shelf: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: shelf | ||||||
|  |       sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "1.4.2" | ||||||
|  |   shelf_packages_handler: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: shelf_packages_handler | ||||||
|  |       sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "3.0.2" | ||||||
|  |   shelf_static: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: shelf_static | ||||||
|  |       sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "1.1.3" | ||||||
|  |   shelf_web_socket: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: shelf_web_socket | ||||||
|  |       sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "3.0.0" | ||||||
|   sky_engine: |   sky_engine: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: flutter |     description: flutter | ||||||
|     source: sdk |     source: sdk | ||||||
|     version: "0.0.0" |     version: "0.0.0" | ||||||
|  |   source_map_stack_trace: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: source_map_stack_trace | ||||||
|  |       sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "2.1.2" | ||||||
|  |   source_maps: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: source_maps | ||||||
|  |       sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "0.10.13" | ||||||
|   source_span: |   source_span: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -477,6 +653,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "1.2.2" |     version: "1.2.2" | ||||||
|  |   test: | ||||||
|  |     dependency: "direct dev" | ||||||
|  |     description: | ||||||
|  |       name: test | ||||||
|  |       sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "1.26.2" | ||||||
|   test_api: |   test_api: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -485,6 +669,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "0.7.6" |     version: "0.7.6" | ||||||
|  |   test_core: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: test_core | ||||||
|  |       sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "0.6.11" | ||||||
|   tuple: |   tuple: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -613,6 +805,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "15.0.2" |     version: "15.0.2" | ||||||
|  |   watcher: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: watcher | ||||||
|  |       sha256: "5bf046f41320ac97a469d506261797f35254fa61c641741ef32dacda98b7d39c" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "1.1.3" | ||||||
|   web: |   web: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -621,6 +821,30 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "1.1.1" |     version: "1.1.1" | ||||||
|  |   web_socket: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: web_socket | ||||||
|  |       sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "1.0.1" | ||||||
|  |   web_socket_channel: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: web_socket_channel | ||||||
|  |       sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "3.0.3" | ||||||
|  |   webkit_inspection_protocol: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: webkit_inspection_protocol | ||||||
|  |       sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "1.2.1" | ||||||
|   xdg_directories: |   xdg_directories: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
|   | |||||||
| @@ -37,8 +37,8 @@ dependencies: | |||||||
|   math_expressions: ^3.1.0 |   math_expressions: ^3.1.0 | ||||||
|   latext: ^0.5.1 |   latext: ^0.5.1 | ||||||
|   google_fonts: ^6.3.1 |   google_fonts: ^6.3.1 | ||||||
|   go_router: ^14.2.0 |   go_router: ^16.2.1 | ||||||
|   url_launcher: ^6.3.0 |   url_launcher: ^6.3.2 | ||||||
|   rational: ^2.2.3 |   rational: ^2.2.3 | ||||||
|  |  | ||||||
| dev_dependencies: | dev_dependencies: | ||||||
| @@ -53,6 +53,7 @@ dev_dependencies: | |||||||
|   flutter_lints: ^6.0.0 |   flutter_lints: ^6.0.0 | ||||||
|   flutter_native_splash: ^2.4.6 |   flutter_native_splash: ^2.4.6 | ||||||
|   flutter_launcher_icons: ^0.14.4 |   flutter_launcher_icons: ^0.14.4 | ||||||
|  |   test: ^1.26.2 | ||||||
|  |  | ||||||
| # For information on the generic Dart part of this file, see the | # For information on the generic Dart part of this file, see the | ||||||
| # following page: https://dart.dev/tools/pub/pubspec | # following page: https://dart.dev/tools/pub/pubspec | ||||||
|   | |||||||
							
								
								
									
										104
									
								
								test/calculator_test.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								test/calculator_test.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | |||||||
|  | import 'package:simple_math_calc/parser.dart'; | ||||||
|  | import 'package:test/test.dart'; | ||||||
|  |  | ||||||
|  | void main() { | ||||||
|  |   group('整数', () { | ||||||
|  |     test('加法', () { | ||||||
|  |       var expr = Parser("2 + 3").parse(); | ||||||
|  |       expect(expr.evaluate().toString(), "5"); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('乘法', () { | ||||||
|  |       var expr = Parser("4 * 5").parse(); | ||||||
|  |       expect(expr.evaluate().toString(), "20"); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   group('分数', () { | ||||||
|  |     test('简单分数', () { | ||||||
|  |       var expr = Parser("1/2").parse(); | ||||||
|  |       expect(expr.evaluate().toString(), "1/2"); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('分数加法', () { | ||||||
|  |       var expr = Parser("1/2 + 3/4").parse(); | ||||||
|  |       expect(expr.evaluate().toString().replaceAll(' ', ''), "5/4"); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('分数与整数相乘', () { | ||||||
|  |       var expr = Parser("2 * 3/4").parse(); | ||||||
|  |       expect(expr.evaluate().toString(), "3/2"); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   group('开平方', () { | ||||||
|  |     test('完全平方数', () { | ||||||
|  |       var expr = Parser("sqrt(9)").parse(); | ||||||
|  |       expect(expr.evaluate().toString(), "3"); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('非完全平方数', () { | ||||||
|  |       var expr = Parser("sqrt(8)").parse(); | ||||||
|  |       expect(expr.simplify().toString().replaceAll(' ', ''), "(2*sqrt(2))"); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   group('组合表达式', () { | ||||||
|  |     test('sqrt + 整数', () { | ||||||
|  |       var expr = Parser("2 + sqrt(9)").parse(); | ||||||
|  |       expect(expr.simplify().toString().replaceAll(' ', ''), "(2+3)"); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('分数 + sqrt', () { | ||||||
|  |       var expr = Parser("sqrt(8)/4 + 1/2").parse(); | ||||||
|  |       expect( | ||||||
|  |         expr.evaluate().toString().replaceAll(' ', ''), | ||||||
|  |         "((sqrt(2)/2)+1/2)", | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   group('加减除优先级', () { | ||||||
|  |     test('减法', () { | ||||||
|  |       var expr = Parser("5 - 2").parse(); | ||||||
|  |       expect(expr.evaluate().toString(), "3"); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('除法', () { | ||||||
|  |       var expr = Parser("6 / 3").parse(); | ||||||
|  |       expect(expr.evaluate().toString(), "2"); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('加法和乘法优先级', () { | ||||||
|  |       var expr = Parser("1 + 2 * 3").parse(); | ||||||
|  |       expect(expr.evaluate().toString(), "7"); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('加减混合', () { | ||||||
|  |       var expr = Parser("10 - 3 + 2").parse(); | ||||||
|  |       expect(expr.evaluate().toString(), "9"); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('括号优先级', () { | ||||||
|  |       var expr = Parser("(1 + 2) * 3").parse(); | ||||||
|  |       expect(expr.evaluate().toString(), "9"); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   group('三角函数', () { | ||||||
|  |     test('cos(0)', () { | ||||||
|  |       var expr = Parser("cos(0)").parse(); | ||||||
|  |       expect(expr.evaluate().toString(), "1.0"); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('sin(0)', () { | ||||||
|  |       var expr = Parser("sin(0)").parse(); | ||||||
|  |       expect(expr.evaluate().toString(), "0.0"); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('tan(0)', () { | ||||||
|  |       var expr = Parser("tan(0)").parse(); | ||||||
|  |       expect(expr.evaluate().toString(), "0.0"); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user