Skip to content

Commit

Permalink
Simplified CompileNodeToValue
Browse files Browse the repository at this point in the history
  • Loading branch information
kukulich committed May 8, 2018
1 parent c614882 commit e3e8140
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 210 deletions.
218 changes: 17 additions & 201 deletions src/NodeCompiler/CompileNodeToValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@

namespace Roave\BetterReflection\NodeCompiler;

use PhpParser\ConstExprEvaluator;
use PhpParser\Node;
use ReflectionFunction;
use Roave\BetterReflection\NodeCompiler\Exception\UnableToCompileNode;
use Roave\BetterReflection\Reflection\ReflectionClass;
use Roave\BetterReflection\Reflector\Exception\IdentifierNotFound;
use Roave\BetterReflection\Util\FileHelper;
use function array_combine;
use function array_map;
use function constant;
use function defined;
use function dirname;
Expand All @@ -22,9 +19,6 @@

class CompileNodeToValue
{
/** @var callable[]|null indexed by supported expression node class name */
private static $nodeEvaluators;

/**
* Compile an expression from a node into a value.
*
Expand All @@ -40,63 +34,27 @@ public function __invoke(Node $node, CompilerContext $context)
return $this($node->expr, $context);
}

if ($node instanceof Node\Scalar\String_
|| $node instanceof Node\Scalar\DNumber
|| $node instanceof Node\Scalar\LNumber) {
return $node->value;
}

// common edge case - negative numbers
if ($node instanceof Node\Expr\UnaryMinus) {
return $this($node->expr, $context) * -1;
}

if ($node instanceof Node\Expr\Array_) {
return $this->compileArray($node, $context);
}

if ($node instanceof Node\Expr\ConstFetch) {
return $this->compileConstFetch($node);
}

if ($node instanceof Node\Expr\ClassConstFetch) {
return $this->compileClassConstFetch($node, $context);
}

if ($node instanceof Node\Expr\BinaryOp) {
return $this->compileBinaryOperator($node, $context);
}

if ($node instanceof Node\Scalar\MagicConst\Dir) {
return $this->compileDirConstant($context);
}

if ($node instanceof Node\Scalar\MagicConst\Class_) {
return $this->compileClassConstant($context);
}
$constExprEvaluator = new ConstExprEvaluator(function (Node\Expr $node) use ($context) {
if ($node instanceof Node\Expr\ConstFetch) {
return $this->compileConstFetch($node);
}

throw new Exception\UnableToCompileNode('Unable to compile expression: ' . get_class($node));
}
if ($node instanceof Node\Expr\ClassConstFetch) {
return $this->compileClassConstFetch($node, $context);
}

/**
* Compile arrays
*
* @return mixed[]
*/
private function compileArray(Node\Expr\Array_ $arrayNode, CompilerContext $context) : array
{
$compiledArray = [];
foreach ($arrayNode->items as $arrayItem) {
$compiledValue = $this($arrayItem->value, $context);
if ($node instanceof Node\Scalar\MagicConst\Dir) {
return $this->compileDirConstant($context);
}

if ($arrayItem->key === null) {
$compiledArray[] = $compiledValue;
continue;
if ($node instanceof Node\Scalar\MagicConst\Class_) {
return $this->compileClassConstant($context);
}

$compiledArray[$this($arrayItem->key, $context)] = $compiledValue;
}
return $compiledArray;
throw new Exception\UnableToCompileNode('Unable to compile expression: ' . get_class($node));
});

return $constExprEvaluator->evaluateDirectly($node);
}

/**
Expand Down Expand Up @@ -162,30 +120,6 @@ private function compileClassConstFetch(Node\Expr\ClassConstFetch $node, Compile
);
}

/**
* Compile a binary operator node
*
*
* @return mixed
*
* @throws UnableToCompileNode
*/
private function compileBinaryOperator(Node\Expr\BinaryOp $node, CompilerContext $context)
{
$evaluators = self::loadEvaluators();
$nodeClass = get_class($node);

if (! isset($evaluators[$nodeClass])) {
throw new Exception\UnableToCompileNode(sprintf(
'Unable to compile binary operator: %s',
$nodeClass
));
}

// Welcome to method overloading implemented PHP-style. Yay?
return $evaluators[$nodeClass]($node, $context, $this);
}

/**
* Compile a __DIR__ node
*/
Expand All @@ -212,122 +146,4 @@ private function getConstantDeclaringClass(string $constantName, ReflectionClass

return $parentClass ? $this->getConstantDeclaringClass($constantName, $parentClass) : null;
}

/**
* @return callable[] indexed by node class name
*/
private static function loadEvaluators() : array
{
if (self::$nodeEvaluators) {
return self::$nodeEvaluators;
}

$evaluators = self::makeEvaluators();

return self::$nodeEvaluators = array_combine(
array_map(function (callable $nodeEvaluator) : string {
/** @noinspection ExceptionsAnnotatingAndHandlingInspection */
/** @noinspection NullPointerExceptionInspection */
return (new ReflectionFunction($nodeEvaluator))->getParameters()[0]->getType()->getName();
}, $evaluators),
$evaluators
);
}

