命名空间和 use 语法支持 [1] ,仅 function,TODO class

pull/1/head
韩天峰 8 months ago
parent 955a3b0e4f
commit 68c272355c
  1. 36
      examples/c/nfft.cc
  2. 16
      examples/call.php
  3. 13
      examples/test/call.php
  4. 8
      examples/test/main.php
  5. 166
      src/Php/Translator.php

@ -0,0 +1,36 @@
#include <nfft3.h>
#include <iostream>
#include <vector>
int main() {
const int d = 1; // 维度(1D)
const int M = 1000; // 非均匀点数量
const int N[] = {2048}; // 均匀网格大小(频域)
// 分配内存
nfft_plan plan;
nfft_init_1d(&plan, N[0], M);
// 设置非均匀采样点 x_j ∈ [-0.5, 0.5)
for (int j = 0; j < M; ++j) {
plan.x[j] = (double)j / M - 0.5; // 示例:均匀分布,实际可任意
}
// 设置源系数 c_j(复数)
for (int j = 0; j < M; ++j) {
plan.f_hat[j][0] = 1.0; // 实部
plan.f_hat[j][1] = 0.0; // 虚部
}
// 执行 NFFT(Type 1)
nfft_adjoint(&plan); // 注意:NFFT3 中 Type 1 用 nfft_adjoint!
// 输出部分结果
for (int k = 0; k < 10; ++k) {
std::cout << "f[" << k << "] = " << plan.f[k] << std::endl;
}
// 清理
nfft_finalize(&plan);
return 0;
}

@ -1,16 +0,0 @@
<?php
function fn1()
{
echo "fn1\n";
fn2();
}
function fn2()
{
echo __FUNCTION__ . "\n";
}
fn1();
include __DIR__ . '/expr1.php';
expr1();

@ -0,0 +1,13 @@
<?php
namespace app\test {
function fn1()
{
echo "fn1\n";
fn2();
}
function fn2()
{
echo __FUNCTION__ . "\n";
}
}

