Regenerator progress

This commit is contained in:
Daniil Gentili 2020-08-24 12:25:35 +02:00
parent 49a12d79b3
commit 5228af041e
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
5 changed files with 123 additions and 54 deletions

View File

@ -0,0 +1,22 @@
<?php
namespace Phabel\Plugin;
use Phabel\Plugin;
use PhpParser\Builder\FunctionLike;
class ReGenerator extends Plugin
{
const SHOULD_ATTRIBUTE = 'shouldRegenerate';
public function enter(FunctionLike $function)
{
if (!$function->getAttribute(self::SHOULD_ATTRIBUTE, false)) {
return;
}
}
public function runAfter(): array
{
return [ArrowClosure::class];
}
}

View File

@ -1,6 +1,6 @@
<?php <?php
namespace Phabel\Target\Php70\ReGenerator; namespace Phabel\Plugin\ReGenerator;
/** /**
* Regenerator class. * Regenerator class.
@ -65,7 +65,7 @@ class ReGenerator implements \Iterator
/** /**
* Yielded from (re)generator. * Yielded from (re)generator.
* *
* @var \Generator|self * @var \Generator|self|null
*/ */
private $yieldedFrom; private $yieldedFrom;
@ -82,7 +82,7 @@ class ReGenerator implements \Iterator
/** /**
* Get return value. * Get return value.
* *
* @return void * @return mixed
*/ */
public function getReturn() public function getReturn()
{ {
@ -97,7 +97,7 @@ class ReGenerator implements \Iterator
private function start(): void private function start(): void
{ {
if (!$this->started) { if (!$this->started) {
($this->generator)($this->state, $this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned); ($this->generator)($this->state, $this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned, $this->yieldedFrom);
$this->started = true; $this->started = true;
} }
} }
@ -112,9 +112,9 @@ class ReGenerator implements \Iterator
$e = $exception; $e = $exception;
} }
if (!$this->yieldedFrom->valid()) { // Returned from yield from if (!$this->yieldedFrom->valid()) { // Returned from yield from
$returnValue = $this->yieldedFrom->getReturn(); $returnValue = \method_exists($this->yieldedFrom, 'getReturn') ? $this->yieldedFrom->getReturn() : null;
$this->yieldedFrom = null; $this->yieldedFrom = null;
if ($e) { if (isset($e)) {
return $this->throw($e); return $this->throw($e);
} }
return $this->send($returnValue); return $this->send($returnValue);
@ -125,7 +125,7 @@ class ReGenerator implements \Iterator
if (!$this->returned) { if (!$this->returned) {
$this->sentValue = $value; $this->sentValue = $value;
try { try {
($this->generator)($this->state, $this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned); ($this->generator)($this->state, $this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned, $this->yieldedFrom);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->returned = true; $this->returned = true;
throw $e; throw $e;
@ -145,9 +145,9 @@ class ReGenerator implements \Iterator
$e = $exception; $e = $exception;
} }
if (!$this->yieldedFrom->valid()) { // Returned from yield from if (!$this->yieldedFrom->valid()) { // Returned from yield from
$returnValue = $this->yieldedFrom->getReturn(); $returnValue = \method_exists($this->yieldedFrom, 'getReturn') ? $this->yieldedFrom->getReturn() : null;
$this->yieldedFrom = null; $this->yieldedFrom = null;
if ($e) { if (isset($e)) {
return $this->throw($e); return $this->throw($e);
} }
return $this->send($returnValue); return $this->send($returnValue);
@ -158,7 +158,7 @@ class ReGenerator implements \Iterator
if (!$this->returned) { if (!$this->returned) {
$this->sentException = $value; $this->sentException = $value;
try { try {
($this->generator)($this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned); ($this->generator)($this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned, $this->yieldedFrom);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->returned = true; $this->returned = true;
throw $e; throw $e;
@ -171,18 +171,18 @@ class ReGenerator implements \Iterator
public function current() public function current()
{ {
$this->start();
if ($this->yieldedFrom) { if ($this->yieldedFrom) {
return $this->yieldedFrom->current(); return $this->yieldedFrom->current();
} }
$this->start();
return $this->yieldValue; return $this->yieldValue;
} }
public function key() public function key()
{ {
$this->start();
if ($this->yieldedFrom) { if ($this->yieldedFrom) {
return $this->yieldedFrom->key(); return $this->yieldedFrom->key();
} }
$this->start();
return $this->yieldKey; return $this->yieldKey;
} }
public function next(): void public function next(): void

View File

@ -0,0 +1,30 @@
<?php
namespace Phabel\Target\Php55;
use Phabel\Context;
use Phabel\Plugin;
use Phabel\Plugin\ReGenerator;
use PhpParser\Node\Expr\Yield_;
use PhpParser\Node\FunctionLike;
class YieldDetector extends Plugin
{
public function enterYield(Yield_ $node, Context $ctx): void
{
foreach ($ctx->parents as $parent) {
if ($parent instanceof FunctionLike) {
$parent->setAttribute(ReGenerator::SHOULD_ATTRIBUTE, true);
return;
}
}
}
public function runBefore(): array
{
return [ReGenerator::class];
}
public function runAfter(): array
{
return [ArrowClosure::class];
}
}

View File

@ -1,42 +0,0 @@
<?php
namespace Phabel\Target\Php70;
use Phabel\Context;
use Phabel\Plugin;
use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Instanceof_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Expr\YieldFrom;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\While_;
class YieldFromReplacer extends Plugin
{
private string $phabelVar = '';
private int $phabelCount = 0;
public function shouldRunFile(string $file): bool
{
$this->phabelVar = 'phabelGeneratorYieldFrom'.\hash('sha256', $file);
return parent::shouldRunFile($file);
}
public function enterNode(YieldFrom $node, Context $ctx)
{
$var = new Variable($this->phabelVar.($this->phabelCount++));
$assign = new Assign($var, $node->expr);
$ifInstanceof = new If_(new Instanceof_(Plugin::callMethod($var, 'valid'), new FullyQualified(\YieldReturnValue::class)), ['stmts' => [new Assign()]]);
$while = new While_(new MethodCall($var, new Identifier('valid')));
foreach ($ctx->parents as $node) {
if ($node->hasAttribute('currentNodeIndex')) {
$ctx->insertBefore($node, $node->expr);
}
}
$generator = $node->expr;
return new Node\Expr\Yield_($generator);
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace Phabel\Target\Php70;
use Phabel\Context;
use Phabel\Plugin;
use Phabel\Plugin\ReGenerator;
use PhpParser\Node\Expr\Yield_;
use PhpParser\Node\Expr\YieldFrom;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\Return_;
class YieldFromReturnDetector extends Plugin
{
public function enterYieldFrom(YieldFrom $node, Context $ctx): void
{
foreach ($ctx->parents as $parent) {
if ($parent instanceof FunctionLike) {
$parent->setAttribute(ReGenerator::SHOULD_ATTRIBUTE, true);
return;
}
}
}
public function enterYield(Yield_ $node, Context $ctx): void
{
foreach ($ctx->parents as $parent) {
if ($parent instanceof FunctionLike) {
$parent->setAttribute('hasYield', true);
if ($parent->getAttribute('hasReturn')) {
$parent->setAttribute(ReGenerator::SHOULD_ATTRIBUTE, true);
}
return;
}
}
}
public function enterReturn(Return_ $node, Context $ctx): void
{
if (!$node->expr) {
return;
}
foreach ($ctx->parents as $parent) {
if ($parent instanceof FunctionLike) {
$parent->setAttribute('hasReturn', true);
if ($parent->getAttribute('hasYield')) {
$parent->setAttribute(ReGenerator::SHOULD_ATTRIBUTE, true);
}
return;
}
}
}
public function runBefore(): array
{
return [ReGenerator::class];
}
public function runAfter(): array
{
return [ArrowClosure::class];
}
}