/**
* @return callable[]
*/
private static function makeEvaluators() : array
{
return [
function (Node\Expr\BinaryOp\Plus $node, CompilerContext $context, self $next) {
return $next($node->left, $context) + $next($node->right, $context);
},
function (Node\Expr\BinaryOp\Mul $node, CompilerContext $context, self $next) {
return $next($node->left, $context) * $next($node->right, $context);
},
function (Node\Expr\BinaryOp\Minus $node, CompilerContext $context, self $next) {
return $next($node->left, $context) - $next($node->right, $context);
},
function (Node\Expr\BinaryOp\Div $node, CompilerContext $context, self $next) {
return $next($node->left, $context) / $next($node->right, $context);
},
function (Node\Expr\BinaryOp\Concat $node, CompilerContext $context, self $next) {
return $next($node->left, $context) . $next($node->right, $context);
},
function (Node\Expr\BinaryOp\BooleanAnd $node, CompilerContext $context, self $next) {
return $next($node->left, $context) && $next($node->right, $context);
},
function (Node\Expr\BinaryOp\BooleanOr $node, CompilerContext $context, self $next) {
return $next($node->left, $context) || $next($node->right, $context);
},
function (Node\Expr\BinaryOp\BitwiseAnd $node, CompilerContext $context, self $next) {
return $next($node->left, $context) & $next($node->right, $context);
},
function (Node\Expr\BinaryOp\BitwiseOr $node, CompilerContext $context, self $next) {
return $next($node->left, $context) | $next($node->right, $context);
},
function (Node\Expr\BinaryOp\BitwiseXor $node, CompilerContext $context, self $next) {
return $next($node->left, $context) ^ $next($node->right, $context);
},
function (Node\Expr\BinaryOp\Equal $node, CompilerContext $context, self $next) {
/** @noinspection TypeUnsafeComparisonInspection */
// phpcs:disable SlevomatCodingStandard.ControlStructures.DisallowEqualOperators
return $next($node->left, $context) == $next($node->right, $context);
// phpcs:enable
},
function (Node\Expr\BinaryOp\Greater $node, CompilerContext $context, self $next) {
return $next($node->left, $context) > $next($node->right, $context);
},
function (Node\Expr\BinaryOp\GreaterOrEqual $node, CompilerContext $context, self $next) {
return $next($node->left, $context) >= $next($node->right, $context);
},
function (Node\Expr\BinaryOp\Identical $node, CompilerContext $context, self $next) {
return $next($node->left, $context) === $next($node->right, $context);
},
function (Node\Expr\BinaryOp\LogicalAnd $node, CompilerContext $context, self $next) {
// phpcs:disable Squiz.Operators.ValidLogicalOperators.NotAllowed
return $next($node->left, $context) and $next($node->right, $context);
// phpcs:enable
},
function (Node\Expr\BinaryOp\LogicalOr $node, CompilerContext $context, self $next) {
// phpcs:disable Squiz.Operators.ValidLogicalOperators.NotAllowed
return $next($node->left, $context) or $next($node->right, $context);
// phpcs:enable
},
function (Node\Expr\BinaryOp\LogicalXor $node, CompilerContext $context, self $next) {
return $next($node->left, $context) xor $next($node->right, $context);
},
function (Node\Expr\BinaryOp\Mod $node, CompilerContext $context, self $next) {
return $next($node->left, $context) % $next($node->right, $context);
},
function (Node\Expr\BinaryOp\NotEqual $node, CompilerContext $context, self $next) {
/** @noinspection TypeUnsafeComparisonInspection */
// phpcs:disable SlevomatCodingStandard.ControlStructures.DisallowEqualOperators
return $next($node->left, $context) != $next($node->right, $context);
// phpcs:enable
},
function (Node\Expr\BinaryOp\NotIdentical $node, CompilerContext $context, self $next) {
return $next($node->left, $context) !== $next($node->right, $context);
},
function (Node\Expr\BinaryOp\Pow $node, CompilerContext $context, self $next) {
return $next($node->left, $context) ** $next($node->right, $context);
},
function (Node\Expr\BinaryOp\ShiftLeft $node, CompilerContext $context, self $next) {
return $next($node->left, $context) << $next($node->right, $context);
},
function (Node\Expr\BinaryOp\ShiftRight $node, CompilerContext $context, self $next) {
return $next($node->left, $context) >> $next($node->right, $context);
},
function (Node\Expr\BinaryOp\Smaller $node, CompilerContext $context, self $next) {
return $next($node->left, $context) < $next($node->right, $context);
},
function (Node\Expr\BinaryOp\SmallerOrEqual $node, CompilerContext $context, self $next) {
return $next($node->left, $context) <= $next($node->right, $context);
},
function (Node\Expr\BinaryOp\Spaceship $node, CompilerContext $context, self $next) {
return $next($node->left, $context) <=> $next($node->right, $context);
},
];
}
}
10 changes: 1 addition & 9 deletions test/unit/NodeCompiler/CompileNodeToValueTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
namespace Roave\BetterReflectionTest\NodeCompiler;

use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp\Coalesce;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\Yield_;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Parser;
use PHPUnit\Framework\TestCase;
use Roave\BetterReflection\NodeCompiler\CompileNodeToValue;
Expand Down Expand Up @@ -150,6 +148,7 @@ public function nodeProvider() : array
['1 <=> 4', -1],
['4 <=> 1', 1],
['1 <=> 1', 0],
['5 ?? 4', 5],
];
}

Expand All @@ -173,13 +172,6 @@ public function testExceptionThrownWhenInvalidNodeGiven() : void
(new CompileNodeToValue())->__invoke(new Yield_(), $this->getDummyContext());
}

public function testExceptionThrownWhenCoalesceOperatorUsed() : void
{
$this->expectException(UnableToCompileNode::class);
$this->expectExceptionMessage('Unable to compile binary operator');
(new CompileNodeToValue())->__invoke(new Coalesce(new LNumber(5), new LNumber(3)), $this->getDummyContext());
}

public function testExceptionThrownWhenUndefinedConstUsed() : void
{
$this->expectException(UnableToCompileNode::class);
Expand Down

0 comments on commit e3e8140

Please sign in to comment.