链式赋值语法

pull/1/head
韩天峰 8 months ago
parent f2d6ee2b63
commit 023c017112
  1. 76
      examples/fannkuchredux.php
  2. 109
      src/Php/Translator.php

@ -0,0 +1,76 @@
<?php
/* The Computer Language Benchmarks Game
https://salsa.debian.org/benchmarksgame-team/benchmarksgame/
Naive transliteration from Rex Kerr's Scala program
contributed by Isaac Gouy
*/
function fannkuch($n)
{
$perm1 = [];
for ($i = 0; $i < $n; $i++) $perm1[$i] = $i;
$perm = [];
$count = [];
$f = $flips = $nperm = $checksum = $i = $k = $r = 0;
$r = $n;
while ($r > 0) {
$i = 0;
while ($r != 1) {
$count[$r - 1] = $r;
$r -= 1;
}
while ($i < $n) {
$perm[$i] = $perm1[$i];
$i += 1;
}
// Count flips and update max and checksum
$f = 0;
$k = $perm[0];
while ($k != 0) {
$i = 0;
while (2 * $i < $k) {
$t = $perm[$i];
$perm[$i] = $perm[$k - $i];
$perm[$k - $i] = $t;
$i += 1;
}
$k = $perm[0];
$f += 1;
}
if ($f > $flips) $flips = $f;
if (($nperm & 0x1) == 0) $checksum += $f; else $checksum -= $f;
// Use incremental change to generate another permutation
$more = true;
while ($more) {
if ($r == $n) {
echo $checksum, "\n";
return $flips;
}
$p0 = $perm1[0];
$i = 0;
while ($i < $r) {
$j = $i + 1;
$perm1[$i] = $perm1[$j];
$i = $j;
}
$perm1[$r] = $p0;
$count[$r] -= 1;
if ($count[$r] > 0) $more = false; else $r += 1;
}
$nperm += 1;
}
return $flips;
}
function main()
{
global $argc, $argv;
$n = $argc > 2 ? $argv[2] : 7;
printf("Pfannkuchen(%d) = %d\n", $n, fannkuch($n));
}

