pull/1/head
韩天峰 8 months ago
parent 1ab142406a
commit f2d6ee2b63
  1. 111
      .clang-format
  2. 5
      bin/bootstrap.php
  3. 7
      bin/compiler.php
  4. 6
      bin/linker.php
  5. 7
      examples/binary-tree.php
  6. 8
      src/Php/ReturnTypeChanged.php
  7. 239
      src/Php/Translator.php

@ -0,0 +1,111 @@
---
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: false
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 8
UseTab: Never

@ -0,0 +1,5 @@
<?php
define("ROOT_PATH", dirname(__DIR__));
define('DEBUG', true);
require ROOT_PATH . '/vendor/autoload.php';

@ -1,18 +1,15 @@
<?php
require dirname(__DIR__) . '/vendor/autoload.php';
require __DIR__ . '/bootstrap.php';
use PhpAot\Php\Translator;
define('DEBUG', true);
if (empty($argv[1])) {
die("php compiler.php [file]\n");
}
try {
$file = $argv[1];
$translator = new Translator();
$translator = new Translator(ROOT_PATH);
$translator->setIndent(' ');
$code = $translator->convert($file);
$info = pathinfo($file);

@ -1,7 +1,5 @@
<?php
require dirname(__DIR__) . '/vendor/autoload.php';
require __DIR__ . '/bootstrap.php';
use PhpAot\Php\Translator;
@ -11,7 +9,7 @@ if (empty($argv[1])) {
try {
$file = $argv[1];
$translator = new Translator();
$translator = new Translator(ROOT_PATH);
$info = pathinfo($file);
$objectFile = './tmp/' . $info['filename'] . '.cc.o';
$translator->compileBinary($info['filename'], $objectFile);

@ -1,5 +1,4 @@
<?php
/* The Computer Language Benchmarks Game
https://salsa.debian.org/benchmarksgame-team/benchmarksgame/
@ -36,6 +35,8 @@ function startWorker($depth, $iter, $n, $size, $shmId)
if ($pid) {
return $pid;
} else {
var_dump($iter, $n);
echo str_repeat('----', 16).PHP_EOL;
$check = 0;
for ($i = 1; $i <= $iter; ++$i) {
$t = createTree($depth);
@ -52,7 +53,7 @@ function main()
{
global $argc, $argv;
$minDepth = 4;
$n = ($argc == 2) ? $argv[1] : 1;
$n = ($argc == 3) ? $argv[2] : 1;
$maxDepth = max($minDepth + 2, $n);
$stretchDepth = $maxDepth + 1;
@ -89,5 +90,3 @@ function main()
echo "long lived tree of depth $maxDepth\t check: ", checkTree($longLivedTree), PHP_EOL;
}
main();

@ -0,0 +1,8 @@
<?php
namespace PhpAot\Php;
class ReturnTypeChanged extends \RuntimeException
{
}

@ -2,10 +2,10 @@
namespace PhpAot\Php;
use PhpAot\Php\Visitor;
use PhpParser\Node;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpAot\Php\Visitor;
use PhpParser\Error;
use PhpParser\NodeTraverser;
use PhpParser\ParserFactory;
@ -23,6 +23,9 @@ class Translator extends \PhpAot\Core\Translator
private string $phpxDir = '~/workspace/projects/phpx';
protected string $lang = 'PHP';
private array $scope = [];
private int $tmpVarIndex = 0;
// 需要插入到函数头部的变量定义代码
private array $definitions = [];
private array $zendTypeMap = [
'int' => self::TYPE_INT,
'float' => self::TYPE_FLOAT,
@ -36,19 +39,21 @@ class Translator extends \PhpAot\Core\Translator
private array $internalFunctions = [];
private int $optimizeLevel = 5;
private bool $verbose = false;
private bool $verbose = true;
private string $file;
private string $dir;
private string $namespace = '';
private array $uses = [];
private string $class = '';
private string $returnType = '';
private FunctionDef $functionDef;
const PREFIX = 'php_';
private string $rootPath;
public function __construct()
public function __construct(string $rootPath)
{
$this->rootPath = $rootPath;
}
public function parseHeaders(): string
@ -65,12 +70,9 @@ class Translator extends \PhpAot\Core\Translator
$this->phpxDir = $dir;
}
public function convert(string $file)
private function doConvert(string $phpCode)
{
$parser = (new ParserFactory())->createForNewestSupportedVersion();
$phpCode = file_get_contents($file);
$this->file = realpath($file);
$this->dir = dirname($this->file);
$ast = $parser->parse($phpCode);
$traverser = new NodeTraverser;
@ -79,6 +81,7 @@ class Translator extends \PhpAot\Core\Translator
$traverser->addVisitor(new Visitor());
$stmts = $traverser->traverse($ast);
$this->indentLevel = 0;
$cppCode = $this->parseHeaders();
foreach($stmts as $v) {
@ -100,9 +103,26 @@ class Translator extends \PhpAot\Core\Translator
return $cppCode;
}
public function save($code, $file)
public function convert(string $file): string
{
$phpCode = file_get_contents($file);
$this->file = realpath($file);
$this->dir = dirname($this->file);
while (true) {
try {
return $this->doConvert($phpCode);
} catch (ReturnTypeChanged $e) {
// 某些情况,例如返回值变更,需要重新解析
continue;
}
}
}
public function save(string $code, string $file): void
{
file_put_contents($file, $code);
$this->formatCppCode($file);
}
@ -131,10 +151,16 @@ class Translator extends \PhpAot\Core\Translator
return isset($this->internalFunctions[$name]);
}
private function parseFunctionDef($v): string
private function resetScope(): void
{
$this->scope = [];
$def = new FunctionDef();
$this->definitions = [];
$this->tmpVarIndex = 0;
}
private function parseFunctionDef($v): string
{
$this->resetScope();
$names[] = $this->parseIdentifier($v->name);
if ($this->class) {
$names[] = strtolower($this->class);
@ -144,22 +170,30 @@ class Translator extends \PhpAot\Core\Translator
}
$name = implode('__', array_reverse($names));
if (isset($this->internalFunctions[$name])) {
$this->functionDef = $this->internalFunctions[$name];
} else {
$this->functionDef = new FunctionDef();
$this->functionDef->name = $name;
if ($v->returnType) {
$this->returnType = $this->getZendType($this->parseIdentifier($v->returnType));
$this->functionDef->returnType = $this->getZendType($this->parseIdentifier($v->returnType));
} else {
$this->returnType = 'void';
$this->functionDef->returnType = 'void';
}
$this->internalFunctions[$name] = $this->functionDef;
}
$def->name = $name;
$def->returnType = $this->returnType;
$this->internalFunctions[$name] = $def;
$params = $this->parseParams($v->params, $def);
$code = $this->returnType . ' ' . self::PREFIX . $name . '(' . $params . ') {' . PHP_EOL;
$params = $this->parseParams($v->params);
$this->indentLevel++;
$stmts = $this->parseStmts($v->stmts);
$this->indentLevel--;
$code = $this->getReturnType() . ' ' . self::PREFIX . $name . '(' . $params . ') {' . PHP_EOL;
$this->indentLevel++;
foreach ($this->definitions as $definition) {
$code .= $this->getIndent() . $definition . PHP_EOL;
}
$this->indentLevel--;
$code .= $stmts;
$code .= "}\n";
@ -187,15 +221,17 @@ class Translator extends \PhpAot\Core\Translator
return $expr->getAttribute('rawValue');
case 'Scalar_String':
return '"' . $this->escapeString($expr->value) . '"';
case 'Expr_ConstFetch':
return $this->parseConstFetch($expr);
default:
return $this->parseExpr($expr);
}
}
private function parseParams($params, FunctionDef $def): string
private function parseParams($params): string
{
$list = [];
$def->argumentCountRequired = count($params);
$this->functionDef->argumentCountRequired = count($params);
foreach ($params as $param) {
$type = $this->parseType($param->type);
$name = $this->parseIdentifier($param->var);
@ -206,10 +242,10 @@ class Translator extends \PhpAot\Core\Translator
$argInfo->name = $name;
$argInfo->type = $type;
if (isset($param->default)) {
$def->argumentCountRequired = count($list) - 1;
$this->functionDef->argumentCountRequired = count($list) - 1;
$argInfo->default = $param->default->getAttribute('rawValue');
}
$def->arguments[] = $argInfo;
$this->functionDef->arguments[] = $argInfo;
}
return implode(', ', $list);
}
@ -233,6 +269,9 @@ class Translator extends \PhpAot\Core\Translator
case 'Stmt_For':
$lines[] = $this->parseFor($v);
break;
case 'Stmt_Foreach':
$lines[] = $this->parseForeach($v);
break;
case 'Stmt_While':
$lines[] = $this->parseWhile($v);
break;
@ -259,6 +298,7 @@ class Translator extends \PhpAot\Core\Translator
}
}
$code = '';
foreach ($lines as $line) {
$code .= $this->getIndent() . $line . PHP_EOL;
}
@ -398,8 +438,12 @@ class Translator extends \PhpAot\Core\Translator
$this->addVar($array, self::TYPE_VAR);
$code .= self::TYPE_ARRAY . " $array;\n" . $this->getIndent();
}
if ($left->dim === null) {
return $code . "$array.offsetSet(php::null, " . $this->parseExpr($right) . ")";
} else {
$dim = $this->parseIdentifier($left->dim);
return $code . "$array.offsetSet($dim, " . $this->parseExpr($right) . ")";
}
} elseif ($left->getType() === 'Expr_PropertyFetch') {
$array = $this->parseIdentifier($left->var);
$propName = $this->parseIdentifier($left->name);
@ -429,19 +473,45 @@ class Translator extends \PhpAot\Core\Translator
}
}
private function parseBinaryOpPlus(mixed $expr): string
private function parseBinaryOp($left, $right, $op): string
{
$left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right);
$leftExpr = $this->parseIdentifier($left);
$rightExpr = $this->parseIdentifier($right);
$leftType = $this->detectType($left);
$rightType = $this->detectType($right);
if ($leftType === self::TYPE_FLOAT) {
$rightExpr = $this->convertExprType($rightExpr, self::TYPE_FLOAT, $rightType);
} elseif ($rightType === self::TYPE_FLOAT) {
$leftExpr = $this->convertExprType($leftExpr, self::TYPE_FLOAT, $leftType);
} elseif ($leftType === self::TYPE_INT) {
$rightExpr = $this->convertExprType($rightExpr, self::TYPE_INT, $rightType);
} elseif ($rightType === self::TYPE_INT) {
$leftExpr = $this->convertExprType($leftExpr, self::TYPE_INT, $leftType);
} else {
$leftExpr = $this->convertExprType($leftExpr, self::TYPE_INT, $leftType);
$rightExpr = $this->convertExprType($rightExpr, self::TYPE_INT, $rightType);
}
return $left . ' + ' . $right;
return $leftExpr . ' ' .$op .' ' . $rightExpr;
}
private function parseBinaryOpPlus(mixed $expr): string
{
return $this->parseBinaryOp($expr->left, $expr->right, '+');
}
private function parseReturn(mixed $v): string
{
// 实际函数的返回值
$type = $this->detectType($v->expr);
$expr = $this->parseExpr($v->expr);
return 'return ' . $this->convertExprType($expr, $this->returnType, $type);
// 函数定义时没有声明返回值,但函数体中有返回值,修改为实际的返回值类型
if ($this->getReturnType() === 'void') {
$this->resetReturnType($type);
}
return 'return ' . $this->convertExprType($expr, $this->getReturnType(), $type);
}
private function parseBinaryOpMul(mixed $expr): string
@ -462,6 +532,12 @@ class Translator extends \PhpAot\Core\Translator
return isset($this->scope[$name]);
}
private function resetReturnType(string $type): void
{
$this->functionDef->returnType = $type;
throw new ReturnTypeChanged;
}
private function detectType($expr): string
{
$exprType = $expr->getType();
@ -507,7 +583,8 @@ class Translator extends \PhpAot\Core\Translator
if ($item->key) {
$list[] = $this->getIndent() . '{ ' . $this->parseIdentifier($item->key) . ', php::Variant(' . $this->parseIdentifier($item->value) . ') }';
} else {
$list[] = $this->getIndent() . 'php::Variant(' . $this->parseIdentifier($item->value) . ')';
$value = $this->parseIdentifier($item->value);
$list[] = $this->getIndent() . 'php::Variant(' . $value . ')';
}
}
$this->indentLevel--;
@ -708,16 +785,16 @@ class Translator extends \PhpAot\Core\Translator
{
$name = $this->parseIdentifier($expr->name);
if ($this->isInternalFunction($name)) {
return self::PREFIX . $name . '(' . $this->parseArgs($expr->args, $name) . ')';
return self::PREFIX . $name . '(' . $this->parseCallArgs($expr->args, $name) . ')';
}
if (empty($expr->args)) {
return 'php::call("' . $name . '")';
} else {
return 'php::call("' . $name . '", {' . $this->parseArgs($expr->args, $name) . '})';
return 'php::call("' . $name . '", {' . $this->parseCallArgs($expr->args, $name) . '})';
}
}
private function parseArgs($args, string $funcName): string
private function parseCallArgs($args, string $funcName): string
{
$internalFunction = $this->isInternalFunction($funcName);
$list_args = [];
@ -728,6 +805,14 @@ class Translator extends \PhpAot\Core\Translator
} else {
$list_args[] = $this->parseArg($arg);
}
// 调用了不存在的变量,可能是引用
if ($arg->value->getType() === 'Expr_Variable') {
$name = $this->parseIdentifier($arg->value);
if (!$this->hasVar($name)) {
$this->definitions[] = self::TYPE_VAR . ' ' . $name . ' = php::newReference();';
}
}
}
return implode(', ', $list_args);
}
@ -904,13 +989,28 @@ class Translator extends \PhpAot\Core\Translator
return $code;
}
private function convertIntExpr(string $expr): string
{
return 'php::to_int(' . $expr . ')';
}
private function convertFloatExpr(string $expr): string
{
return 'php::to_float(' . $expr . ')';
}
private function convertBoolExpr(string $expr): string
{
return 'php::to_bool(' . $expr . ')';
}
private function optimizeBinaryOpCompare(string $left, string $right, string $op): string
{
if ($this->getDetectedType($left) == self::TYPE_INT and $this->getDetectedType($right) == self::TYPE_VAR) {
return $left . ' ' . $op . ' ' . $right . '.toInt()';
return $left . ' ' . $op . ' ' . $this->convertIntExpr($right);
}
if ($this->getDetectedType($left) == self::TYPE_FLOAT and $this->getDetectedType($right) == self::TYPE_VAR) {
return $left . ' ' . $op . ' ' . $right . '.toFloat()';
return $left . ' ' . $op . ' ' . $this->convertFloatExpr($right);
}
return '';
}
@ -960,6 +1060,10 @@ class Translator extends \PhpAot\Core\Translator
$left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right);
if ($right === 'nullptr') {
return $left . '.isNull()';
}
return 'php::same(' . $left . ', ' . $right . ')';
}
@ -970,7 +1074,7 @@ class Translator extends \PhpAot\Core\Translator
if (empty($args)) {
return 'php::newObject("' . $className . '")';
} else {
return 'php::newObject("' . $className . '"(' . $this->parseArgs($args) . ')';
return 'php::newObject("' . $className . '"(' . $this->parseCallArgs($args) . ')';
}
}
@ -1036,7 +1140,11 @@ class Translator extends \PhpAot\Core\Translator
private function parseConstFetch(Node $expr)
{
return $this->parseIdentifier($expr->name);
$name = $this->parseIdentifier($expr->name);
if ($name === 'null') {
return 'nullptr';
}
return $name;
}
private function parseUnaryMinus(Node $expr): string
@ -1096,6 +1204,11 @@ class Translator extends \PhpAot\Core\Translator
return $funcDef->arguments[$index];
}
private function getReturnType(): string
{
return $this->functionDef->returnType;
}
private function getTypeConvertedArg($arg, $argInfo): string
{
$expr = $this->parseArg($arg);
@ -1106,13 +1219,13 @@ class Translator extends \PhpAot\Core\Translator
private function convertExprType(string $expr, $leftType, $rightType): string
{
if ($leftType === self::TYPE_INT && $rightType !== self::TYPE_INT) {
return '(' . $expr . ').toInt()';
return $this->convertIntExpr($expr);
}
if ($leftType === self::TYPE_FLOAT && $rightType !== self::TYPE_FLOAT) {
return '(' . $expr . ').toFloat()';
return $this->convertFloatExpr($expr);
}
if ($leftType === self::TYPE_BOOL && $rightType !== self::TYPE_BOOL) {
return '(' . $expr . ').toBool()';
return $this->convertBoolExpr($expr);
}
return $expr;
}
@ -1154,11 +1267,55 @@ class Translator extends \PhpAot\Core\Translator
private function parseAssignOpShiftRight(Node $node): string
{
$var = $this->parseIdentifier($node->var);
return $var . ' >> ' . $this->parseIdentifier($node->expr);
return $var . ' >>= ' . $this->parseIdentifier($node->expr);
}
private function parseMagicConstFile(mixed $expr): string
{
return '"' . $this->escapeString($this->file) . '"';
}
private function parseForeach(Node $node)
{
$expr = $this->parseIdentifier($node->expr);
if ($node->keyVar) {
$keyVar = $this->parseIdentifier($node->keyVar);
}
$valueVar = $this->parseIdentifier($node->valueVar);
$iteratorVar = $this->addTmpVar();
$stmts = $node->stmts;
$code = '';
$code .= self::TYPE_ARRAY . " $iteratorVar = " . $expr . ';' . PHP_EOL;
$code .= 'for (auto iter = ' . $iteratorVar . '.begin(); iter != ' . $iteratorVar . '.end(); ++iter) {' . PHP_EOL;
$this->indentLevel++;
if ($node->keyVar) {
$code .= $this->getIndent() . self::TYPE_VAR . ' ' . $keyVar . ' = iter.key();' . PHP_EOL;
$this->addVar($keyVar, self::TYPE_VAR);
}
$code .= $this->getIndent() . self::TYPE_VAR . ' ' . $valueVar . ' = iter.value();' . PHP_EOL;
$this->addVar($valueVar, self::TYPE_VAR);
$code .= $this->parseStmts($stmts);
$this->indentLevel--;
$code .= $this->getIndent() . '}';
return $code;
}
private function formatCppCode(string $file): void
{
$cmd = 'cd ' . $this->rootPath . ' && clang-format -i ' . $file;
$this->writeLog('formatting ' . $file . '...');
$this->writeLog($cmd);
shell_exec($cmd);
}
private function addTmpVar(): string
{
return 'tmp_var_' . $this->tmpVarIndex++;
}
}

Loading…
Cancel
Save