From 5cf66cd1f2fd78f982ce0e0141aeaa81f4b1eb35 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 14 Sep 2025 14:13:14 +0800 Subject: [PATCH] :sparkles: Percentage --- lib/calculator.dart | 90 +++++++++++++++++++++++++++++++++++++++ lib/parser.dart | 16 ++++++- test/calculator_test.dart | 42 ++++++++++++++++++ 3 files changed, 147 insertions(+), 1 deletion(-) diff --git a/lib/calculator.dart b/lib/calculator.dart index 20cfedf..35aedea 100644 --- a/lib/calculator.dart +++ b/lib/calculator.dart @@ -151,6 +151,17 @@ class AddExpr extends Expr { return IntExpr(l.value + r.value); } + // 小数相加 + if (l is DoubleExpr && r is DoubleExpr) { + return DoubleExpr(l.value + r.value); + } + if (l is IntExpr && r is DoubleExpr) { + return DoubleExpr(l.value + r.value); + } + if (l is DoubleExpr && r is IntExpr) { + return DoubleExpr(l.value + r.value); + } + // 分数相加 / 分数与整数相加 if (l is FractionExpr && r is FractionExpr) { return FractionExpr( @@ -171,6 +182,14 @@ class AddExpr extends Expr { ).simplify(); } + // 分数与小数相加 + if (l is FractionExpr && r is DoubleExpr) { + return DoubleExpr(l.numerator / l.denominator + r.value); + } + if (l is DoubleExpr && r is FractionExpr) { + return DoubleExpr(l.value + r.numerator / r.denominator); + } + // 合并同类的 sqrt 项: a*sqrt(X) + b*sqrt(X) = (a+b)*sqrt(X) var a = _asSqrtTerm(l); var b = _asSqrtTerm(r); @@ -221,6 +240,18 @@ class SubExpr extends Expr { if (l is IntExpr && r is IntExpr) { return IntExpr(l.value - r.value); } + + // 小数相减 + if (l is DoubleExpr && r is DoubleExpr) { + return DoubleExpr(l.value - r.value); + } + if (l is IntExpr && r is DoubleExpr) { + return DoubleExpr(l.value - r.value); + } + if (l is DoubleExpr && r is IntExpr) { + return DoubleExpr(l.value - r.value); + } + if (l is FractionExpr && r is FractionExpr) { return FractionExpr( l.numerator * r.denominator - r.numerator * l.denominator, @@ -240,6 +271,14 @@ class SubExpr extends Expr { ).simplify(); } + // 分数与小数相减 + if (l is FractionExpr && r is DoubleExpr) { + return DoubleExpr(l.numerator / l.denominator - r.value); + } + if (l is DoubleExpr && r is FractionExpr) { + return DoubleExpr(l.value - r.numerator / r.denominator); + } + // 处理同类 sqrt 项: a*sqrt(X) - b*sqrt(X) = (a-b)*sqrt(X) var a = _asSqrtTerm(l); var b = _asSqrtTerm(r); @@ -311,6 +350,18 @@ class MulExpr extends Expr { if (l is IntExpr && r is IntExpr) { return IntExpr(l.value * r.value); } + + // 小数相乘 + if (l is DoubleExpr && r is DoubleExpr) { + return DoubleExpr(l.value * r.value); + } + if (l is IntExpr && r is DoubleExpr) { + return DoubleExpr(l.value * r.value); + } + if (l is DoubleExpr && r is IntExpr) { + return DoubleExpr(l.value * r.value); + } + if (l is FractionExpr && r is IntExpr) { return FractionExpr(l.numerator * r.value, l.denominator).simplify(); } @@ -324,6 +375,14 @@ class MulExpr extends Expr { ).simplify(); } + // 分数与小数相乘 + if (l is FractionExpr && r is DoubleExpr) { + return DoubleExpr(l.numerator / l.denominator * r.value); + } + if (l is DoubleExpr && r is FractionExpr) { + return DoubleExpr(l.value * r.numerator / r.denominator); + } + // sqrt * sqrt: sqrt(a)*sqrt(a) = a if (l is SqrtExpr && r is SqrtExpr && @@ -856,6 +915,37 @@ class AbsExpr extends Expr { String toString() => "|$inner|"; } +// === PercentExpr === +class PercentExpr extends Expr { + final Expr inner; + PercentExpr(this.inner); + + @override + Expr simplify() => PercentExpr(inner.simplify()); + + @override + Expr evaluate() { + var i = inner.evaluate(); + if (i is IntExpr) { + return DoubleExpr(i.value / 100.0); + } + if (i is DoubleExpr) { + return DoubleExpr(i.value / 100.0); + } + if (i is FractionExpr) { + return DoubleExpr(i.numerator / (i.denominator * 100.0)); + } + return PercentExpr(i); + } + + @override + Expr substitute(String varName, Expr value) => + PercentExpr(inner.substitute(varName, value)); + + @override + String toString() => "$inner%"; +} + // === 辅助:识别 a * sqrt(X) 形式 === class _SqrtTerm { final int coef; diff --git a/lib/parser.dart b/lib/parser.dart index 7cb2fce..1d9a750 100644 --- a/lib/parser.dart +++ b/lib/parser.dart @@ -17,7 +17,15 @@ class Parser { } } - Expr parse() => parseAdd(); + Expr parse() { + var expr = parseAdd(); + skipSpaces(); + if (!isEnd && current == '%') { + eat(); + expr = PercentExpr(expr); + } + return expr; + } Expr parseAdd() { var expr = parseMul(); @@ -46,6 +54,12 @@ class Parser { } skipSpaces(); } + // Handle percentage operator + skipSpaces(); + if (!isEnd && current == '%') { + eat(); + expr = PercentExpr(expr); + } return expr; } diff --git a/test/calculator_test.dart b/test/calculator_test.dart index 988acb2..ac2c069 100644 --- a/test/calculator_test.dart +++ b/test/calculator_test.dart @@ -231,4 +231,46 @@ void main() { ); }); }); + + group('百分比运算符', () { + test('基本百分比', () { + var expr = Parser("50%").parse(); + expect(expr.evaluate().toString(), "0.5"); + }); + + test('100%', () { + var expr = Parser("100%").parse(); + expect(expr.evaluate().toString(), "1.0"); + }); + + test('25%', () { + var expr = Parser("25%").parse(); + expect(expr.evaluate().toString(), "0.25"); + }); + + test('负百分比', () { + var expr = Parser("-50%").parse(); + expect(expr.evaluate().toString(), "-0.5"); + }); + + test('小数百分比', () { + var expr = Parser("50.5%").parse(); + expect(expr.evaluate().toString(), "0.505"); + }); + + test('分数百分比', () { + var expr = Parser("1/2%").parse(); + expect(expr.evaluate().toString(), "0.005"); + }); + + test('百分比在表达式中', () { + var expr = Parser("50% + 25%").parse(); + expect(expr.evaluate().toString(), "0.75"); + }); + + test('百分比与数字相乘', () { + var expr = Parser("2 * 50%").parse(); + expect(expr.evaluate().toString(), "1.0"); + }); + }); }