@ -14,7 +14,7 @@ use PhpParser\PrettyPrinter;
class Translator extends \PhpAot\Core\Translator
{
const TYPE_VAR = 'php::Variant';
const TYPE_BOOL = 'bool';
const TYPE_BOOL = 'php::Bool';
const TYPE_INT = 'php::Int';
const TYPE_FLOAT = 'php::Float';
const TYPE_OBJECT = 'php::Object';
@ -29,7 +29,7 @@ class Translator extends \PhpAot\Core\Translator
private array $zendTypeMap = [
'int' => self::TYPE_INT,
'float' => self::TYPE_FLOAT,
'bool' => 'bool',
'bool' => self::TYPE_BOOL,
];
private array $headers = [
@ -39,7 +39,7 @@ class Translator extends \PhpAot\Core\Translator
private array $internalFunctions = [];
private int $optimizeLevel = 5;
private bool $verbose = true;
private bool $verbose = false;
private string $file;
private string $dir;
private string $namespace = '';
@ -47,8 +47,9 @@ class Translator extends \PhpAot\Core\Translator
private string $class = '';
private FunctionDef $functionDef;
const PREFIX = 'php_';
const string PREFIX = 'php_';
private string $rootPath;
private int $debugLine = 0;
public function __construct(string $rootPath)
@ -309,6 +310,9 @@ class Translator extends \PhpAot\Core\Translator
{
$type = $expr->getType();
$this->writeLog('Line ' . $this->getLine($expr) . ': ' . $type);
if ($expr->getLine() === $this->debugLine) {
var_dump($expr);
}
switch ($type) {
case 'Expr_Assign':
return $this->parseAssign($expr);
@ -448,21 +452,42 @@ class Translator extends \PhpAot\Core\Translator
$array = $this->parseIdentifier($left->var);
$propName = $this->parseIdentifier($left->name);
return "$array.setProperty(\"$propName\", " . $this->parseExpr($right) . ")";
} elseif ($right->getType() === 'Expr_Assign') {
$chain[] = $left;
while ($right->getType() === 'Expr_Assign') {
$chain[] = $right->var;
$right = $right->expr;
}
// 翻转赋值链
$chain = array_reverse($chain);
// 取最后一个变量作为第一行的 left,右值为表达式
$left = array_shift($chain);
$list[] = $this->parseFinallyAssign($left, $right);
/**
* 构造赋值链
* a = b = c = d = (expr) -> d = (expr); c = d; b = c; a = b;
*/
$right = $left;
foreach ($chain as $left) {
$list[] = $this->getIndent() . $this->parseFinallyAssign($left, $right);
$right = $left;
}
return implode(";\n" . $this->getIndent(), $list);
}
return $this->parseFinallyAssign($left, $right);
}
private function parseFinallyAssign($left, $right): string
{
$var = $this->parseIdentifier($left);
$expr = $this->parseExpr($right);
if (!$this->hasVar($var)) {
if ($right->getType() === 'Expr_New') {
$type = self::TYPE_OBJECT;
} else {
$type = $this->detectType($right);
}
$type = $this->detectExprType($right);
$this->addVar($var, $type);
return $type . ' ' . $var . ' = ' . $expr;
} else {
return $var . ' = ' . $expr;
return $var . ' = ' . $this->convertExprType($expr, $this->detectExprType($left), $this->detectExprType($right));
}
}
@ -478,8 +503,8 @@ class Translator extends \PhpAot\Core\Translator
$leftExpr = $this->parseIdentifier($left);
$rightExpr = $this->parseIdentifier($right);
$leftType = $this->detectType($left);
$rightType = $this->detectType($right);
$leftType = $this->detectExprType($left);
$rightType = $this->detectExprType($right);
if ($leftType === self::TYPE_FLOAT) {
$rightExpr = $this->convertExprType($rightExpr, self::TYPE_FLOAT, $rightType);
@ -505,7 +530,7 @@ class Translator extends \PhpAot\Core\Translator
private function parseReturn(mixed $v): string
{
// 实际函数的返回值
$type = $this->detectType($v->expr);
$type = $this->detectExprType($v->expr);
$expr = $this->parseExpr($v->expr);
// 函数定义时没有声明返回值,但函数体中有返回值,修改为实际的返回值类型
if ($this->getReturnType() === 'void') {
@ -538,9 +563,21 @@ class Translator extends \PhpAot\Core\Translator
throw new ReturnTypeChanged;
}
private function detectType($expr): string
private function detectVarType($var): string
{
$name = $this->parseIdentifier($var);
if ($this->hasVar($name)) {
return $this->scope[$name];
}
return self::TYPE_VAR;
}
private function detectExprType($expr): string
{
$exprType = $expr->getType();
if ($this->debugLine === $expr->getLine()) {
var_dump($expr, $exprType);
}
switch ($exprType) {
case 'Expr_Cast_Int':
case 'Scalar_Int':
@ -554,29 +591,44 @@ class Translator extends \PhpAot\Core\Translator
return 'php::Array';
case 'Expr_BinaryOp_Plus':
case 'Expr_BinaryOp_Minus':
$leftType = $this->detectType($expr->left);
$rightType = $this->detectType($expr->right);
$leftType = $this->detectExprType($expr->left);
$rightType = $this->detectExprType($expr->right);
if ($leftType === self::TYPE_INT || $rightType === self::TYPE_INT) {
return self::TYPE_INT;
} else {
return self::TYPE_VAR;
}
break;
case 'Expr_FuncCall':
$name = $this->parseIdentifier($expr->name);
if ($this->isInternalFunction($name)) {
return $this->internalFunctions[$name]->returnType;
} else {
return self::TYPE_VAR;
}
break;
case 'Expr_New':
return self::TYPE_OBJECT;
case 'Expr_Assign':
$var = $this->parseIdentifier($expr->var);
if ($this->hasVar($var)) {
return $this->scope[$var];
}
break;
case 'Expr_Variable':
return $this->detectVarType($expr);
case 'Expr_ConstFetch':
return $this->detectConstType($expr);
case 'Scalar_String':
default:
return self::TYPE_VAR;
break;
}
return self::TYPE_VAR;
}
private function parseArray($node): string
{
$items = $node->items;
// 优化代码风格,空数组直接返回{},否则会产生一些空洞内容
if (count($items) === 0) {
return '{}';
}
$list = [];
$this->indentLevel++;
foreach ($items as $item) {
@ -614,7 +666,7 @@ class Translator extends \PhpAot\Core\Translator
}
}
private function parseIncludes()
private function parseIncludes(): string
{
$list = [
$this->phpxDir . '/include',
@ -626,7 +678,7 @@ class Translator extends \PhpAot\Core\Translator
return $out;
}
private function parseLdflags()
private function parseLdflags(): string
{
$list = [
'$(php-config --prefix)/lib',
@ -1212,7 +1264,7 @@ class Translator extends \PhpAot\Core\Translator
private function getTypeConvertedArg($arg, $argInfo): string
{
$expr = $this->parseArg($arg);
$type = $this->detectType($arg->value);
$type = $this->detectExprType($arg->value);
return $this->convertExprType($expr, $argInfo->type, $type);
}
@ -1318,4 +1370,13 @@ class Translator extends \PhpAot\Core\Translator
{
return 'tmp_var_' . $this->tmpVarIndex++;
}
private function detectConstType($expr): string
{
$name = $this->parseIdentifier($expr->name);
if ($name === 'true') {
return self::TYPE_BOOL;
}
return $name;
}
}

Loading…
Cancel
Save