Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add combination operators to selectors #187

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/Parser/Ast/Operator/AllOf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php declare(strict_types=1);

namespace PhpAT\Parser\Ast\Operator;

use PhpAT\Parser\Ast\ClassLike;

final class AllOf implements ClassLike
{
/** @var ClassLike */
private array $constraints;

public function __construct(
ClassLike ...$constraints,
) {
$this->constraints = $constraints;
}

public function matches(string $name): bool
{
foreach ($this->constraints as $constraint) {
if ($constraint->matches($name) === false) {
return false;
}
}

return true;
}

public function getMatchingNodes(array $nodes): array
{
return array_merge(
[],
...array_map(
fn (ClassLike $constraint) => $constraint->getMatchingNodes($nodes),
$this->constraints,
)
);
}

public function toString(): string
{
return implode(' and ', array_map(
fn (ClassLike $constraint) => $constraint->toString(),
$this->constraints,
));
}
}
47 changes: 47 additions & 0 deletions src/Parser/Ast/Operator/AnyOf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php declare(strict_types=1);

namespace PhpAT\Parser\Ast\Operator;

use PhpAT\Parser\Ast\ClassLike;

final class AnyOf implements ClassLike
{
/** @var ClassLike */
private array $constraints;

public function __construct(
ClassLike ...$constraints,
) {
$this->constraints = $constraints;
}

public function matches(string $name): bool
{
foreach ($this->constraints as $constraint) {
if ($constraint->matches($name) === true) {
return true;
}
}

return false;
}

public function getMatchingNodes(array $nodes): array
{
return array_merge(
[],
...array_map(
fn (ClassLike $constraint) => $constraint->getMatchingNodes($nodes),
$this->constraints,
)
);
}

public function toString(): string
{
return implode(' or ', array_map(
fn (ClassLike $constraint) => $constraint->toString(),
$this->constraints,
));
}
}
52 changes: 52 additions & 0 deletions src/Parser/Ast/Operator/AtLeastCountOf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php declare(strict_types=1);

namespace PhpAT\Parser\Ast\Operator;

use PhpAT\Parser\Ast\ClassLike;

final class AtLeastCountOf implements ClassLike
{
/** @var ClassLike */
private array $constraints;

public function __construct(
private int $threshold,
ClassLike ...$constraints,
) {
$this->constraints = $constraints;
}

public function matches(string $name): bool
{
$count = 0;
foreach ($this->constraints as $constraint) {
if ($constraint->matches($name) === true) {
$count++;
}
if ($count >= $this->threshold) {
return true;
}
}

return $count >= $this->threshold;
}

public function getMatchingNodes(array $nodes): array
{
return array_merge(
[],
...array_map(
fn (ClassLike $constraint) => $constraint->getMatchingNodes($nodes),
$this->constraints,
)
);
}

public function toString(): string
{
return 'at least one of '.implode(' and ', array_map(
fn (ClassLike $constraint) => $constraint->toString(),
$this->constraints,
));
}
}
52 changes: 52 additions & 0 deletions src/Parser/Ast/Operator/AtMostCountOf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php declare(strict_types=1);

namespace PhpAT\Parser\Ast\Operator;

use PhpAT\Parser\Ast\ClassLike;

final class AtMostCountOf implements ClassLike
{
/** @var ClassLike */
private array $constraints;

public function __construct(
private int $threshold,
ClassLike ...$constraints,
) {
$this->constraints = $constraints;
}

public function matches(string $name): bool
{
$count = 0;
foreach ($this->constraints as $constraint) {
if ($constraint->matches($name) === true) {
$count++;
}
if ($count > $this->threshold) {
return false;
}
}

return $count <= $this->threshold;
}

public function getMatchingNodes(array $nodes): array
{
return array_merge(
[],
...array_map(
fn (ClassLike $constraint) => $constraint->getMatchingNodes($nodes),
$this->constraints,
)
);
}

public function toString(): string
{
return 'at most one of '.implode(' and ', array_map(
fn (ClassLike $constraint) => $constraint->toString(),
$this->constraints,
));
}
}
47 changes: 47 additions & 0 deletions src/Parser/Ast/Operator/NoneOf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php declare(strict_types=1);

namespace PhpAT\Parser\Ast\Operator;

use PhpAT\Parser\Ast\ClassLike;

final class NoneOf implements ClassLike
{
/** @var ClassLike */
private array $constraints;

public function __construct(
ClassLike ...$constraints,
) {
$this->constraints = $constraints;
}

public function matches(string $name): bool
{
foreach ($this->constraints as $constraint) {
if ($constraint->matches($name) === true) {
return false;
}
}

return true;
}

public function getMatchingNodes(array $nodes): array
{
return array_merge(
[],
...array_map(
fn (ClassLike $constraint) => $constraint->getMatchingNodes($nodes),
$this->constraints,
)
);
}

public function toString(): string
{
return 'none of '.implode(' or ', array_map(
fn (ClassLike $constraint) => $constraint->toString(),
$this->constraints,
));
}
}
52 changes: 52 additions & 0 deletions src/Parser/Ast/Operator/OneOf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php declare(strict_types=1);

namespace PhpAT\Parser\Ast\Operator;

use PhpAT\Parser\Ast\ClassLike;

final class OneOf implements ClassLike
{
/** @var ClassLike */
private array $constraints;

public function __construct(
ClassLike ...$constraints,
) {
$this->constraints = $constraints;
}

public function matches(string $name): bool
{
$count = 0;
foreach ($this->constraints as $constraint) {
if ($constraint->matches($name) === true) {
$count++;
}

if ($count > 1) {
return false;
}
}

return $count === 1;
}

public function getMatchingNodes(array $nodes): array
{
return array_merge(
[],
...array_map(
fn (ClassLike $constraint) => $constraint->getMatchingNodes($nodes),
$this->constraints,
)
);
}

public function toString(): string
{
return 'one of '.implode(' or ', array_map(
fn (ClassLike $constraint) => $constraint->toString(),
$this->constraints,
));
}
}
53 changes: 53 additions & 0 deletions src/Selector/Operator/AllOfSelector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php declare(strict_types=1);

namespace PhpAT\Selector\Operator;

use PhpAT\Parser\Ast\ClassLike;
use PhpAT\Parser\Ast\Operator\AllOf;
use PhpAT\Parser\Ast\ReferenceMap;
use PhpAT\Selector\SelectorInterface;

final class AllOfSelector implements SelectorInterface
{
/** @var list<SelectorInterface> */
private array $selectors;

public function __construct(SelectorInterface ...$selectors)
{
$this->selectors = $selectors;
}

public function getDependencies(): array
{
return array_merge(
...array_map(fn (SelectorInterface $selector) => $selector->getDependencies(), $this->selectors)
);
}

public function injectDependencies(array $dependencies): void
{
array_walk($this->selectors, fn (SelectorInterface $selector) => $selector->injectDependencies($dependencies));
}

public function setReferenceMap(ReferenceMap $map): void
{
array_walk($this->selectors, fn (SelectorInterface $selector) => $selector->setReferenceMap($map));
}

/**
* @return array<ClassLike>
*/
public function select(): array
{
return [
new AllOf(
...array_map(fn (SelectorInterface $selector) => new AllOf(...$selector->select()), $this->selectors)
),
];
}

public function getParameter(): string
{
return implode(' and ', array_map(fn (SelectorInterface $selector) => $selector->getParameter(), $this->selectors));
}
}
Loading