From c604fc09ab352c7df43764a6beba2c1a2c0f5f30 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 29 May 2026 18:59:30 +0800 Subject: [PATCH] =?UTF-8?q?feat(compiler):=20=E6=B7=BB=E5=8A=A0=E5=AF=B9?= =?UTF-8?q?=20Big*=20=E7=B1=BB=E5=9E=8B=E7=9A=84=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E8=BD=AC=E6=8D=A2=E5=92=8C=E6=95=B0=E5=AD=A6=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E4=BC=98=E5=8C=96=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 echo 表达式中实现 BigInt、BigFloat 和 Decimal 类型的自动 toString 转换 - 为 abs、pow、sqrt、floor、ceil、round 数学函数添加 Big* 类型返回值推断 - 优化二进制连接操作中的类型转换逻辑,支持 Big* 类型到字符串的转换 - 为 (string) 强制转换、strval() 函数和 floor/ceil/round 函数添加 Big* 类型支持 - 新增测试用例验证 Big* 类型的数学函数优化功能 - 添加 Decimal 类型的精确计算和舍入处理示例 --- examples/decimal/decimal.php | 10 +++++ src/Php/CompilerBase.php | 63 ++++++++++++++++++++++++++--- src/Php/FuncCallOptimizer.php | 62 +++++++++++++++++++++++++++- tests/aot/bigint/math_func_opt.phpt | 33 +++++++++++++++ tests/aot/bigint/toString_cast.phpt | 37 +++++++++++++++++ tests/aot/decimal/floor-02.phpt | 17 ++++++++ tests/aot/decimal/round-02.phpt | 25 ++++++++++++ 7 files changed, 241 insertions(+), 6 deletions(-) create mode 100644 examples/decimal/decimal.php create mode 100644 tests/aot/bigint/math_func_opt.phpt create mode 100644 tests/aot/bigint/toString_cast.phpt create mode 100644 tests/aot/decimal/floor-02.phpt create mode 100644 tests/aot/decimal/round-02.phpt diff --git a/examples/decimal/decimal.php b/examples/decimal/decimal.php new file mode 100644 index 00000000..51dc65f4 --- /dev/null +++ b/examples/decimal/decimal.php @@ -0,0 +1,10 @@ +fatalError($expr, 'Cannot echo assign expression'); } else { - $lines[] = 'php::echo(' . $this->parseExpr($expr) . ');'; + $type = $this->detectTypeOfExpr($expr); + $parsed = $this->parseExpr($expr); + if ($type === self::TYPE_BIGINT) { + $parsed = 'php::BigInt::toString(' . $parsed . ')'; + } elseif ($type === self::TYPE_BIGFLOAT) { + $parsed = 'php::BigFloat::toString(' . $parsed . ')'; + } elseif ($type === self::TYPE_DECIMAL) { + $parsed = 'php::Decimal::toString(' . $parsed . ')'; + } + $lines[] = 'php::echo(' . $parsed . ');'; } } @@ -2460,6 +2469,25 @@ class CompilerBase extends \PhpAot\Core\Translator case 'Expr_FuncCall': if ($this->isNameExpr($expr->name)) { $name = $this->parseIdentifier($expr->name); + // Math function optimization: propagate Big* return types + if (in_array($name, ['abs', 'pow', 'sqrt', 'floor', 'ceil', 'round'], true) && !empty($expr->args)) { + $argType = $this->detectTypeOfExpr($expr->args[0]->value); + if ( + $argType === self::TYPE_BIGINT + && in_array($name, ['abs', 'pow', 'sqrt'], true) + ) { + return self::TYPE_BIGINT; + } + if ( + $argType === self::TYPE_DECIMAL + && in_array($name, ['abs', 'pow', 'sqrt', 'floor', 'ceil', 'round'], true) + ) { + return self::TYPE_DECIMAL; + } + if ($argType === self::TYPE_BIGFLOAT && $name === 'abs') { + return self::TYPE_BIGFLOAT; + } + } if (in_array($name, self::STREAM_FUNCTIONS) || $name === 'stream_cast') { return self::TYPE_STREAM; } @@ -2976,15 +3004,29 @@ class CompilerBase extends \PhpAot\Core\Translator protected function parseBinaryOpConcat(Expr\BinaryOp\Concat $expr): string { - $left = $this->parseIdentifier($expr->left); - $right = $this->parseIdentifier($expr->right); + $left = $this->parseExpr($expr->left); + $right = $this->parseExpr($expr->right); $leftType = $this->detectTypeOfExpr($expr->left); $rightType = $this->detectTypeOfExpr($expr->right); if ($leftType === self::TYPE_VOID or $rightType === self::TYPE_VOID) { $this->fatalError($expr, 'Cannot concat void'); } - return Symbol::concat() . '(' . $this->convertStringExpr($left) . ', ' . $this->convertStringExpr($right) . ')'; + return Symbol::concat() . '(' . $this->convertExprToStringByType($left, $leftType) . ', ' . $this->convertExprToStringByType($right, $rightType) . ')'; + } + + protected function convertExprToStringByType(string $expr, $type): string + { + if ($type === self::TYPE_BIGINT) { + return 'php::BigInt::toString(' . $expr . ')'; + } + if ($type === self::TYPE_BIGFLOAT) { + return 'php::BigFloat::toString(' . $expr . ')'; + } + if ($type === self::TYPE_DECIMAL) { + return 'php::Decimal::toString(' . $expr . ')'; + } + return $this->convertStringExpr($expr); } protected function parseFor(Node\Stmt\For_ $v): string @@ -4348,7 +4390,18 @@ class CompilerBase extends \PhpAot\Core\Translator protected function parseCastString(Expr\Cast\String_ $node): string { - return $this->convertStringExpr($this->parseExpr($node->expr)); + $type = $this->detectTypeOfExpr($node->expr); + $expr = $this->parseExpr($node->expr); + if ($type === self::TYPE_BIGINT) { + return 'php::BigInt::toString(' . $expr . ')'; + } + if ($type === self::TYPE_BIGFLOAT) { + return 'php::BigFloat::toString(' . $expr . ')'; + } + if ($type === self::TYPE_DECIMAL) { + return 'php::Decimal::toString(' . $expr . ')'; + } + return $this->convertStringExpr($expr); } protected function parseCastBool(Expr\Cast\Bool_ $node): string diff --git a/src/Php/FuncCallOptimizer.php b/src/Php/FuncCallOptimizer.php index edc124c6..278c22c0 100644 --- a/src/Php/FuncCallOptimizer.php +++ b/src/Php/FuncCallOptimizer.php @@ -69,7 +69,19 @@ trait FuncCallOptimizer case 'boolval': return $this->convertBoolExpr($this->parseExpr($expr->args[0]->value)); case 'strval': - return $this->convertStringExpr($this->parseExpr($expr->args[0]->value)); + $arg = $expr->args[0]->value; + $type = $this->detectTypeOfExpr($arg); + $parsed = $this->parseExpr($arg); + if ($type === self::TYPE_BIGINT) { + return 'php::BigInt::toString(' . $parsed . ')'; + } + if ($type === self::TYPE_BIGFLOAT) { + return 'php::BigFloat::toString(' . $parsed . ')'; + } + if ($type === self::TYPE_DECIMAL) { + return 'php::Decimal::toString(' . $parsed . ')'; + } + return $this->convertStringExpr($parsed); default: break; } @@ -91,11 +103,59 @@ trait FuncCallOptimizer } } if ($name === 'abs') { + $type = $this->detectTypeOfExpr($expr->args[0]->value); + if ($type === self::TYPE_BIGINT) { + return 'php::BigInt::abs(' . $this->parseExpr($expr->args[0]->value) . ')'; + } + if ($type === self::TYPE_BIGFLOAT) { + return 'php::BigFloat::abs(' . $this->parseExpr($expr->args[0]->value) . ')'; + } + if ($type === self::TYPE_DECIMAL) { + return 'php::Decimal::abs(' . $this->parseExpr($expr->args[0]->value) . ')'; + } return 'php::math::abs(' . $getArg(0) . ')'; } if ($name === 'pow') { + $type = $this->detectTypeOfExpr($expr->args[0]->value); + if ($type === self::TYPE_BIGINT) { + return 'php::BigInt::pow(' . $this->parseExpr($expr->args[0]->value) . ', ' . $this->parseExpr($expr->args[1]->value) . ')'; + } + if ($type === self::TYPE_DECIMAL) { + return 'php::Decimal::pow(' . $this->parseExpr($expr->args[0]->value) . ', ' . $this->parseExpr($expr->args[1]->value) . ')'; + } return 'php::math::pow(' . $getArg(0) . ', ' . $getArg(1) . ')'; } + if ($name === 'sqrt') { + $type = $this->detectTypeOfExpr($expr->args[0]->value); + if ($type === self::TYPE_BIGINT) { + return 'php::BigInt::sqrt(' . $this->parseExpr($expr->args[0]->value) . ')'; + } + if ($type === self::TYPE_DECIMAL) { + return 'php::Decimal::sqrt(' . $this->parseExpr($expr->args[0]->value) . ')'; + } + } + if ($name === 'floor') { + $type = $this->detectTypeOfExpr($expr->args[0]->value); + if ($type === self::TYPE_DECIMAL) { + return 'php::Decimal::floor(' . $this->parseExpr($expr->args[0]->value) . ')'; + } + } + if ($name === 'ceil') { + $type = $this->detectTypeOfExpr($expr->args[0]->value); + if ($type === self::TYPE_DECIMAL) { + return 'php::Decimal::ceil(' . $this->parseExpr($expr->args[0]->value) . ')'; + } + } + if ($name === 'round') { + $type = $this->detectTypeOfExpr($expr->args[0]->value); + if ($type === self::TYPE_DECIMAL) { + $arg0 = $this->parseExpr($expr->args[0]->value); + if (count($expr->args) >= 2) { + return 'php::Decimal::round(' . $arg0 . ', ' . $this->parseExpr($expr->args[1]->value) . ')'; + } + return 'php::Decimal::round(' . $arg0 . ')'; + } + } if ($name === 'strlen') { return 'php::fn::strlen(' . $getArg(0) . ')'; } diff --git a/tests/aot/bigint/math_func_opt.phpt b/tests/aot/bigint/math_func_opt.phpt new file mode 100644 index 00000000..70526db9 --- /dev/null +++ b/tests/aot/bigint/math_func_opt.phpt @@ -0,0 +1,33 @@ +--TEST-- +Big* types: math function optimization (abs/pow/sqrt/floor/ceil/round) +--FILE-- + +--EXPECT-- +OK diff --git a/tests/aot/bigint/toString_cast.phpt b/tests/aot/bigint/toString_cast.phpt new file mode 100644 index 00000000..7de4513f --- /dev/null +++ b/tests/aot/bigint/toString_cast.phpt @@ -0,0 +1,37 @@ +--TEST-- +Big* types: (string) cast, strval() and echo → toString() +--FILE-- + +--EXPECT-- +string(20) "12345678901234567890" +string(20) "12345678901234567890" +12345678901234567890 +string(18) "3.1415926535897931" +string(18) "3.1415926535897931" +3.1415926535897931 +string(11) "0.123456789" +string(11) "0.123456789" +0.123456789 diff --git a/tests/aot/decimal/floor-02.phpt b/tests/aot/decimal/floor-02.phpt new file mode 100644 index 00000000..5c4abf42 --- /dev/null +++ b/tests/aot/decimal/floor-02.phpt @@ -0,0 +1,17 @@ +--TEST-- +decimal: literal +--FILE-- + +--EXPECT-- +1.79 \ No newline at end of file diff --git a/tests/aot/decimal/round-02.phpt b/tests/aot/decimal/round-02.phpt new file mode 100644 index 00000000..fb1610c9 --- /dev/null +++ b/tests/aot/decimal/round-02.phpt @@ -0,0 +1,25 @@ +--TEST-- +Decimal: round +--FILE-- + +--EXPECT-- +小计: 89.97 +税额: 7.1976 +总计: 97.17