diff --git a/bin/arg_info.php b/bin/arg_info.php new file mode 100644 index 00000000..a61a5d07 --- /dev/null +++ b/bin/arg_info.php @@ -0,0 +1,4 @@ +isPassedByReference()); \ No newline at end of file diff --git a/examples/div.php b/examples/div.php new file mode 100644 index 00000000..62fec320 --- /dev/null +++ b/examples/div.php @@ -0,0 +1,8 @@ +getName(); } + + public static function getFunctionParameter(string $fn, int $index): ?ReflectionParameter + { + $func = self::getFunction($fn); + if (!$func) { + return null; + } + $args = $func->getParameters(); + if ($index >= count($args)) { + return null; + } + return $args[$index]; + } } \ No newline at end of file diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 197609cf..c91b28eb 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -114,6 +114,8 @@ class Translator extends \PhpAot\Core\Translator private int $debugLine = 0; private CLImate $climate; private array $definedFunctions = []; + private array $beforeStmtLines = []; + private array $afterStmtLines = []; public function __construct(string $rootPath) { @@ -237,6 +239,7 @@ class Translator extends \PhpAot\Core\Translator private function doConvert(string $phpCode) { + $this->climate->info('do convert'); $parser = (new ParserFactory())->createForNewestSupportedVersion(); $ast = $parser->parse($phpCode); @@ -397,10 +400,11 @@ class Translator extends \PhpAot\Core\Translator { $type = $expr->getType(); switch ($type) { - case 'Name': - case 'Identifier': case 'Expr_Variable': return $this->escapeVarName($expr->name); + case 'Name': + case 'Identifier': + return $expr->name; case 'Scalar_Int': return $expr->value . 'L'; case 'Scalar_Float': @@ -440,9 +444,10 @@ class Translator extends \PhpAot\Core\Translator private function parseStmts(array $stmts): string { $lines = []; - foreach ($stmts as $v) { $class = $v->getType(); + $this->beforeStmtLines = []; + $this->afterStmtLines = []; $this->writeLog('Line ' . $this->getLine($v) . ': ' . $class); switch ($class) { case 'Stmt_Expression': @@ -493,7 +498,9 @@ class Translator extends \PhpAot\Core\Translator default: abort($v); } + $lines = array_merge($lines, $this->beforeStmtLines); $lines[] = $result; + $lines = array_merge($lines, $this->afterStmtLines); } $code = ''; @@ -837,6 +844,7 @@ class Translator extends \PhpAot\Core\Translator case 'Scalar_Int': return self::TYPE_INT; case 'Expr_Cast_Float': + case 'Expr_Cast_Double': case 'Scalar_Float': return self::TYPE_FLOAT; case 'Expr_Cast_Bool': @@ -1169,6 +1177,7 @@ class Translator extends \PhpAot\Core\Translator break; } } + if (empty($expr->args)) { return 'php::call(' . $fn . ')'; } else { @@ -1181,20 +1190,30 @@ class Translator extends \PhpAot\Core\Translator $internalFunction = $this->isInternalFunction($funcName); $list_args = []; foreach ($args as $i => $arg) { - if ($internalFunction) { - $argInfo = $this->getArgInfo($funcName, $i); - $list_args[] = $this->getTypeConvertedArg($arg, $argInfo); - } else { - $list_args[] = $this->parseArg($arg); - } - - // 调用了不存在的变量,可能是引用 if ($arg->value->getType() === 'Expr_Variable') { $name = $this->parseIdentifier($arg->value); + // 调用了不存在的变量,可能是引用 if (!$this->hasVar($name)) { $this->addLocalVar($name, self::TYPE_REF); + } else { + $funcArg = Reflection::getFunctionParameter($funcName, 0); + // 需要引用类型的参数,使用临时变量作为引用,并替换掉实际的参数 + if ($funcArg and $funcArg->isPassedByReference()) { + $tmpVar = $this->genTmpVarName(); + $this->addLocalVar($tmpVar, self::TYPE_REF); + $this->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($arg->value) . '.toReference();'; + $this->afterStmtLines[] = $name . ' = *' . $tmpVar . ';'; + $list_args[] = $tmpVar; + continue; + } } } + if ($internalFunction) { + $argInfo = $this->getArgInfo($funcName, $i); + $list_args[] = $this->getTypeConvertedArg($arg, $argInfo); + } else { + $list_args[] = $this->parseArg($arg); + } } return implode(', ', $list_args); } @@ -1670,7 +1689,7 @@ class Translator extends \PhpAot\Core\Translator } $valueVar = $this->parseIdentifier($node->valueVar); - $iteratorVar = $this->addTmpVar(); + $iteratorVar = $this->genTmpVarName(); $stmts = $node->stmts; $code = ''; @@ -1712,7 +1731,7 @@ class Translator extends \PhpAot\Core\Translator shell_exec($cmd); } - private function addTmpVar(): string + private function genTmpVarName(): string { return 'tmp_var_' . $this->tmpVarIndex++; } @@ -1735,7 +1754,7 @@ class Translator extends \PhpAot\Core\Translator private function parseSwitch(mixed $v): string { $cond = $v->cond; - $tmp_var = $this->addTmpVar(); + $tmp_var = $this->genTmpVarName(); $type = $this->detectExprType($cond); $var_def = $type . ' ' . $tmp_var . ' = ' . $this->parseExpr($cond) . ';' . PHP_EOL; diff --git a/tests/aot/arrays.phpt b/tests/aot/arrays.phpt new file mode 100644 index 00000000..3515b986 --- /dev/null +++ b/tests/aot/arrays.phpt @@ -0,0 +1,196 @@ +--TEST-- +Arrays - Indexed, Associative, and Multidimensional +--FILE-- + "John", + "age" => 30, + "city" => "New York" +]; +var_dump($assoc); + +// Test array access +$first_element = $indexed[0]; +var_dump($first_element); + +$name = $assoc["name"]; +var_dump($name); + +// Test array modification +$indexed[0] = 10; +var_dump($indexed[0]); + +$assoc["age"] = 35; +var_dump($assoc["age"]); + +// Test array functions +$numbers = [3, 1, 4, 1, 5, 9, 2, 6]; +sort($numbers); +var_dump($numbers); + +$assoc2 = [ + "product" => "Laptop", + "price" => 999.99, + "in_stock" => true +]; + +$keys = array_keys($assoc2); +var_dump($keys); + +$values = array_values($assoc2); +var_dump($values); + +// Test multidimensional array +$multi = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] +]; +var_dump($multi); + +// Access multidimensional array +$element = $multi[1][2]; // Should be 6 +var_dump($element); + +// Test array push +$stack = []; +array_push($stack, "first"); +array_push($stack, "second"); +array_push($stack, "third"); +var_dump($stack); + +// Test foreach loop with arrays +$output = []; +foreach ($indexed as $value) { + $output[] = $value * 2; +} +var_dump($output); + +$assoc_output = []; +foreach ($assoc as $key => $value) { + $assoc_output[$key] = "Value: " . $value; +} +var_dump($assoc_output); +?> +--EXPECT-- +array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) +} +array(3) { + ["name"]=> + string(4) "John" + ["age"]=> + int(30) + ["city"]=> + string(8) "New York" +} +int(1) +string(4) "John" +int(10) +int(35) +array(8) { + [0]=> + int(1) + [1]=> + int(1) + [2]=> + int(2) + [3]=> + int(3) + [4]=> + int(4) + [5]=> + int(5) + [6]=> + int(6) + [7]=> + int(9) +} +array(3) { + [0]=> + string(7) "product" + [1]=> + string(5) "price" + [2]=> + string(8) "in_stock" +} +array(3) { + [0]=> + string(6) "Laptop" + [1]=> + float(999.99) + [2]=> + bool(true) +} +array(3) { + [0]=> + array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + } + [1]=> + array(3) { + [0]=> + int(4) + [1]=> + int(5) + [2]=> + int(6) + } + [2]=> + array(3) { + [0]=> + int(7) + [1]=> + int(8) + [2]=> + int(9) + } +} +int(6) +array(3) { + [0]=> + string(5) "first" + [1]=> + string(6) "second" + [2]=> + string(5) "third" +} +array(5) { + [0]=> + int(20) + [1]=> + int(4) + [2]=> + int(6) + [3]=> + int(8) + [4]=> + int(10) +} +array(3) { + ["name"]=> + string(11) "Value: John" + ["age"]=> + string(9) "Value: 35" + ["city"]=> + string(15) "Value: New York" +} diff --git a/tests/aot/basic_variables.phpt b/tests/aot/basic_variables.phpt new file mode 100644 index 00000000..ad530964 --- /dev/null +++ b/tests/aot/basic_variables.phpt @@ -0,0 +1,48 @@ +--TEST-- +Basic Variables and Data Types +--FILE-- + +--EXPECT-- +int(42) +float(3.14) +bool(true) +string(13) "Hello, World!" +int(13) +int(7) +int(30) +float(3.3333333333333335) +int(1) +string(11) "Hello World" \ No newline at end of file