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
namespace Phabel\Target\Php70\ReGenerator;
namespace Phabel\Plugin\ReGenerator;
/**
* Regenerator class.
@ -65,7 +65,7 @@ class ReGenerator implements \Iterator
/**
* Yielded from (re)generator.
*
* @var \Generator|self
* @var \Generator|self|null
*/
private $yieldedFrom;
@ -82,7 +82,7 @@ class ReGenerator implements \Iterator
/**
* Get return value.
*
* @return void
* @return mixed
*/
public function getReturn()
{
@ -97,7 +97,7 @@ class ReGenerator implements \Iterator
private function start(): void
{
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;
}
}
@ -112,9 +112,9 @@ class ReGenerator implements \Iterator
$e = $exception;
}
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;
if ($e) {
if (isset($e)) {
return $this->throw($e);
}
return $this->send($returnValue);
@ -125,7 +125,7 @@ class ReGenerator implements \Iterator
if (!$this->returned) {
$this->sentValue = $value;
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) {
$this->returned = true;
throw $e;
@ -145,9 +145,9 @@ class ReGenerator implements \Iterator
$e = $exception;
}
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;
if ($e) {
if (isset($e)) {
return $this->throw($e);
}
return $this->send($returnValue);
@ -158,7 +158,7 @@ class ReGenerator implements \Iterator
if (!$this->returned) {
$this->sentException = $value;
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) {
$this->returned = true;
throw $e;
@ -171,18 +171,18 @@ class ReGenerator implements \Iterator
public function current()
{
$this->start();
if ($this->yieldedFrom) {
return $this->yieldedFrom->current();
}
$this->start();
return $this->yieldValue;
}
public function key()
{
$this->start();
if ($this->yieldedFrom) {
return $this->yieldedFrom->key();
}
$this->start();
return $this->yieldKey;
}
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];
}
}