forked from CuyZ/Valinor
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: introduce compiled normalizer cache
- Loading branch information
Showing
69 changed files
with
3,003 additions
and
289 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CuyZ\Valinor\Compiler; | ||
|
||
use function count; | ||
use function str_repeat; | ||
use function str_replace; | ||
|
||
/** @internal */ | ||
final class Compiler | ||
{ | ||
private string $code = ''; | ||
|
||
/** @var non-negative-int */ | ||
private int $indentation = 0; | ||
|
||
public function compile(Node ...$nodes): self | ||
{ | ||
$compiler = $this; | ||
|
||
while ($current = array_shift($nodes)) { | ||
$compiler = $current->compile($compiler); | ||
|
||
if (count($nodes) > 0) { | ||
$compiler = $compiler->write("\n"); | ||
} | ||
} | ||
|
||
return $compiler; | ||
} | ||
|
||
public function sub(): self | ||
{ | ||
return new self(); | ||
} | ||
|
||
public function write(string $code): self | ||
{ | ||
$self = clone $this; | ||
$self->code .= $code; | ||
|
||
return $self; | ||
} | ||
|
||
public function indent(): self | ||
{ | ||
$self = clone $this; | ||
$self->indentation++; | ||
|
||
return $self; | ||
} | ||
|
||
public function code(): string | ||
{ | ||
$indent = str_repeat(' ', $this->indentation); | ||
|
||
return $indent . str_replace("\n", "\n" . $indent, $this->code); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CuyZ\Valinor\Compiler\Library; | ||
|
||
use CuyZ\Valinor\Compiler\Compiler; | ||
use CuyZ\Valinor\Compiler\Node; | ||
use CuyZ\Valinor\Definition\AttributeDefinition; | ||
|
||
use function array_map; | ||
use function serialize; | ||
|
||
/** @internal */ | ||
final class NewAttributeNode extends Node | ||
{ | ||
public function __construct(private AttributeDefinition $attribute) {} | ||
|
||
public function compile(Compiler $compiler): Compiler | ||
{ | ||
$argumentNodes = $this->argumentNode($this->attribute->arguments); | ||
|
||
return $compiler->compile( | ||
Node::newClass( | ||
$this->attribute->class->name, | ||
...$argumentNodes, | ||
), | ||
); | ||
} | ||
|
||
/** | ||
* @param array<mixed> $arguments | ||
* @return list<Node> | ||
*/ | ||
private function argumentNode(array $arguments): array | ||
{ | ||
return array_map(function (mixed $argument) { | ||
if (is_object($argument)) { | ||
return Node::functionCall( | ||
name: 'unserialize', | ||
arguments: [Node::value(serialize($argument))], | ||
); | ||
} | ||
|
||
if (is_array($argument)) { | ||
return Node::array($this->argumentNode($argument)); | ||
} | ||
|
||
/** @var scalar $argument */ | ||
return Node::value($argument); | ||
}, $arguments); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CuyZ\Valinor\Compiler\Library; | ||
|
||
use CuyZ\Valinor\Compiler\Compiler; | ||
use CuyZ\Valinor\Compiler\Node; | ||
use CuyZ\Valinor\Type\CompositeTraversableType; | ||
use CuyZ\Valinor\Type\FixedType; | ||
use CuyZ\Valinor\Type\ObjectType; | ||
use CuyZ\Valinor\Type\Type; | ||
use CuyZ\Valinor\Type\Types\EnumType; | ||
use CuyZ\Valinor\Type\Types\MixedType; | ||
use CuyZ\Valinor\Type\Types\NativeBooleanType; | ||
use CuyZ\Valinor\Type\Types\NativeFloatType; | ||
use CuyZ\Valinor\Type\Types\NativeIntegerType; | ||
use CuyZ\Valinor\Type\Types\NativeStringType; | ||
use CuyZ\Valinor\Type\Types\NegativeIntegerType; | ||
use CuyZ\Valinor\Type\Types\NullType; | ||
use CuyZ\Valinor\Type\Types\ShapedArrayElement; | ||
use CuyZ\Valinor\Type\Types\ShapedArrayType; | ||
use CuyZ\Valinor\Type\Types\UndefinedObjectType; | ||
use CuyZ\Valinor\Type\Types\UnionType; | ||
use LogicException; | ||
use UnitEnum; | ||
|
||
use function array_map; | ||
use function implode; | ||
use function var_export; | ||
|
||
/** @internal */ | ||
final class TypeAcceptNode extends Node | ||
{ | ||
public function __construct(private Type $type) {} | ||
|
||
public function compile(Compiler $compiler): Compiler | ||
{ | ||
return $this->compileType($compiler, $this->type); | ||
} | ||
|
||
private function compileType(Compiler $compiler, Type $type): Compiler | ||
{ | ||
return match (true) { | ||
$type instanceof CompositeTraversableType => $compiler->write('\is_iterable($value)'), | ||
$type instanceof EnumType => $this->compileEnumType($compiler, $type), | ||
$type instanceof FixedType => $compiler->write('$value === ' . var_export($type->value(), true)), | ||
$type instanceof MixedType => $compiler->write('true'), | ||
$type instanceof NativeBooleanType => $compiler->write('\is_bool($value)'), | ||
$type instanceof NativeFloatType => $compiler->write('\is_float($value)'), | ||
$type instanceof NativeIntegerType => $compiler->write('\is_int($value)'), | ||
// @todo positive int | ||
$type instanceof NativeStringType => $compiler->write('\is_string($value)'), | ||
$type instanceof NegativeIntegerType => $compiler->write('\is_string($value) && $value < 0'), | ||
$type instanceof NullType => $compiler->write('\is_null($value)'), | ||
$type instanceof ObjectType => $compiler->write("\$value instanceof ('{$type->className()}')"), // @todo anonymous class contains absolute file path | ||
$type instanceof ShapedArrayType => $this->compileShapedArrayType($compiler, $type), | ||
$type instanceof UnionType => $this->compileUnionType($compiler, $type), | ||
$type instanceof UndefinedObjectType => $compiler->write('\is_object($value)'), | ||
default => throw new LogicException("Type `{$type->toString()}` cannot be compiled."), | ||
}; | ||
} | ||
|
||
private function compileEnumType(Compiler $compiler, EnumType $type): Compiler | ||
{ | ||
$code = '$value instanceof ' . $type->className(); | ||
|
||
if ($type->cases() !== []) { | ||
$code .= ' && (' . implode( | ||
' || ', | ||
array_map( | ||
fn (UnitEnum $enum) => '$value === ' . $enum::class . '::' . $enum->name, | ||
$type->cases() | ||
) | ||
) . ')'; | ||
} | ||
|
||
return $compiler->write($code); | ||
} | ||
|
||
private function compileShapedArrayType(Compiler $compiler, ShapedArrayType $type): Compiler | ||
{ | ||
// @todo test | ||
$code = implode( | ||
' && ', | ||
array_map( | ||
fn (ShapedArrayElement $element) => $this->compileType($compiler->sub(), $type)->code(), | ||
$type->elements() | ||
) | ||
); | ||
|
||
return $compiler->write($code); | ||
} | ||
|
||
private function compileUnionType(Compiler $compiler, UnionType $type): Compiler | ||
{ | ||
$code = implode( | ||
' || ', | ||
array_map( | ||
fn (Type $type) => $this->compileType($compiler->sub(), $type)->code(), | ||
$type->types() | ||
) | ||
); | ||
|
||
return $compiler->write($code); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CuyZ\Valinor\Compiler\Native; | ||
|
||
use CuyZ\Valinor\Compiler\Compiler; | ||
use CuyZ\Valinor\Compiler\Node; | ||
|
||
/** @internal */ | ||
final class AggregateNode extends Node | ||
{ | ||
/** @var array<Node> */ | ||
private array $nodes; | ||
|
||
public function __construct(Node ...$nodes) | ||
{ | ||
$this->nodes = $nodes; | ||
} | ||
|
||
public function compile(Compiler $compiler): Compiler | ||
{ | ||
while ($current = array_shift($this->nodes)) { | ||
$compiler = $current->compile($compiler); | ||
|
||
if (count($this->nodes) > 0) { | ||
$compiler = $compiler->write("\n"); | ||
} | ||
} | ||
|
||
return $compiler; | ||
} | ||
} |
Oops, something went wrong.