feat(compiler): 添加可变参数支持功能

- 在 ArgInfo 类中添加 variadic 属性用于标识可变参数
- 实现编译器对可变参数的验证逻辑,确保可变参数位于最后位置
- 修改参数类型处理逻辑,将可变参数转换为数组类型
- 更新函数调用参数解析,支持可变参数的展开和传递
- 在翻译器中实现可变参数的 C++ 代码生成逻辑
- 添加遍历可变参数的循环处理机制
- 新增可变参数功能的测试用例和示例文件
- 完善参数列表生成时对可变参数的特殊处理
pull/1/head
韩天峰 6 months ago
parent 9c51551567
commit aa6d81d2a7
  1. 24
      examples/python/sys.php
  2. 10
      examples/vargs.php
  3. 1
      src/Php/ArgInfo.php
  4. 27
      src/Php/CompilerBase.php
  5. 30
      src/Php/Translator.php
  6. 26
      tests/aot/variadic-args.phpt

@ -0,0 +1,24 @@
<?php
// python: import sys
use sys;
// python: import sos
use os;
function main()
{
var_dump(sys::api_version);
var_dump(os::getpid());
var_dump(os::environ->get("PATH"));
}
use numpy as np;
function np()
{
var_dump(np::$module);
var_dump(np::version->full_version);
}

@ -0,0 +1,10 @@
<?php
function test($a, $b, int...$args)
{
var_dump($args);
}
function main()
{
test(1, 2, 3, 4, 5);
}

@ -13,4 +13,5 @@ class ArgInfo
public string $name; public string $name;
public string $type; public string $type;
public string $default = ''; public string $default = '';
public bool $variadic = false;
} }

@ -867,7 +867,8 @@ class CompilerBase extends \PhpAot\Core\Translator
{ {
$list = []; $list = [];
$functionDef->argCountRequired = count($params); $functionDef->argCountRequired = count($params);
foreach ($params as $param) { $last = array_key_last($params);
foreach ($params as $i => $param) {
// .stub 存根定义 C++ Native 函数,必须设置函数的参数类型 // .stub 存根定义 C++ Native 函数,必须设置函数的参数类型
if ($this->stubFile and !$param->type) { if ($this->stubFile and !$param->type) {
throw new \RuntimeException('No type for ' . $this->parseIdentifier($param->var)); throw new \RuntimeException('No type for ' . $this->parseIdentifier($param->var));
@ -875,12 +876,20 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($param->byRef) { if ($param->byRef) {
$this->fatalError($param, 'ByRef parameters are not supported'); $this->fatalError($param, 'ByRef parameters are not supported');
} }
if ($param->variadic and $i !== $last) {
$this->fatalError($param, 'Variadic parameters must be the last parameter');
}
$name = $this->parseIdentifier($param->var); $name = $this->parseIdentifier($param->var);
$type = $this->parseParameterType($param, $name); $type = $this->parseParameterType($param, $name);
$list[] = $type . ' ' . $name; if ($param->variadic) {
$list[] = self::TYPE_ARRAY . ' ' . $name;
} else {
$list[] = $type . ' ' . $name;
}
$argInfo = new ArgInfo(); $argInfo = new ArgInfo();
$argInfo->name = $name; $argInfo->name = $name;
$argInfo->type = $type; $argInfo->type = $type;
$argInfo->variadic = $param->variadic;
if (isset($param->default)) { if (isset($param->default)) {
$functionDef->argCountRequired = count($list) - 1; $functionDef->argCountRequired = count($list) - 1;
$argInfo->default = $this->parseIdentifier($param->default); $argInfo->default = $this->parseIdentifier($param->default);
@ -1957,8 +1966,18 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($arg->name !== null) { if ($arg->name !== null) {
$this->fatalError($arg, 'Named arguments are not supported'); $this->fatalError($arg, 'Named arguments are not supported');
} }
$argInfo = $this->getArgInfo($arg, $nativeFunc, $i); $argInfo = $this->getArgInfo($arg, $nativeFunc, $i);
$list_args[] = $this->getTypeConvertedArg($arg, $argInfo); if ($argInfo->variadic) {
$vargs = array_slice($args, $i);
$list_vargs = [];
foreach ($vargs as $varg) {
$list_vargs[] = $this->getTypeConvertedArg($varg, $argInfo);
}
$list_args[] = '{' . implode(', ', $list_vargs) . '}';
break;
} else {
$list_args[] = $this->getTypeConvertedArg($arg, $argInfo);
}
} }
return implode(', ', $list_args); return implode(', ', $list_args);

@ -300,9 +300,13 @@ class Translator extends Preprocessor
$argInfoList = $func->argInfoList; $argInfoList = $func->argInfoList;
if ($argInfoList) { if ($argInfoList) {
foreach ($argInfoList as $argInfo) { foreach ($argInfoList as $argInfo) {
$arg = $argInfo->type . ' ' . $argInfo->name; if ($argInfo->variadic) {
if ($argInfo->default) { $arg = self::TYPE_ARRAY . ' ' . $argInfo->name . '()';
$arg .= ' = ' . $argInfo->default; } else {
$arg = $argInfo->type . ' ' . $argInfo->name;
if ($argInfo->default) {
$arg .= ' = ' . $argInfo->default;
}
} }
$list[] = $arg; $list[] = $arg;
} }
@ -734,13 +738,23 @@ class Translator extends Preprocessor
$cppCode = ''; $cppCode = '';
$callParams = ''; $callParams = '';
foreach ($functionDef->argInfoList as $k => $argInfo) { foreach ($functionDef->argInfoList as $k => $argInfo) {
if ($argInfo->default) { $var = 'arg_' . $argInfo->name;
$argExpr = 'php::getCallArg(' . $k . ', ' . $argInfo->default . ')'; if ($argInfo->variadic) {
$cppCode .= $this->getIndent() . self::TYPE_ARRAY . ' ' . $var . ';' . PHP_EOL;
$cppCode .= $this->getIndent() . 'for (uint32_t i = ' . $k . '; i < php::getCallArgNum(); i++) {' . PHP_EOL;
$this->indentLevel++;
$cppCode .= $this->getIndent() . $var . '.append(php::getCallArg(i));' . PHP_EOL;
$this->indentLevel--;
$cppCode .= '}' . PHP_EOL;
} else { } else {
$argExpr = 'php::getCallArg(' . $k . ')'; if ($argInfo->default) {
$argExpr = 'php::getCallArg(' . $k . ', ' . $argInfo->default . ')';
} else {
$argExpr = 'php::getCallArg(' . $k . ')';
}
$expr = $this->convertExprFromType($argInfo->type, $argExpr);
$cppCode .= $this->getIndent() . $argInfo->type . ' ' . $var . ' = ' . $expr . ';' . PHP_EOL;
} }
$expr = $this->convertExprFromType($argInfo->type, $argExpr);
$cppCode .= $this->getIndent() . $argInfo->type . ' arg_' . $argInfo->name . ' = ' . $expr . ';' . PHP_EOL;
$callParams .= 'arg_' . $argInfo->name . ','; $callParams .= 'arg_' . $argInfo->name . ',';
} }

@ -0,0 +1,26 @@
--TEST--
variadic args
--FILE--
<?php
function test($a, $b, int...$args)
{
var_dump($a, $b, $args);
}
function main()
{
test(1, 2, 3, 4, 5);
}
?>
--EXPECT--
int(1)
int(2)
array(3) {
[0]=>
int(3)
[1]=>
int(4)
[2]=>
int(5)
}
Loading…
Cancel
Save