Implement expression extraction
This commit is contained in:
parent
9d6c66361e
commit
8dda97c514
172
src/Context.php
172
src/Context.php
|
@ -2,22 +2,32 @@
|
||||||
|
|
||||||
namespace Phabel;
|
namespace Phabel;
|
||||||
|
|
||||||
|
use PhpParser\BuilderHelpers;
|
||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||||
use PhpParser\Node\Expr\ArrowFunction;
|
use PhpParser\Node\Expr\ArrowFunction;
|
||||||
use PhpParser\Node\Expr\Assign;
|
use PhpParser\Node\Expr\Assign;
|
||||||
use PhpParser\Node\Expr\AssignOp;
|
use PhpParser\Node\Expr\AssignOp;
|
||||||
use PhpParser\Node\Expr\AssignRef;
|
use PhpParser\Node\Expr\AssignRef;
|
||||||
|
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
|
||||||
|
use PhpParser\Node\Expr\BinaryOp\BooleanOr;
|
||||||
|
use PhpParser\Node\Expr\BinaryOp\Coalesce;
|
||||||
|
use PhpParser\Node\Expr\BooleanNot;
|
||||||
|
use PhpParser\Node\Expr\Cast\Bool_;
|
||||||
use PhpParser\Node\Expr\Closure;
|
use PhpParser\Node\Expr\Closure;
|
||||||
use PhpParser\Node\Expr\FuncCall;
|
use PhpParser\Node\Expr\FuncCall;
|
||||||
use PhpParser\Node\Expr\MethodCall;
|
use PhpParser\Node\Expr\MethodCall;
|
||||||
use PhpParser\Node\Expr\StaticCall;
|
use PhpParser\Node\Expr\StaticCall;
|
||||||
|
use PhpParser\Node\Expr\Ternary;
|
||||||
use PhpParser\Node\Expr\Variable;
|
use PhpParser\Node\Expr\Variable;
|
||||||
use PhpParser\Node\FunctionLike;
|
use PhpParser\Node\FunctionLike;
|
||||||
use PhpParser\Node\Param;
|
use PhpParser\Node\Param;
|
||||||
|
use PhpParser\Node\Stmt\If_;
|
||||||
use SplStack;
|
use SplStack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* AST Context
|
||||||
|
*
|
||||||
* @author Daniil Gentili <daniil@daniil.it>
|
* @author Daniil Gentili <daniil@daniil.it>
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
@ -117,10 +127,27 @@ class Context
|
||||||
return new Variable($this->parents->top()->getVar());
|
return new Variable($this->parents->top()->getVar());
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Insert nodes before node.
|
* Get child currently being iterated on.
|
||||||
*
|
*
|
||||||
* @param Node $node
|
* @param Node $node
|
||||||
* @param Node ...$nodes
|
* @return Node
|
||||||
|
*/
|
||||||
|
public static function getCurrentChild(Node $node): Node
|
||||||
|
{
|
||||||
|
if (!$subNode = $node->getAttribute('currentNode')) {
|
||||||
|
throw new \RuntimeException('Node is not a part of the current AST stack!');
|
||||||
|
}
|
||||||
|
$child = $node->{$subNode};
|
||||||
|
if ($index = $node->getAttribute('currentNodeIndex')) {
|
||||||
|
return $child[$index];
|
||||||
|
}
|
||||||
|
return $child;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Insert nodes before node.
|
||||||
|
*
|
||||||
|
* @param Node $node Node before which to insert nodes
|
||||||
|
* @param Node ...$nodes Nodes to insert
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function insertBefore(Node $node, Node ...$nodes): void
|
public function insertBefore(Node $node, Node ...$nodes): void
|
||||||
|
@ -128,25 +155,142 @@ class Context
|
||||||
if (empty($nodes)) {
|
if (empty($nodes)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$subNode = $node->getAttribute('currentNode');
|
$found = false;
|
||||||
$subNodeIndex = $node->getAttribute('currentNodeIndex');
|
foreach ($this->parents as $parent) {
|
||||||
\array_splice($node->{$subNode}, $subNodeIndex, 0, $nodes);
|
if ($this->getCurrentChild($parent) === $node) {
|
||||||
$skips = $node->getAttribute('skipNodes', []);
|
$found = true;
|
||||||
$skips []= $subNodeIndex+\count($nodes);
|
break;
|
||||||
$node->setAttribute('skipNodes', $skips);
|
}
|
||||||
$node->setAttribute('currentNodeIndex', $subNodeIndex - 1);
|
}
|
||||||
|
if (!$found) {
|
||||||
|
throw new \RuntimeException('Node is not a part of the current AST stack!');
|
||||||
|
}
|
||||||
|
$this->insertBeforeParent($parent, $nodes);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Insert nodes before node.
|
||||||
|
*
|
||||||
|
* @param Node $node Node before which to insert nodes
|
||||||
|
* @param Node[] $nodes Nodes to insert
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function insertBeforeParent(Node $parent, array $nodes): void
|
||||||
|
{
|
||||||
|
$subNode = $parent->getAttribute('currentNode');
|
||||||
|
if ($subNode === 'stmts') {
|
||||||
|
$subNodeIndex = $parent->getAttribute('currentNodeIndex');
|
||||||
|
\array_splice($parent->{$subNode}, $subNodeIndex, 0, $nodes);
|
||||||
|
$skips = $parent->getAttribute('skipNodes', []);
|
||||||
|
$skips []= $subNodeIndex+\count($nodes);
|
||||||
|
$parent->setAttribute('skipNodes', $skips);
|
||||||
|
$parent->setAttribute('currentNodeIndex', $subNodeIndex - 1);
|
||||||
|
} else { // Cannot insert, is not in a statement
|
||||||
|
$curNode = &$parent->{$subNode};
|
||||||
|
if ($curNode instanceof BooleanOr && $subNode === 'right') {
|
||||||
|
$result = $this->getVariable();
|
||||||
|
$nodes = new If_(
|
||||||
|
$curNode->left,
|
||||||
|
[
|
||||||
|
'stmts' => [
|
||||||
|
new Assign($result, BuilderHelpers::normalizeValue(true))
|
||||||
|
],
|
||||||
|
'else' => [
|
||||||
|
...$nodes,
|
||||||
|
new Assign($result, new Bool_($curNode->right))
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$curNode = $result;
|
||||||
|
} elseif ($curNode instanceof BooleanAnd && $subNode === 'right') {
|
||||||
|
$result = $this->getVariable();
|
||||||
|
$nodes = new If_(
|
||||||
|
$curNode->left,
|
||||||
|
[
|
||||||
|
'stmts' => [
|
||||||
|
...$nodes,
|
||||||
|
new Assign($result, new Bool_($curNode->right))
|
||||||
|
],
|
||||||
|
'else' => [
|
||||||
|
new Assign($result, BuilderHelpers::normalizeValue(false))
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$curNode = $result;
|
||||||
|
} elseif ($curNode instanceof Ternary && $subNode !== 'cond') {
|
||||||
|
$result = $this->getVariable();
|
||||||
|
if (!$curNode->if) { // ?:
|
||||||
|
$nodes = new If_(
|
||||||
|
new BooleanNot(
|
||||||
|
new Assign($result, $curNode->cond)
|
||||||
|
),
|
||||||
|
[
|
||||||
|
'stmts' => [
|
||||||
|
...$nodes,
|
||||||
|
new Assign($result, $curNode->else)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$nodes = new If_(
|
||||||
|
$curNode->cond,
|
||||||
|
[
|
||||||
|
'stmts' => [
|
||||||
|
...$subNode === 'if' ? $nodes : [],
|
||||||
|
new Assign($result, $curNode->if)
|
||||||
|
],
|
||||||
|
'else' => [
|
||||||
|
...$subNode === 'else' ? $nodes : [],
|
||||||
|
new Assign($result, $curNode->else)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$curNode = $result;
|
||||||
|
} elseif ($subNode instanceof Coalesce && $subNode === 'right') {
|
||||||
|
$result = $this->getVariable();
|
||||||
|
$nodes = new If_(
|
||||||
|
Plugin::call(
|
||||||
|
'is_null',
|
||||||
|
new Assign($result, $curNode->left)
|
||||||
|
),
|
||||||
|
[
|
||||||
|
'stmts' => [
|
||||||
|
...$nodes,
|
||||||
|
new Assign($result, $curNode->right)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$curNode = $result;
|
||||||
|
} else if ($subNode instanceof FuncCall) {
|
||||||
|
|
||||||
|
}
|
||||||
|
$this->insertBefore($parent, $nodes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Insert nodes after node.
|
* Insert nodes after node.
|
||||||
*
|
*
|
||||||
* @param Node $node
|
* @param Node $node Node ater which to insert nodes
|
||||||
* @param Node ...$nodes
|
* @param Node ...$nodes Nodes to insert
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function insertAfter(Node $node, Node ...$nodes): void
|
public function insertAfter(Node $node, Node ...$nodes): void
|
||||||
{
|
{
|
||||||
$subNode = $node->getAttribute('currentNode');
|
if (empty($nodes)) {
|
||||||
$subNodeIndex = $node->getAttribute('currentNodeIndex');
|
return;
|
||||||
\array_splice($node->{$subNode}, $subNodeIndex+1, 0, $nodes);
|
}
|
||||||
|
$found = false;
|
||||||
|
foreach ($this->parents as $parent) {
|
||||||
|
if ($this->getCurrentChild($parent) === $node) {
|
||||||
|
$found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$found) {
|
||||||
|
throw new \RuntimeException('Node is not a part of the current AST stack!');
|
||||||
|
}
|
||||||
|
$subNode = $parent->getAttribute('currentNode');
|
||||||
|
$subNodeIndex = $parent->getAttribute('currentNodeIndex');
|
||||||
|
\array_splice($parent->{$subNode}, $subNodeIndex+1, 0, $nodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ use PhpParser\ParserFactory;
|
||||||
use ReflectionClass;
|
use ReflectionClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Plugin
|
||||||
|
*
|
||||||
* @author Daniil Gentili <daniil@daniil.it>
|
* @author Daniil Gentili <daniil@daniil.it>
|
||||||
*/
|
*/
|
||||||
abstract class Plugin implements PluginInterface
|
abstract class Plugin implements PluginInterface
|
||||||
|
|
|
@ -264,7 +264,7 @@ class TypeHintStripper extends Plugin
|
||||||
|
|
||||||
$return->expr = $var;
|
$return->expr = $var;
|
||||||
|
|
||||||
$ctx->insertBefore($ctx->parents->top(), $assign, $if);
|
$ctx->insertBefore($return, $assign, $if);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Get trace string for errors.
|
* Get trace string for errors.
|
||||||
|
|
|
@ -5,6 +5,8 @@ namespace Phabel;
|
||||||
use Phabel\PluginGraph\PackageContext;
|
use Phabel\PluginGraph\PackageContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Plugin interface
|
||||||
|
*
|
||||||
* @author Daniil Gentili <daniil@daniil.it>
|
* @author Daniil Gentili <daniil@daniil.it>
|
||||||
*/
|
*/
|
||||||
interface PluginInterface
|
interface PluginInterface
|
||||||
|
|
|
@ -7,6 +7,8 @@ use PhpParser\NodeAbstract;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Root node.
|
* Root node.
|
||||||
|
*
|
||||||
|
* @author Daniil Gentili <email@email.com>
|
||||||
*/
|
*/
|
||||||
class RootNode extends NodeAbstract
|
class RootNode extends NodeAbstract
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,6 +7,7 @@ use Phabel\Plugin;
|
||||||
use Phabel\RootNode;
|
use Phabel\RootNode;
|
||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
use PhpParser\Node\Expr\New_;
|
use PhpParser\Node\Expr\New_;
|
||||||
|
use PhpParser\Node\Identifier;
|
||||||
use PhpParser\Node\Stmt\Namespace_;
|
use PhpParser\Node\Stmt\Namespace_;
|
||||||
|
|
||||||
class AnonymousClassReplacer extends Plugin
|
class AnonymousClassReplacer extends Plugin
|
||||||
|
@ -43,14 +44,13 @@ class AnonymousClassReplacer extends Plugin
|
||||||
if (!$classNode instanceof Node\Stmt\Class_) {
|
if (!$classNode instanceof Node\Stmt\Class_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
$classNode->name = new Identifier('PhabelAnonymousClass'.$this->fileName.($this->count++));
|
||||||
|
|
||||||
$classNode->name = 'PhabelAnonymousClass'.$this->fileName.($this->count++);
|
$node->class = new Node\Name($classNode->name->name);
|
||||||
|
|
||||||
$node->class = new Node\Name($classNode->name);
|
|
||||||
|
|
||||||
foreach ($ctx->parents as $node) {
|
foreach ($ctx->parents as $node) {
|
||||||
if ($node instanceof Namespace_ || $node instanceof RootNode) {
|
if ($node instanceof Namespace_ || $node instanceof RootNode) {
|
||||||
$ctx->insertAfter($node, $classNode);
|
$ctx->insertBefore($ctx->getCurrentChild($node), $classNode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ use PhpParser\ParserFactory;
|
||||||
use SplQueue;
|
use SplQueue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* AST traverser
|
||||||
|
*
|
||||||
* @author Daniil Gentili <daniil@daniil.it>
|
* @author Daniil Gentili <daniil@daniil.it>
|
||||||
*/
|
*/
|
||||||
class Traverser
|
class Traverser
|
||||||
|
|
|
@ -4,6 +4,8 @@ namespace Phabel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represent variables currently in scope.
|
* Represent variables currently in scope.
|
||||||
|
*
|
||||||
|
* @author Daniil Gentili <daniil@daniil.it>
|
||||||
*/
|
*/
|
||||||
class VariableContext
|
class VariableContext
|
||||||
{
|
{
|
||||||
|
@ -13,10 +15,6 @@ class VariableContext
|
||||||
* @var array<string, true>
|
* @var array<string, true>
|
||||||
*/
|
*/
|
||||||
private array $variables;
|
private array $variables;
|
||||||
/**
|
|
||||||
* Custom variable counter.
|
|
||||||
*/
|
|
||||||
private int $counter = 0;
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
|
@ -77,8 +75,7 @@ class VariableContext
|
||||||
public function getVar(): string
|
public function getVar(): string
|
||||||
{
|
{
|
||||||
do {
|
do {
|
||||||
$var = 'phabel'.$this->counter;
|
$var = 'phabel'.bin2hex(random_bytes(8));
|
||||||
$this->counter++;
|
|
||||||
} while (isset($this->variables[$var]));
|
} while (isset($this->variables[$var]));
|
||||||
$this->variables[$var] = true;
|
$this->variables[$var] = true;
|
||||||
return $var;
|
return $var;
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Dump AST of file.
|
||||||
|
*
|
||||||
|
* @author Daniil Gentili <daniil@daniil.it>
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
use PhpParser\ParserFactory;
|
use PhpParser\ParserFactory;
|
||||||
use PhpParser\PrettyPrinter\Standard;
|
use PhpParser\PrettyPrinter\Standard;
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
// This file generates an array containing all possible expression nodes, generated using default parameters.
|
* This file generates an array containing all possible expression nodes, generated using default parameters.
|
||||||
// Then, for each expression that accepts another expression as subnode, it tries to use all the expressions generated in the previous step,
|
* Then, for each expression that accepts another expression as subnode, it tries to use all the expressions generated in the previous step,
|
||||||
// for each subnode, to test compatibility with various versions of the PHP lexer.
|
* for each subnode, to test compatibility with various versions of the PHP lexer.
|
||||||
|
*
|
||||||
|
* @author Daniil Gentili <daniil@daniil.it>
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
use HaydenPierce\ClassFinder\ClassFinder;
|
use HaydenPierce\ClassFinder\ClassFinder;
|
||||||
use Phabel\Plugin\IssetExpressionFixer;
|
use Phabel\Plugin\IssetExpressionFixer;
|
||||||
|
|
Loading…
Reference in New Issue