diff --git a/bin/compiler.php b/bin/compiler.php index 2d829cd6..45ec141d 100755 --- a/bin/compiler.php +++ b/bin/compiler.php @@ -3,9 +3,6 @@ require __DIR__ . '/bootstrap.php'; require __DIR__ . '/../src/gen_stub.php'; -use PhpAot\Php\Exception\SyntaxError; -use PhpAot\Php\Exception\Unsupported; -use PhpAot\Php\FileScanner; use PhpAot\Php\Translator; if (empty($argv[1])) { @@ -15,52 +12,11 @@ if (empty($argv[1])) { $path = $argv[1]; $translator = new Translator(ROOT_PATH); $translator->setIndent(' '); -$list = $translator->getFiles($path); - -$sourceFiles = []; -$objectFiles = []; - -// 分析 PHP 文件,预处理 -foreach ($list as $k => $file) { - if (FileScanner::isPhpFile($file)) { - try { - $translator->prepare($file); - } catch (Unsupported $e) { - $translator->output(" unsupported syntax: " . $e->getMessage() . "\n" . " skip: " . $file . "\n", 'error'); - unset($list[$k]); - } catch (SyntaxError $e) { - $translator->output(" syntax error: " . $e->getMessage() . "\n" . " skip: " . $file . "\n", 'error'); - unset($list[$k]); - } - } -} - -$translator->sortFiles($list); - +// 扫描所有 PHP 文件,预处理 +$files = $translator->prepare($path); // 生成 C++ 文件 -foreach ($list as $k => $file) { - try { - if (FileScanner::isPhpFile($file)) { - $cppFile = $translator->convert($file); - } elseif (FileScanner::isCppFile($file)) { - $cppFile = $file; - } else { - continue; - } - $sourceFiles[] = $cppFile; - } catch (Unsupported $e) { - echo " unsupported syntax: " . $e->getMessage() . "\n"; - echo " skip: " . $file . "\n"; - unset($list[$k]); - } -} - -if (empty($sourceFiles)) { - $translator->stop("No valid source file found"); -} - +$sourceFiles = $translator->convert($files); // 编译所有 C++ 文件 $objectFiles = $translator->compile($sourceFiles); - // 连接所有目标文件,生成可执行文件 $translator->build($objectFiles); diff --git a/phpunit/bootstrap.php b/phpunit/bootstrap.php index f56426c8..7006852b 100644 --- a/phpunit/bootstrap.php +++ b/phpunit/bootstrap.php @@ -14,8 +14,8 @@ class BaseTest extends TestCase $compiler = CompilerTest::create(ROOT_PATH); $testFile = __DIR__ . '/code/' . $file; $compiler->addFiles([$testFile]); - $compiler->prepare($testFile); - $compiler->convert($testFile); + $compiler->prepareFile($testFile); + $compiler->convertFile($testFile); } catch (TestError $exception) { $this->assertStringContainsString($expected, $exception->getMessage()); return; diff --git a/phpunit/src/DuplicateTest.php b/phpunit/src/DuplicateTest.php index 215e1e97..787bb38d 100644 --- a/phpunit/src/DuplicateTest.php +++ b/phpunit/src/DuplicateTest.php @@ -13,8 +13,8 @@ class DuplicateTest extends TestCase $compiler = CompilerTest::create(ROOT_PATH); $testFile = __DIR__ . '/../code/' . $file; $compiler->addFiles([$testFile]); - $compiler->prepare($testFile); - $compiler->convert($testFile); + $compiler->prepareFile($testFile); + $compiler->convertFile($testFile); } catch (TestError $exception) { $this->assertStringContainsString($expected, $exception->getMessage()); return; diff --git a/src/Php/Preprocessor.php b/src/Php/Preprocessor.php index 2f189b03..921c0642 100644 --- a/src/Php/Preprocessor.php +++ b/src/Php/Preprocessor.php @@ -90,7 +90,7 @@ class Preprocessor extends CompilerBase return false; } - public function prepare(string $file): void + public function prepareFile(string $file): void { if ($this->hasCppFileCache($file)) { $this->climate->darkGray('skip: ' . $file . ', cache exists'); @@ -210,11 +210,9 @@ class Preprocessor extends CompilerBase case 'Stmt_Const': case 'Stmt_Interface': break; - case 'Stmt_Expression': - $this->foundStrayCode($v2); - // no break default: - throw new Unsupported('Unsupported statement: ' . $type2); + $this->foundStrayCode($v2); + break; } } } diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 794ad162..f91099ea 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -17,6 +17,7 @@ use PhpAot\Php\Entity\InterfaceDef; use PhpAot\Php\Entity\MethodDef; use PhpAot\Php\Entity\PropertyDef; use PhpAot\Php\Exception\Redo; +use PhpAot\Php\Exception\SyntaxError; use PhpAot\Php\Exception\Unsupported; use PhpParser\Modifiers; use PhpParser\Node; @@ -31,6 +32,7 @@ class Translator extends Preprocessor protected array $sourceDirs = []; protected bool $verbose = false; protected array $phpSrcFiles = []; + protected array $ignorePaths = []; protected array $argInfoHeaderFiles = []; protected array $registerSymbols = []; @@ -98,7 +100,7 @@ class Translator extends Preprocessor $climate->br(); } - public function convert(string $file): string + public function convertFile(string $file): string { $file = realpath($file); if ($this->hasCppFileCache($file)) { @@ -190,6 +192,68 @@ class Translator extends Preprocessor return $list; } + public function prepare(string $path): array + { + $files = $this->getFiles($path); + // 应用 ignorePaths 过滤 + if (!empty($this->ignorePaths)) { + $files = array_filter($files, function ($file) { + foreach ($this->ignorePaths as $ignorePath) { + if ($file === $ignorePath) { + return false; + } + if (is_dir($ignorePath) && str_starts_with($file, $ignorePath . DIRECTORY_SEPARATOR)) { + return false; + } + } + return true; + }); + } + // 分析 PHP 文件,预处理 + foreach ($files as $k => $file) { + if (FileScanner::isPhpFile($file)) { + try { + $this->prepareFile($file); + } catch (Unsupported $e) { + $this->output(" unsupported syntax: " . $e->getMessage() . "\n" . " skip: " . $file . "\n", 'error'); + unset($files[$k]); + } catch (SyntaxError $e) { + $this->output(" syntax error: " . $e->getMessage() . "\n" . " skip: " . $file . "\n", 'error'); + unset($files[$k]); + } + } + } + $this->sortFiles($files); + return $files; + } + + public function convert(array $files): array + { + $sourceFiles = []; + // 生成 C++ 文件 + foreach ($files as $k => $file) { + try { + if (FileScanner::isPhpFile($file)) { + $cppFile = $this->convertFile($file); + } elseif (FileScanner::isCppFile($file)) { + $cppFile = $file; + } else { + continue; + } + $sourceFiles[] = $cppFile; + } catch (Unsupported $e) { + echo " unsupported syntax: " . $e->getMessage() . "\n"; + echo " skip: " . $file . "\n"; + unset($files[$k]); + } + } + + if (empty($sourceFiles)) { + $this->stop("No valid source file found"); + } + return $sourceFiles; + } + public function preprocessArgvAdvanced(): void { global $argv; @@ -653,6 +717,17 @@ class Translator extends Preprocessor return $code; } + protected function getAbsolutePath(string $path, string $projectDir): string + { + $path = trim($path); + if ($path[0] != '/') { + $absPath = $projectDir . '/' . $path; + } else { + $absPath = $path; + } + return realpath($absPath); + } + protected function parseProjectYaml(string $path): array { $cfg = Yaml::parseFile($path); @@ -660,15 +735,12 @@ class Translator extends Preprocessor if (!empty($cfg['sources'])) { $sources = $cfg['sources']; + if (!is_array($sources)) { + $this->error('`sources` must be array'); + } $list = []; foreach ($sources as $src) { - $src = trim($src); - if ($src[0] != '/') { - $absPath = $projectDir . '/' . $src; - } else { - $absPath = $src; - } - $realPath = realpath($absPath); + $realPath = $this->getAbsolutePath($src, $projectDir); if (!$realPath) { $this->error('Source file not exists: `' . $src . '`'); } @@ -705,6 +777,19 @@ class Translator extends Preprocessor $this->setBuildMode($cfg['type']); } + if (!empty($cfg['ignore'])) { + if (!is_array($cfg['ignore'])) { + $this->error('`ignore` must be array'); + } + foreach ($cfg['ignore'] as $src) { + $realPath = $this->getAbsolutePath($src, $projectDir); + if (!$realPath) { + $this->error('Source file not exists: `' . $src . '`'); + } + $this->ignorePaths[] = $realPath; + } + } + return $list; }