public function __construct(Providers\IQueryProvider $provider, Queries\ISourceInfo $sourceInfo, O\TraversalExpression $queryExpression = null, IIteratorScheme $scheme = null) { parent::__construct($provider); $this->sourceInfo = $sourceInfo; $this->expression = $queryExpression ?: O\Expression::value($this); $this->scheme = $scheme ?: Iterators\SchemeProvider::getDefault(); }
/** * @dataProvider interpreters */ public function testParameterExpressions() { $this->assertParametersAre(function ($i) { }, [O\Expression::parameter('i')]); $this->assertParametersAre(function ($i, $foo) { }, [O\Expression::parameter('i'), O\Expression::parameter('foo')]); $this->assertParametersAre(function ($i = null) { }, [O\Expression::parameter('i', null, O\Expression::value(null))]); $this->assertParametersAre(function ($i = 'bar') { }, [O\Expression::parameter('i', null, O\Expression::value('bar'))]); $this->assertParametersAre(function (\DateTime $i) { }, [O\Expression::parameter('i', '\\DateTime')]); $this->assertParametersAre(function (self $i) { }, [O\Expression::parameter('i', '\\' . __CLASS__)]); $this->assertParametersAre(function (parent $i) { }, [O\Expression::parameter('i', '\\' . get_parent_class())]); $this->assertParametersAre(function (MiscInterpreterTest $i) { }, [O\Expression::parameter('i', '\\' . __CLASS__)]); $this->assertParametersAre(function (ParameterClassTest $i) { }, [O\Expression::parameter('i', '\\' . __NAMESPACE__ . '\\ParameterClassTest')]); $this->assertParametersAre(function (namespace\ParameterClassTest $i) { }, [O\Expression::parameter('i', '\\' . __NAMESPACE__ . '\\ParameterClassTest')]); $this->assertParametersAre(function (&$i) { }, [O\Expression::parameter('i', null, null, true)]); $this->assertParametersAre(function (\stdClass &$i = null, array $array = ['foo']) { }, [O\Expression::parameter('i', '\\stdClass', O\Expression::value(null), true), O\Expression::parameter('array', 'array', O\Expression::value(['foo']))]); $this->assertParametersAre(function (callable &$v = null) { }, [O\Expression::parameter('v', 'callable', O\Expression::value(null), true)]); $this->assertParametersAre(function ($v = [1, 2, 3, 'test' => 'foo', [2 => 'boo', '']]) { }, [O\Expression::parameter('v', null, O\Expression::value([1, 2, 3, 'test' => 'foo', [2 => 'boo', '']]))]); }
public function __construct(ITypeSystem $typeSystem, O\IEvaluationContext $evaluationContext) { parent::__construct($typeSystem); $this->evaluationContext = $evaluationContext; foreach ($evaluationContext->getVariableTable() as $variable => $value) { $this->setExpressionType(O\Expression::variable(O\Expression::value($variable)), $typeSystem->getTypeFromValue($value)); } }
public function walkVariable(VariableExpression $expression) { $name = $expression->getName(); if ($name instanceof O\ValueExpression) { return $expression->update(O\Expression::value($this->prefix . $name->getValue())); } return $expression->update(O\Expression::binaryOperation(O\Expression::value($this->prefix), O\Operators\Binary::CONCATENATION, $name)); }
public function testAssignmentToBinaryOperatorEquivalent() { foreach ([O\Operators\Assignment::EQUAL, O\Operators\Assignment::EQUAL_REFERENCE] as $operatorThatShouldNotChange) { $assignment = O\Expression::assign(O\Expression::variable(O\Expression::value('foo')), $operatorThatShouldNotChange, O\Expression::variable(O\Expression::value('bar'))); $this->assertSame($assignment, $assignment->toBinaryOperationEquivalent()); } $assignment = O\Expression::assign(O\Expression::variable(O\Expression::value('foo')), O\Operators\Assignment::ADDITION, O\Expression::variable(O\Expression::value('bar'))); $this->assertEquals(O\Expression::assign(O\Expression::variable(O\Expression::value('foo')), O\Operators\Assignment::EQUAL, O\Expression::binaryOperation(O\Expression::variable(O\Expression::value('foo')), O\Operators\Binary::ADDITION, O\Expression::variable(O\Expression::value('bar')))), $assignment->toBinaryOperationEquivalent()); }
public function testFunctionGetsReturnValueExpression() { /** @var $function Functions\ProjectionBase */ $function = $this->functionWithReturnStatement(); $this->assertSame(true, $function->hasReturnExpression()); $this->assertEquals(O\Expression::returnExpression(O\Expression::value('return')), $function->getReturnExpression()); $this->assertSame(true, $function->hasReturnValueExpression()); $this->assertEquals(O\Expression::value('return'), $function->getReturnValueExpression()); }
public function testWalksBodyAndParameterExpressions() { $processor = $this->getMock('Pinq\\Providers\\DSL\\Compilation\\Processors\\Expression\\ExpressionProcessor', ['walkValue']); $processor->expects($this->exactly(2))->method('walkValue')->will($this->returnValue(O\Expression::value('updated'))); /** @var ExpressionProcessor $processor */ $function = new Functions\ElementProjection('', null, null, [], [O\Expression::parameter('param', 'abc', O\Expression::value('default'))], [O\Expression::value('body')]); $processedFunction = $processor->processFunction($function); $this->assertEquals([O\Expression::value('updated')], $processedFunction->getBodyExpressions()); $this->assertEquals(O\Expression::parameter('param', 'abc', O\Expression::value('updated')), $processedFunction->getParameters()->getValue()); }
public function testExpressionWalkerWorks() { $expression = O\Expression::variable(O\Expression::value('foo')); $expressionWalker = new O\DynamicExpressionWalker([O\ValueExpression::getType() => function (O\ValueExpression $expression) { return O\Expression::value('bar'); }]); $newExpression = $expressionWalker->walk($expression); $this->assertNotEquals($expression, $newExpression); $this->assertSame($newExpression->getName()->getValue(), 'bar'); }
public function testFunctionWithDefaultRelativeConstant() { /** @var $function Functions\ElementProjection */ $function = $this->buildFunction('', null, __NAMESPACE__, [], [O\Expression::parameter('value'), O\Expression::parameter('key'), O\Expression::parameter('excessive1', null, O\Expression::constant('__RELATIVE_CONSTANT')), O\Expression::parameter('excessive2', null, O\Expression::value([false]))]); $this->assertSame(false, $function->getParameters()->hasRequiredUnusedParameters()); $this->assertEquals([], $function->getParameters()->getRequiredUnusedParameters()); $this->assertEquals([O\Expression::parameter('excessive1', null, O\Expression::constant('__RELATIVE_CONSTANT')), O\Expression::parameter('excessive2', null, O\Expression::value([false]))], $function->getParameters()->getUnused()); $this->assertEquals(['excessive1' => O\Expression::constant('__RELATIVE_CONSTANT'), 'excessive2' => O\Expression::value([false])], $function->getParameters()->getUnusedParameterDefaultMap()); //This fails in PHP due to strange behaviour with closures and relative constants. //Bug reported: https://bugs.php.net/bug.php?id=67897 if (defined('HHVM_VERSION')) { define('__RELATIVE_CONSTANT', 'in global namespace'); $this->assertEquals(['excessive1' => 'in global namespace', 'excessive2' => [false]], $function->getEvaluationContextFactory()->getEvaluationContext()->getVariableTable()); //Should take precedence define(__NAMESPACE__ . '\\__RELATIVE_CONSTANT', 'in relative namespace'); $this->assertEquals(['excessive1' => 'in relative namespace', 'excessive2' => [false]], $function->getEvaluationContextFactory()->getEvaluationContext()->getVariableTable()); } }
public function testFunctionWithReturnStatement() { $function = $this->functionWithReturnStatement(); $this->assertSame('', $function->getCallableId()); $this->assertSame(1, $function->getParameters()->count()); $this->assertEquals([O\Expression::parameter('foo')], $function->getParameters()->getAll()); $this->assertEquals(['param' => 'scope'], $function->getParameterScopedVariableMap()); $this->assertEquals(['', 'param'], $function->getParameterIds()); $this->assertSame(true, $function->hasScopeType()); $this->assertSame(__CLASS__, $function->getScopeType()); $this->assertSame(true, $function->hasNamespace()); $this->assertSame(__NAMESPACE__, $function->getNamespace()); $this->assertSame(4, $function->countBodyExpressions()); $this->assertSame(2, $function->countBodyExpressionsUntilReturn()); $this->assertEquals([O\Expression::value('before'), O\Expression::returnExpression(O\Expression::value('return')), O\Expression::value('after'), O\Expression::functionCall(O\Expression::value('boom'))], $function->getBodyExpressions()); $this->assertEquals([O\Expression::value('before'), O\Expression::returnExpression(O\Expression::value('return'))], $function->getBodyExpressionsUntilReturn()); $this->assertEquals(new O\EvaluationContext(__NAMESPACE__, __CLASS__, null, ['scope' => 'value']), $function->getEvaluationContextFactory()->getEvaluationContext(new ResolvedParameterRegistry(['param' => 'value']))); }
public function interpretSource(O\Expression $expression) { $isQueryScope = false; $queryableQueryResolver = new O\DynamicExpressionWalker([O\TraversalExpression::getType() => function (O\TraversalExpression $expression, O\ExpressionWalker $self) use(&$isQueryScope) { $expression = $expression->updateValue($self->walk($expression->getValue())); if ($isQueryScope) { return $expression; } else { return $self->walk(O\Expression::value($expression->evaluate($this->evaluationContext))); } }, O\ValueExpression::getType() => function (O\ValueExpression $expression) use(&$isQueryScope) { if ($expression->getValue() instanceof IQueryable) { $isQueryScope = true; } return $expression; }]); $expression = $queryableQueryResolver->walk($expression); if ($isQueryScope) { $this->scopeInterpreter->interpretScope($expression); $this->interpretation->interpretQueryScope($this->getId('source-scope'), $this->scopeInterpreter->getInterpretation()); } else { $this->interpretation->interpretArrayOrIterator($this->getId('source-iterator'), $expression->evaluate($this->evaluationContext)); } }
protected function parseQueryExpression(callable $queryFunction, O\IEvaluationContext &$evaluationContext = null) { $reflection = $this->functionInterpreter->getReflection($queryFunction); $evaluationContext = $reflection->asEvaluationContext(); $function = $this->functionInterpreter->getStructure($reflection); $expressions = $function->getBodyExpressions(); $this->assertCount(1, $expressions); //Resolve the parameter variable with the queryable value and $this $parameterName = $reflection->getSignature()->getParameterExpressions()[0]->getName(); $expression = $expressions[0]; foreach ([$parameterName => $this->queryable, 'this' => $this] as $variable => $value) { $variableReplacer = new O\DynamicExpressionWalker([O\VariableExpression::getType() => function (O\VariableExpression $expression) use($variable, &$value) { if ($expression->getName() instanceof O\ValueExpression && $expression->getName()->getValue() === $variable) { return O\Expression::value($value); } else { return $expression; } }, O\ClosureExpression::getType() => function ($closure) { return $closure; }]); $expression = $variableReplacer->walk($expression); } if ($expression instanceof O\ReturnExpression) { return $expression->getValue(); } else { return $expression; } }
/** * @param string $name * @param mixed[] $arguments * * @return O\MethodCallExpression */ protected function newMethod($name, array $arguments = []) { return O\Expression::methodCall($this->expression, O\Expression::value($name), array_map([O\Expression::getType(), 'argument'], array_map([O\Expression::getType(), 'value'], $arguments))); }
/** * @dataProvider parsers */ public function testInvokableObject() { $this->assertParsedAs(new InvokableObject(), [O\Expression::returnExpression(O\Expression::value(1.1))]); }
/** * @dataProvider parsers */ public function testArgumentUnpacking() { $this->assertParsedAs([$this->parameters, 'argumentUnpacking'], [O\Expression::functionCall(O\Expression::value('func'), [O\Expression::argument(O\Expression::arrayExpression([]), true)])]); }
protected static function variable($name) { return O\Expression::variable(O\Expression::value($name)); }
/** * @dataProvider parsers */ public function testMagicLine() { $this->assertParsedAs(function () { __LINE__; }, [O\Expression::value(__LINE__ - 2)]); }
public function tryComputeResults(O\Expression $queryExpression, &$results) { if (isset($this->storage[$queryExpression])) { $results = $this->storage[$queryExpression]; return true; } $foundApplicableResults = false; //Searches the query expression tree and checks if any parent expressions have saved results //If so, the expression tree is updated with a Traversable implementation with the saved results $applicableScopeFinder = function (O\Expression $expression, O\ExpressionWalker $self) use(&$foundApplicableResults) { if (isset($this->storage[$expression])) { $foundApplicableResults = true; return O\Expression::value($this->newTraversable($this->storage[$expression])); } if ($expression instanceof O\ValueExpression) { return $expression; } /** @var $expression O\TraversalExpression */ return $expression->updateValue($self->walk($expression->getValue())); }; $traversalWalker = new O\DynamicExpressionWalker([O\TraversalExpression::getType() => $applicableScopeFinder, O\ValueExpression::getType() => $applicableScopeFinder]); $remainingQueryExpression = $traversalWalker->walk($queryExpression); //If found applicable results, execute the updated expression tree against the Traversable //implementation to compute the result of the query. $results = $foundApplicableResults ? $remainingQueryExpression->evaluate() : null; return $foundApplicableResults; }
/** * @dataProvider parsers */ public function testAssignmentOperator() { $this->assertParsedAs([PowerOperators::TYPE, 'assignmentOperator'], [O\Expression::assign(O\Expression::variable(O\Expression::value('i')), O\Operators\Assignment::POWER, O\Expression::value(5))]); }
public function expressions() { return [[O\Expression::arrayExpression([])], [O\Expression::arrayItem(null, O\Expression::value(0), false)], [O\Expression::assign(O\Expression::value(0), O\Operators\Assignment::EQUAL, O\Expression::value(0))], [O\Expression::binaryOperation(O\Expression::value(0), O\Operators\Binary::ADDITION, O\Expression::value(0))], [O\Expression::unaryOperation(O\Operators\Unary::PLUS, O\Expression::value(0))], [O\Expression::cast(O\Operators\Cast::STRING, O\Expression::value(0))], [O\Expression::closure(false, false, [], [], [])], [O\Expression::closureUsedVariable('var')], [O\Expression::emptyExpression(O\Expression::value(0))], [O\Expression::field(O\Expression::value(0), O\Expression::value(0))], [O\Expression::functionCall(O\Expression::value(0))], [O\Expression::index(O\Expression::value(0), O\Expression::value(0))], [O\Expression::invocation(O\Expression::value(0))], [O\Expression::issetExpression([O\Expression::value(0)])], [O\Expression::unsetExpression([O\Expression::value(0)])], [O\Expression::methodCall(O\Expression::value(0), O\Expression::value(0))], [O\Expression::newExpression(O\Expression::value(0))], [O\Expression::parameter('')], [O\Expression::argument(O\Expression::value(0))], [O\Expression::returnExpression()], [O\Expression::staticMethodCall(O\Expression::value(0), O\Expression::value(0))], [O\Expression::staticField(O\Expression::value(0), O\Expression::value(0))], [O\Expression::ternary(O\Expression::value(0), null, O\Expression::value(0))], [O\Expression::throwExpression(O\Expression::value(0))], [O\Expression::value(0)], [O\Expression::variable(O\Expression::value(0))], [O\Expression::constant('foo')], [O\Expression::classConstant(O\Expression::value(0), 'foo')]]; }
/** * Updates the matched expression with it's resolved value from * the supplied registry. * * @param IFunction $function * @param O\Expression $expression * @param Parameters\ResolvedParameterRegistry $parameters * * @return O\Expression */ public function inline(IFunction $function, O\Expression $expression, Parameters\ResolvedParameterRegistry $parameters) { /** @var O\InvocationExpression $expression */ return O\Expression::functionCall(O\Expression::value($this->getResolvedValue($parameters, $expression->getValue())), $expression->getArguments()); }
/** * Simplifies the expression tree in the supplied context. * Example: * <code> * -2 + 4 * </code> * Will become: * <code> * 2 * </code> * * @param IEvaluationContext|null $context * * @return Expression */ public function simplify(IEvaluationContext $context = null) { return Expression::value($this->evaluate($context)); }
/** * @dataProvider parsers */ private function assertCast(callable $function, $typeOperator, $castName) { $this->assertParsedAs($function, [O\Expression::cast($typeOperator, O\Expression::variable(O\Expression::value($castName)))]); }
public function visitRange(Segments\Range $query) { if (!$query instanceof StaticRange) { throw new PinqDemoSqlException('Range must be a static range class'); } // As the LIMIT clause will always be the last clause in the compiled SELECT // query, we do not need to wrap the SELECT in a derived table as this will // always be evaluated after the previous segment. $this->compilation->sql .= ' LIMIT '; if ($query->hasAmount()) { $this->compilation->sql .= $this->compilation->expressionCompiler->compile(O\Expression::value($query->getAmount())); } else { $this->compilation->sql .= '18446744073709551615'; } $this->compilation->sql .= ' OFFSET '; $this->compilation->sql .= $this->compilation->expressionCompiler->compile(O\Expression::value($query->getStart())); }
public function compileReturn(ProjectionBase $projectionFunction) { $returnExpression = $projectionFunction->getReturnValueExpression() ?: O\Expression::value(null); return $this->compile($returnExpression, $projectionFunction); }
public function walkConstant(O\ConstantExpression $expression) { $resolvedMagicConstant = $this->resolveMagicConstantValue($expression->getName()); if ($resolvedMagicConstant !== null) { return O\Expression::value($resolvedMagicConstant); } else { return $expression; } }
private function parseScalarNode(Node\Scalar $node) { switch (true) { case $node instanceof Node\Scalar\DNumber: case $node instanceof Node\Scalar\LNumber: case $node instanceof Node\Scalar\String_: return Expression::value($node->value); case $node instanceof Node\Scalar\MagicConst\Line: return Expression::value($node->getAttribute('startLine')); case $node instanceof Node\Scalar\MagicConst: return Expression::constant($node->getName()); default: return; } }
private static function getParameterExpression(\ReflectionParameter $parameter) { $typeHint = null; if ($parameter->isArray()) { $typeHint = 'array'; } elseif ($parameter->isCallable()) { $typeHint = 'callable'; } elseif ($parameter->getClass() !== null) { $typeHint = $parameter->getClass()->getName(); $typeHint = $typeHint[0] === '\\' ? $typeHint : '\\' . $typeHint; } return O\Expression::parameter($parameter->getName(), $typeHint, $parameter->isDefaultValueAvailable() ? O\Expression::value($parameter->getDefaultValue()) : null, $parameter->isPassedByReference(), method_exists($parameter, 'isVariadic') && $parameter->isVariadic()); }
/** * @dataProvider parsers */ public function testThatChainedMethodCalls() { $this->assertReturn(function (\Pinq\ITraversable $traversable) { return $traversable->asArray(); }, O\Expression::methodCall(O\Expression::variable(O\Expression::value('traversable')), O\Expression::value('asArray'))); $this->assertReturn(function (\Pinq\ITraversable $traversable) { return $traversable->where(function ($i) { return $i > 0; })->all(function ($i) { return $i % 2 === 0; }); }, O\Expression::methodCall(O\Expression::methodCall(O\Expression::variable(O\Expression::value('traversable')), O\Expression::value('where'), [O\Expression::argument(O\Expression::closure(false, false, [O\Expression::parameter('i')], [], [O\Expression::returnExpression(O\Expression::binaryOperation(O\Expression::variable(O\Expression::value('i')), O\Operators\Binary::GREATER_THAN, O\Expression::value(0)))]))]), O\Expression::value('all'), [O\Expression::argument(O\Expression::closure(false, false, [O\Expression::parameter('i')], [], [O\Expression::returnExpression(O\Expression::binaryOperation(O\Expression::binaryOperation(O\Expression::variable(O\Expression::value('i')), O\Operators\Binary::MODULUS, O\Expression::value(2)), O\Operators\Binary::IDENTITY, O\Expression::value(0)))]))])); }
/** * @param callable $function * @param array $variableTypeMap * @param mixed $expression * * @return ITypeAnalysis */ protected function getAnalysis(callable $function, array $variableTypeMap = [], &$expression = null) { $reflection = $this->functionInterpreter->getReflection($function); foreach ($reflection->getSignature()->getParameterExpressions() as $parameterExpression) { $variableTypeMap[$parameterExpression->getName()] = $this->typeSystem->getTypeFromTypeHint($parameterExpression->getTypeHint()); } $analysisContext = $this->expressionAnalyser->createAnalysisContext($reflection->asEvaluationContext()); foreach ($variableTypeMap as $variable => $type) { $analysisContext->setExpressionType(O\Expression::variable(O\Expression::value($variable)), $type); } $bodyExpressions = $this->functionInterpreter->getStructure($reflection)->getBodyExpressions(); foreach ($bodyExpressions as $expression) { if ($expression instanceof O\ReturnExpression) { return $this->expressionAnalyser->analyse($analysisContext, $expression->getValue()); } elseif (count($bodyExpressions) === 1) { return $this->expressionAnalyser->analyse($analysisContext, $expression); } else { $this->expressionAnalyser->analyse($analysisContext, $expression); } } }