@ -0,0 +1,8 @@
<?php
use function app\test\fn1;
function main()
{
fn1();
}

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace PhpAot\Php; namespace PhpAot\Php;
@ -11,6 +12,7 @@ use PhpParser\Error;
use PhpParser\Node\NullableType; use PhpParser\Node\NullableType;
use PhpParser\NodeFinder; use PhpParser\NodeFinder;
use PhpParser\NodeTraverser; use PhpParser\NodeTraverser;
use PhpParser\Parser;
use PhpParser\ParserFactory; use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter; use PhpParser\PrettyPrinter;
use RuntimeException; use RuntimeException;
@ -110,10 +112,12 @@ class Translator extends \PhpAot\Core\Translator
private bool $debugInfo = true; private bool $debugInfo = true;
private bool $noLiteralStrings = false; private bool $noLiteralStrings = false;
private bool $verbose = false; private bool $verbose = false;
private bool $useCppNamespace = false;
private string $file; private string $file;
private string $dir; private string $dir;
private string $namespace = ''; private string $namespace = '';
private array $uses = []; private array $useNamespaces = [];
private array $useFunctions = [];
private string $class = ''; private string $class = '';
private FunctionDef $functionDef; private FunctionDef $functionDef;
private array $globalVars = [ private array $globalVars = [
@ -130,6 +134,7 @@ class Translator extends \PhpAot\Core\Translator
]; ];
private array $localVars = []; private array $localVars = [];
private array $objectWrappers = []; private array $objectWrappers = [];
private bool $strictTypes = false;
const string PREFIX = 'php_'; const string PREFIX = 'php_';
private string $rootPath; private string $rootPath;
@ -141,10 +146,13 @@ class Translator extends \PhpAot\Core\Translator
private bool $inLoop = false; private bool $inLoop = false;
private bool $inSwitch = false; private bool $inSwitch = false;
private bool $stubFile = false; private bool $stubFile = false;
private Parser $parser;
public function __construct(string $rootPath) public function __construct(string $rootPath)
{ {
$this->rootPath = $rootPath; $this->rootPath = $rootPath;
$this->parser = (new ParserFactory())->createForNewestSupportedVersion();
// $this->prettyPrinter = new PrettyPrinter\Standard;
$this->setBuildDir($rootPath . '/build'); $this->setBuildDir($rootPath . '/build');
$climate = new CLImate; $climate = new CLImate;
$this->climate = $climate; $this->climate = $climate;
@ -278,27 +286,33 @@ class Translator extends \PhpAot\Core\Translator
private function doConvert(string $phpCode): string private function doConvert(string $phpCode): string
{ {
$this->climate->info('convert: ' . $this->file); $this->climate->info('convert: ' . $this->file);
$parser = (new ParserFactory())->createForNewestSupportedVersion();
$ast = $parser->parse($phpCode);
$ast = $this->parser->parse($phpCode);
$traverser = new NodeTraverser; $traverser = new NodeTraverser;
$prettyPrinter = new PrettyPrinter\Standard;
$traverser->addVisitor(new Visitor()); $traverser->addVisitor(new Visitor());
$stmts = $traverser->traverse($ast); $stmts = $traverser->traverse($ast);
$this->indentLevel = 0; $this->indentLevel = 0;
$this->strictTypes = false;
$this->resetNamespace();
$cppCode = ''; $cppCode = '';
foreach($stmts as $v) { foreach($stmts as $v) {
$type = $v->getType(); $type = $v->getType();
switch ($type) { switch ($type) {
case 'Stmt_Declare':
$this->parseDeclare($v);
break;
case 'Stmt_Namespace': case 'Stmt_Namespace':
$cppCode .= $this->parseNamespaceDef($v); $cppCode .= $this->parseNamespaceDef($v);
break; break;
case 'Stmt_Class': case 'Stmt_Class':
$cppCode .= $this->parseClassDef($v); $cppCode .= $this->parseClassDef($v);
break; break;
case 'Stmt_Use':
$cppCode .= $this->parseUse($v) . PHP_EOL;
break;
case 'Stmt_Function': case 'Stmt_Function':
$cppCode .= $this->parseFunctionDef($v) . PHP_EOL; $cppCode .= $this->parseFunctionDef($v) . PHP_EOL;
break; break;
@ -388,6 +402,13 @@ class Translator extends \PhpAot\Core\Translator
$this->tmpVarIndex = 0; $this->tmpVarIndex = 0;
} }
private function resetNamespace()
{
$this->useNamespaces = [];
$this->useFunctions = [];
$this->namespace = '';
}
private function getFunctionName(Node $v): string private function getFunctionName(Node $v): string
{ {
$names[] = $this->parseIdentifier($v->name); $names[] = $this->parseIdentifier($v->name);
@ -395,7 +416,7 @@ class Translator extends \PhpAot\Core\Translator
$names[] = strtolower($this->class); $names[] = strtolower($this->class);
} }
if ($this->namespace) { if ($this->namespace) {
$names[] = strtolower(str_replace('\\', '_', $this->namespace)); $names[] = $this->escapeNamespace($this->namespace);
} }
return implode('__', array_reverse($names)); return implode('__', array_reverse($names));
} }
@ -1161,7 +1182,7 @@ class Translator extends \PhpAot\Core\Translator
return $out; return $out;
} }
private function parseLibs() private function parseLibs(): string
{ {
$list = [ $list = [
'phpx', 'phpx',
@ -1411,21 +1432,45 @@ class Translator extends \PhpAot\Core\Translator
return $this->parseBinaryOp($expr->left, $expr->right, '%'); return $this->parseBinaryOp($expr->left, $expr->right, '%');
} }
private function parseFuncCall(mixed $expr): string /**
* 查找原生函数
* @param string $fname
* @return bool
*/
private function findNativeFunction(string $fname): string|false
{ {
if ($expr->name->getType() === self::EXPR_VARIABLE) { $possibleFunctionNames = [$fname,];
$fn = $this->parseIdentifier($expr->name); if ($this->namespace) {
$name = ''; $possibleFunctionNames[] = $this->namespace . '__' . $fname;
} elseif ($expr->name->getType() === 'Name') { }
$name = $this->parseIdentifier($expr->name); if (isset($this->useFunctions[$fname])) {
$possibleFunctionNames[] = $this->escapeNamespace($this->useFunctions[$fname]) . '__' . $fname;
}
foreach($possibleFunctionNames as $name) {
// 在预处理阶段检测到函数声明,但是未定义,说明在当前文件,但是顺序错误 // 在预处理阶段检测到函数声明,但是未定义,说明在当前文件,但是顺序错误
if (isset($this->functionDeclInFile[$name]) if (isset($this->functionDeclInFile[$name])
and $this->functionDeclInFile[$name] === $this->file and $this->functionDeclInFile[$name] === $this->file
and !$this->isNativeFunction($name)) { and !$this->isNativeFunction($name)) {
$this->redoAfterDeclare[$name] = true; $this->redoAfterDeclare[$name] = true;
return $name;
} }
if ($this->isNativeFunction($name)) { if ($this->isNativeFunction($name)) {
return self::PREFIX . $name . '(' . $this->parseCallArgs($expr->args, $name) . ')'; return $name;
}
}
return false;
}
private function parseFuncCall(mixed $expr): string
{
if ($expr->name->getType() === self::EXPR_VARIABLE) {
$fn = $this->parseIdentifier($expr->name);
$name = '';
} elseif ($expr->name->getType() === 'Name') {
$name = $this->parseIdentifier($expr->name);
$nativeFn = $this->findNativeFunction($name);
if ($nativeFn) {
return self::PREFIX . $nativeFn . '(' . $this->parseCallArgs($expr->args, $name) . ')';
} }
if ($this->isInternalFunction($name)) { if ($this->isInternalFunction($name)) {
$fn = 'php::' . $name; $fn = 'php::' . $name;
@ -1817,8 +1862,26 @@ class Translator extends \PhpAot\Core\Translator
private function parseNamespaceDef(Node $node): string private function parseNamespaceDef(Node $node): string
{ {
$this->namespace = $this->parseIdentifier($node->name); $ns = $this->parseIdentifier($node->name);
$code = ''; $code = '';
$this->resetNamespace();
if ($this->useCppNamespace) {
$ns = explode('\\', $ns);
$ns = array_filter($ns, function ($v) {
return $v !== '';
});
foreach ($ns as $name) {
$code .= 'namespace ' . $name . ' {' . PHP_EOL;
}
$ns_end = str_repeat('}', count($ns));
$this->namespace = implode('::', $ns);
} else {
$this->namespace = $this->escapeNamespace($node->name->toString());
$ns_end = '';
}
foreach($node->stmts as $v2) { foreach($node->stmts as $v2) {
$type2 = $v2->getType(); $type2 = $v2->getType();
switch ($type2) { switch ($type2) {
@ -1832,16 +1895,14 @@ class Translator extends \PhpAot\Core\Translator
$code .= $this->parseFunctionDef($v2) . PHP_EOL; $code .= $this->parseFunctionDef($v2) . PHP_EOL;
break; break;
case 'Stmt_Use': case 'Stmt_Use':
foreach ($v2->uses as $use) { $code .= $this->parseUse($v2) . PHP_EOL;
$this->uses[] = $use->name->toString();
}
break; break;
default: default:
abort($v2); abort($v2);
} }
} }
$this->namespace = ''; $code .= $ns_end;
$this->uses = []; $this->resetNamespace();
return $code; return $code;
} }
@ -1952,6 +2013,11 @@ class Translator extends \PhpAot\Core\Translator
} }
} }
private function escapeNamespace(string $ns): string
{
return str_replace('\\', '__', strtolower($ns));
}
private function unescapeVarName(string $name): string private function unescapeVarName(string $name): string
{ {
return str_replace('_php__var__', '', $name); return str_replace('_php__var__', '', $name);
@ -2666,16 +2732,14 @@ class Translator extends \PhpAot\Core\Translator
} }
} }
public function prepare(string $file) public function prepare(string $file): void
{ {
$phpCode = $this->loadFile($file); $phpCode = $this->loadFile($file);
$this->climate->info('prepare: ' . $this->file); $this->climate->info('prepare: ' . $this->file);
$parser = (new ParserFactory())->createForNewestSupportedVersion(); $ast = $this->parser->parse($phpCode);
$ast = $parser->parse($phpCode);
$traverser = new NodeTraverser; $traverser = new NodeTraverser;
$prettyPrinter = new PrettyPrinter\Standard;
$traverser->addVisitor(new Visitor()); $traverser->addVisitor(new Visitor());
$stmts = $traverser->traverse($ast); $stmts = $traverser->traverse($ast);
@ -2691,6 +2755,8 @@ class Translator extends \PhpAot\Core\Translator
case 'Stmt_Function': case 'Stmt_Function':
$this->prepareFunctionDef($v) . PHP_EOL; $this->prepareFunctionDef($v) . PHP_EOL;
break; break;
case 'Stmt_Declare':
case 'Stmt_Use':
case 'Stmt_Const': case 'Stmt_Const':
break; break;
default: default:
@ -2726,7 +2792,8 @@ class Translator extends \PhpAot\Core\Translator
private function prepareNamespaceDef(Node $node): void private function prepareNamespaceDef(Node $node): void
{ {
$this->namespace = $this->parseIdentifier($node->name); $this->resetNamespace();
$this->namespace = $this->escapeNamespace($this->parseIdentifier($node->name));
foreach ($node->stmts as $v2) { foreach ($node->stmts as $v2) {
$type2 = $v2->getType(); $type2 = $v2->getType();
switch ($type2) { switch ($type2) {
@ -2743,8 +2810,7 @@ class Translator extends \PhpAot\Core\Translator
abort($v2); abort($v2);
} }
} }
$this->namespace = ''; $this->resetNamespace();
$this->uses = [];
} }
private function prepareClassDef(Node $v): string private function prepareClassDef(Node $v): string
@ -2791,6 +2857,11 @@ class Translator extends \PhpAot\Core\Translator
return str_ends_with($file, '.stub.php'); return str_ends_with($file, '.stub.php');
} }
/**
* @param string $file
* @return string
* @throws \Exception
*/
private function loadFile(string $file): string private function loadFile(string $file): string
{ {
if (!file_exists($file)) { if (!file_exists($file)) {
@ -2826,4 +2897,45 @@ class Translator extends \PhpAot\Core\Translator
{ {
return $this->buildDir; return $this->buildDir;
} }
private function parseDeclare(mixed $v): void
{
$declares = $v->declares;
foreach ($declares as $declare) {
$key = $this->parseIdentifier($declare->key);
$value = $this->parseIdentifier($declare->value);
if ($key === 'ticks') {
$this->fatalError($v, 'declare(ticks=1) is not supported');
} elseif ($key === 'encoding') {
if (strtolower($value) !== 'utf-8') {
$this->fatalError($v, 'declare(encoding="' . $value . '") is not supported, only UTF-8 is supported');
}
}
$this->strictTypes = boolval(intval($value));
}
}
private function parseUse(mixed $v2): string
{
$code = '';
if ($this->useCppNamespace) {
foreach ($v2->uses as $use) {
$code .= 'using ' . str_replace('\\', '::', $use->name->toString()) . ';' . PHP_EOL;
}
} else {
foreach ($v2->uses as $use) {
$id = $this->parseIdentifier($use->name);
if ($use->type == Node\Stmt\Use_::TYPE_NORMAL) {
$this->useNamespaces[] = $id;
} else {
$rpos = strrpos($id, '\\');
$fn = substr($id, $rpos + 1);
$ns = substr($id, 0, $rpos);
// fn => namespace
$this->useFunctions[$fn] = $ns;
}
}
}
return $code;
}
} }

Loading…
Cancel
Save