allow vendord
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s

This commit is contained in:
2026-03-30 14:54:57 +07:00
parent 66aed7c4e8
commit b5e3a778ce
21316 changed files with 2777892 additions and 13 deletions

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler;
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 ($nodes !== []) {
$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);
}
}

View File

@@ -0,0 +1,44 @@
<?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 ReflectionClass;
use ReflectionProperty;
use function array_map;
/** @internal */
final class NewAttributeNode extends Node
{
public function __construct(private AttributeDefinition $attribute) {}
public function compile(Compiler $compiler): Compiler
{
if ($this->attribute->arguments !== null) {
return $compiler->compile(
Node::newClass(
$this->attribute->class->name,
...array_map(Node::value(...), $this->attribute->arguments),
),
);
}
// @phpstan-ignore match.unhandled (for now only those two cases can be handled here anyway)
$node = match ($this->attribute->reflectionParts[0]) {
'class' => Node::newClass(ReflectionClass::class, Node::className($this->attribute->reflectionParts[1])),
'property' => Node::newClass(ReflectionProperty::class, Node::className($this->attribute->reflectionParts[1]), Node::value($this->attribute->reflectionParts[2])),
};
return $compiler->compile(
$node->wrap()
->callMethod('getAttributes')
->key(Node::value($this->attribute->attributeIndex))
->callMethod('newInstance'),
);
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Library;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Native\ComplianceNode;
use CuyZ\Valinor\Compiler\Node;
use CuyZ\Valinor\Type\Type;
/** @internal */
final class TypeAcceptNode extends Node
{
public function __construct(
private ComplianceNode $node,
private Type $type,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->compile($this->type->compiledAccept($this->node));
}
}

View File

@@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
use function array_map;
use function implode;
/** @internal */
final class AnonymousClassNode extends Node
{
/** @var array<Node> */
private array $arguments = [];
/** @var array<interface-string> */
private array $interfaces = [];
/** @var array<PropertyDeclarationNode> */
private array $properties = [];
/** @var array<non-empty-string, MethodNode> */
private array $methods = [];
public function withArguments(Node ...$arguments): self
{
$self = clone $this;
$self->arguments = $arguments;
return $self;
}
/**
* @param interface-string ...$interfaces
*/
public function implements(string ...$interfaces): self
{
$self = clone $this;
$self->interfaces = $interfaces;
return $self;
}
public function withProperties(PropertyDeclarationNode ...$properties): self
{
$self = clone $this;
$self->properties = $properties;
return $self;
}
public function withMethods(MethodNode ...$methods): self
{
$self = clone $this;
foreach ($methods as $method) {
$self->methods[$method->name()] = $method;
}
return $self;
}
public function hasMethod(string $name): bool
{
return isset($this->methods[$name]);
}
public function compile(Compiler $compiler): Compiler
{
$arguments = implode(', ', array_map(
fn (Node $argument) => $compiler->sub()->compile($argument)->code(),
$this->arguments,
));
$compiler = $compiler->write("new class ($arguments)");
if ($this->interfaces !== []) {
$compiler = $compiler->write(
' implements ' . implode(', ', $this->interfaces),
);
}
$body = [
...array_map(
fn (PropertyDeclarationNode $property) => $compiler->sub()->indent()->compile($property)->code(),
$this->properties,
),
...array_map(
fn (MethodNode $method) => $compiler->sub()->indent()->compile($method)->code(),
$this->methods,
),
];
$compiler = $compiler->write(' {');
if ($body !== []) {
$compiler = $compiler->write(PHP_EOL . implode(PHP_EOL . PHP_EOL, $body) . PHP_EOL);
}
return $compiler->write('}');
}
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ArrayKeyAccessNode extends Node
{
public function __construct(
private Node $node,
private Node $key,
) {}
public function compile(Compiler $compiler): Compiler
{
$key = $compiler->sub()->compile($this->key)->code();
return $compiler
->compile($this->node)
->write('[' . $key . ']');
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
use function var_export;
/** @internal */
final class ArrayNode extends Node
{
public function __construct(
/** @var array<Node> */
private array $assignments
) {}
public function compile(Compiler $compiler): Compiler
{
if ($this->assignments === []) {
return $compiler->write('[]');
}
$sub = [];
foreach ($this->assignments as $key => $assignment) {
$sub[] = ' ' . var_export($key, true) . ' => ' . $compiler->sub()->compile($assignment)->code() . ",";
}
$sub = implode("\n", $sub);
return $compiler->write("[\n$sub\n]");
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class AssignNode extends Node
{
public function __construct(
private Node $node,
private Node $value,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->node)
->write(' = ')
->compile($this->value);
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
use function array_map;
use function implode;
/** @internal */
final class CallNode extends Node
{
public function __construct(
private Node $node,
/** @var array<Node> */
private array $arguments = [],
) {}
public function compile(Compiler $compiler): Compiler
{
$compiler = $compiler
->compile($this->node)
->write('(');
if ($this->arguments !== []) {
$arguments = array_map(
fn (Node $argument) => $compiler->sub()->compile($argument)->code(),
$this->arguments,
);
$compiler = $compiler->write(implode(', ', $arguments));
}
return $compiler->write(')');
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class CastNode extends Node
{
private function __construct(
private string $type,
private Node $node,
) {}
public static function toArray(Node $node): self
{
return new self('array', $node);
}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->write('(' . $this->type . ')')
->compile($this->node);
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ClassNameNode extends Node
{
public function __construct(
/** @var class-string */
private string $className,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write($this->className . '::class');
}
}

View File

@@ -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 ClassNode extends Node
{
public function __construct(
/** @var class-string */
private string $name,
) {}
/**
* @param non-empty-string $method
* @param array<Node> $arguments
*/
public function callStaticMethod(
string $method,
array $arguments = [],
): Node {
return new StaticMethodCallNode($this, $method, $arguments);
}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write($this->name);
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class CloneNode extends Node
{
public function __construct(
private Node $node,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->write('clone ')
->compile($this->node);
}
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
use function array_map;
use function implode;
/** @internal */
final class ClosureNode extends Node
{
/** @var list<string> */
private array $use = [];
/** @var array<Node> */
private array $nodes;
public function __construct(Node ...$nodes)
{
$this->nodes = $nodes;
}
/**
* @no-named-arguments
* @param non-empty-string ...$names
*/
public function uses(string ...$names): self
{
$self = clone $this;
$self->use = array_map(fn (string $name) => '$' . $name, $names);
return $self;
}
public function compile(Compiler $compiler): Compiler
{
$use = $this->use !== [] ? ' use (' . implode(', ', $this->use) . ')' : '';
$body = $compiler->sub()->indent()->compile(...$this->nodes)->code();
$code = <<<PHP
function ()$use {
$body
}
PHP;
return $compiler->write($code);
}
}

View File

@@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ComplianceNode extends Node
{
public function __construct(private Node $node) {}
/**
* @param non-empty-string $value
*/
public function access(string $value): self
{
return new self(new VariableAccessNode($this, $value));
}
/**
* @no-named-arguments
*/
public function and(Node ...$nodes): self
{
return new self(new LogicalAndNode($this, ...$nodes));
}
public function castToArray(): self
{
return new self(CastNode::toArray($this));
}
public function clone(): self
{
return new self(new CloneNode($this));
}
public function key(Node $key): self
{
return new self(new ArrayKeyAccessNode($this, $key));
}
public function assign(Node $value): self
{
return new self(new AssignNode($this, $value));
}
/**
* @param array<Node> $arguments
*/
public function call(array $arguments = []): self
{
return new self(new CallNode($this, $arguments));
}
/**
* @param non-empty-string $method
* @param array<Node> $arguments
*/
public function callMethod(string $method, array $arguments = []): self
{
return new self(new MethodCallNode($this, $method, $arguments));
}
public function different(Node $right): self
{
return new self(new DifferentNode($this, $right));
}
public function equals(Node $right): self
{
return new self(new EqualsNode($this, $right));
}
/**
* @no-named-arguments
*/
public function or(Node ...$nodes): self
{
return new self(new LogicalOrNode($this, ...$nodes));
}
/**
* @param class-string $className
*/
public function instanceOf(string $className): self
{
return new self(new InstanceOfNode($this, $className));
}
public function isLessThan(Node $right): self
{
return new self(new LessThanNode($this, $right));
}
public function isLessOrEqualsTo(Node $right): self
{
return new self(new LessOrEqualsToNode($this, $right));
}
public function isGreaterThan(Node $right): self
{
return new self(new GreaterThanNode($this, $right));
}
public function isGreaterOrEqualsTo(Node $right): self
{
return new self(new GreaterOrEqualsToNode($this, $right));
}
public function compile(Compiler $compiler): Compiler
{
return $compiler->compile($this->node);
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class DifferentNode extends Node
{
public function __construct(
private Node $left,
private Node $right,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write(' !== ')
->compile($this->right);
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class EqualsNode extends Node
{
public function __construct(
private Node $left,
private Node $right,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write(' === ')
->compile($this->right);
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ExpressionNode extends Node
{
public function __construct(private Node $node) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write($compiler->sub()->compile($this->node)->code() . ';');
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ForEachNode extends Node
{
public function __construct(
private Node $value,
/** @var non-empty-string */
private string $key,
/** @var non-empty-string */
private string $item,
private Node $body,
) {}
public function compile(Compiler $compiler): Compiler
{
$value = $compiler->sub()->compile($this->value)->code();
$body = $compiler->sub()->indent()->compile($this->body)->code();
return $compiler->write(
<<<PHP
foreach ($value as $$this->key => $$this->item) {
$body
}
PHP
);
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class FunctionCallNode extends Node
{
public function __construct(
/** @var non-empty-string */
private string $name,
/** @var array<Node> */
private array $arguments = [],
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile(new CallNode(new FunctionNameNode($this->name), $this->arguments));
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
use function in_array;
/** @internal */
final class FunctionNameNode extends Node
{
private const RESERVED_FUNCTIONS = [
'isset',
];
public function __construct(
/** @var non-empty-string */
private string $name
) {}
public function compile(Compiler $compiler): Compiler
{
$function = in_array($this->name, self::RESERVED_FUNCTIONS, true)
? $this->name
: '\\' . $this->name;
return $compiler->write($function);
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class GreaterOrEqualsToNode extends Node
{
public function __construct(
private Node $left,
private Node $right,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write(' >= ')
->compile($this->right);
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class GreaterThanNode extends Node
{
public function __construct(
private Node $left,
private Node $right,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write(' > ')
->compile($this->right);
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class IfNode extends Node
{
public function __construct(
private Node $condition,
private Node $body,
) {}
public function compile(Compiler $compiler): Compiler
{
$condition = $compiler->sub()->compile($this->condition)->code();
$body = $compiler->sub()->indent()->compile($this->body)->code();
return $compiler->write(
<<<PHP
if ($condition) {
$body
}
PHP,
);
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class InstanceOfNode extends Node
{
public function __construct(
private Node $node,
/** @var class-string */
private string $className,
) {}
public function compile(Compiler $compiler): Compiler
{
$className = $this->className;
return $compiler
->compile($this->node)
->write(' instanceof ')
->write($className);
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class LessOrEqualsToNode extends Node
{
public function __construct(
private Node $left,
private Node $right,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write(' <= ')
->compile($this->right);
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class LessThanNode extends Node
{
public function __construct(
private Node $left,
private Node $right,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write(' < ')
->compile($this->right);
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class LogicalAndNode extends Node
{
/** @var list<Node> */
private array $nodes;
/**
* @no-named-arguments
*/
public function __construct(Node ...$nodes)
{
$this->nodes = $nodes;
}
public function compile(Compiler $compiler): Compiler
{
$nodes = $this->nodes;
while ($node = array_shift($nodes)) {
$compiler = $compiler->compile($node);
if ($nodes !== []) {
$compiler = $compiler->write(' && ');
}
}
return $compiler;
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class LogicalOrNode extends Node
{
/** @var list<Node> */
private array $nodes;
/**
* @no-named-arguments
*/
public function __construct(Node ...$nodes)
{
$this->nodes = $nodes;
}
public function compile(Compiler $compiler): Compiler
{
$nodes = $this->nodes;
while ($node = array_shift($nodes)) {
$compiler = $compiler->compile($node);
if ($nodes !== []) {
$compiler = $compiler->write(' || ');
}
}
return $compiler;
}
}

View File

@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class MatchNode extends Node
{
/** @var list<array{condition: Node, body: Node}> */
private array $cases = [];
private Node $defaultCase;
public function __construct(private Node $value) {}
public function withCase(Node $condition, Node $body): self
{
$self = clone $this;
$self->cases[] = ['condition' => $condition, 'body' => $body];
return $self;
}
public function withDefaultCase(Node $defaultCase): self
{
$self = clone $this;
$self->defaultCase = $defaultCase;
return $self;
}
public function compile(Compiler $compiler): Compiler
{
$value = $compiler->sub()->compile($this->value)->code();
$body = [];
foreach ($this->cases as $case) {
$body[] = $compiler->sub()->indent()->compile($case['condition'])->code() .
' => ' .
$compiler->sub()->compile($case['body'])->code() .
',';
}
if (isset($this->defaultCase)) {
$body[] = $compiler->sub()->indent()->write('default')->code() .
' => ' .
$compiler->sub()->compile($this->defaultCase)->code() .
',';
}
$body = implode("\n", $body);
return $compiler->write(
<<<PHP
match ($value) {
$body
}
PHP,
);
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class MethodCallNode extends Node
{
public function __construct(
private Node $node,
/** @var non-empty-string */
private string $method,
/** @var array<Node> */
private array $arguments = [],
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->compile(
new CallNode(new VariableAccessNode($this->node, $this->method), $this->arguments)
);
}
}

View File

@@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
use function array_map;
/** @internal */
final class MethodNode extends Node
{
/** @var 'public'|'private' */
private string $visibility = 'private';
/** @var non-empty-string */
private string $name;
private string $returnType;
/** @var array<ParameterDeclarationNode> */
private array $parameters = [];
/** @var array<Node> */
private array $nodes = [];
/**
* @param non-empty-string $name
*/
public function __construct(string $name)
{
$this->name = $name;
}
public static function constructor(): self
{
return new self('__construct');
}
/**
* @return non-empty-string
*/
public function name(): string
{
return $this->name;
}
public function witParameters(ParameterDeclarationNode ...$parameters): self
{
$self = clone $this;
$self->parameters = $parameters;
return $self;
}
/**
* @param 'public'|'private' $visibility
*/
public function withVisibility(string $visibility): self
{
$self = clone $this;
$self->visibility = $visibility;
return $self;
}
/**
* @param non-empty-string $type
*/
public function withReturnType(string $type): self
{
$self = clone $this;
$self->returnType = $type;
return $self;
}
public function withBody(Node ...$nodes): self
{
$self = clone $this;
$self->nodes = $nodes;
return $self;
}
public function compile(Compiler $compiler): Compiler
{
$parameters = implode(', ', array_map(
fn (ParameterDeclarationNode $parameter) => $compiler->sub()->compile($parameter)->code(),
$this->parameters,
));
$compiler = $compiler->write("$this->visibility function $this->name($parameters)");
if ($this->name !== '__construct') {
$compiler = $compiler->write(': ' . ($this->returnType ?? 'void'));
}
if ($this->nodes === []) {
return $compiler->write(' {}');
}
$body = $compiler->sub()->indent()->compile(...$this->nodes)->code();
return $compiler->write(PHP_EOL . '{' . PHP_EOL . $body . PHP_EOL . '}');
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class NegateNode extends Node
{
public function __construct(private Node $node) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write('! ')->compile($this->node);
}
}

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
use function array_map;
/** @internal */
final class NewClassNode extends Node
{
/** @var class-string */
private string $className;
/** @var array<Node> */
private array $arguments;
/**
* @param class-string $className
*/
public function __construct(string $className, Node ...$arguments)
{
$this->className = $className;
$this->arguments = $arguments;
}
public function compile(Compiler $compiler): Compiler
{
$arguments = array_map(
fn (Node $argument) => $compiler->sub()->compile($argument)->code(),
$this->arguments,
);
$arguments = implode(', ', $arguments);
return $compiler->write("new {$this->className}($arguments)");
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ParameterDeclarationNode extends Node
{
public function __construct(
/** @var non-empty-string */
private string $name,
private string $type,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write($this->type . ' $' . $this->name);
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class PhpFileNode extends Node
{
/** @var array<Node> */
private array $nodes;
public function __construct(Node ...$nodes)
{
$this->nodes = $nodes;
}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->write("<?php\n\ndeclare(strict_types=1);\n\n")
->compile(...$this->nodes);
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class PropertyDeclarationNode extends Node
{
public function __construct(
/** @var non-empty-string */
private string $name,
private string $type,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write('private ' . $this->type . ' $' . $this->name . ';');
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class PropertyNode extends Node
{
public function __construct(private string $name) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write('$this->' . $this->name);
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ReturnNode extends Node
{
public function __construct(private ?Node $node = null) {}
public function compile(Compiler $compiler): Compiler
{
$code = $this->node ? ' ' . $compiler->sub()->compile($this->node)->code() : '';
return $compiler->write("return$code;");
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ShortClosureNode extends Node
{
private Node $returnNode;
/** @var array<ParameterDeclarationNode> */
private array $parameters = [];
public function __construct(Node $returnNode)
{
$this->returnNode = $returnNode;
}
public function witParameters(ParameterDeclarationNode ...$parameters): self
{
$self = clone $this;
$self->parameters = $parameters;
return $self;
}
public function compile(Compiler $compiler): Compiler
{
$parameters = implode(', ', array_map(
fn (ParameterDeclarationNode $parameter) => $compiler->sub()->compile($parameter)->code(),
$this->parameters,
));
$return = $compiler->sub()->compile($this->returnNode)->code();
return $compiler->write(
<<<PHP
fn ($parameters) => $return
PHP,
);
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class StaticAccessNode extends Node
{
public function __construct(
private Node $left,
/** @var non-empty-string */
private string $name,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write("::$this->name");
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class StaticMethodCallNode extends Node
{
public function __construct(
private Node $node,
/** @var non-empty-string */
private string $method,
/** @var array<Node> */
private array $arguments = [],
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->compile(
new CallNode(new StaticAccessNode($this->node, $this->method), $this->arguments)
);
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class TernaryNode extends Node
{
public function __construct(
private Node $condition,
private Node $ifTrue,
private Node $ifFalse,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->condition)
->write(' ? ')
->compile($this->ifTrue)
->write(' : ')
->compile($this->ifFalse);
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ThrowNode extends Node
{
public function __construct(
private Node $node,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->write('throw ')
->compile($this->node);
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
use function is_array;
use function var_export;
/** @internal */
final class ValueNode extends Node
{
public function __construct(
/** @var array<mixed>|bool|float|int|string|null */
private array|bool|float|int|string|null $value,
) {}
public function compile(Compiler $compiler): Compiler
{
return $this->compileValue($this->value, $compiler);
}
private function compileValue(mixed $value, Compiler $compiler): Compiler
{
if (is_array($value)) {
$compiler = $compiler->write('[');
$i = 0;
$numItems = count($value);
foreach ($value as $key => $item) {
$compiler = $compiler->write(var_export($key, true) . ' => ');
$compiler = $this->compileValue($item, $compiler);
if (++$i !== $numItems) {
$compiler = $compiler->write(', ');
}
}
$compiler = $compiler->write(']');
} else {
$compiler = $compiler->write(var_export($value, true));
}
return $compiler;
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class VariableAccessNode extends Node
{
public function __construct(
private Node $node,
/** @var non-empty-string */
private string $value
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->node)
->write('->' . $this->value);
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class VariableNode extends Node
{
public function __construct(private string $name) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write('$' . $this->name);
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class WrapNode extends Node
{
public function __construct(private Node $node) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->write('(')
->compile($this->node)
->write(')');
}
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler\Native;
use CuyZ\Valinor\Compiler\Compiler;
use CuyZ\Valinor\Compiler\Node;
/** @internal */
final class YieldNode extends Node
{
public function __construct(
private Node $key,
private Node $value,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->write('yield ')
->compile($this->key)
->write(' => ')
->compile($this->value);
}
}

View File

@@ -0,0 +1,212 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Compiler;
use CuyZ\Valinor\Compiler\Native\AnonymousClassNode;
use CuyZ\Valinor\Compiler\Native\ArrayNode;
use CuyZ\Valinor\Compiler\Native\ClassNameNode;
use CuyZ\Valinor\Compiler\Native\ClassNode;
use CuyZ\Valinor\Compiler\Native\ClosureNode;
use CuyZ\Valinor\Compiler\Native\ComplianceNode;
use CuyZ\Valinor\Compiler\Native\ExpressionNode;
use CuyZ\Valinor\Compiler\Native\ForEachNode;
use CuyZ\Valinor\Compiler\Native\FunctionCallNode;
use CuyZ\Valinor\Compiler\Native\IfNode;
use CuyZ\Valinor\Compiler\Native\LogicalAndNode;
use CuyZ\Valinor\Compiler\Native\LogicalOrNode;
use CuyZ\Valinor\Compiler\Native\MatchNode;
use CuyZ\Valinor\Compiler\Native\MethodNode;
use CuyZ\Valinor\Compiler\Native\NegateNode;
use CuyZ\Valinor\Compiler\Native\NewClassNode;
use CuyZ\Valinor\Compiler\Native\ParameterDeclarationNode;
use CuyZ\Valinor\Compiler\Native\PropertyDeclarationNode;
use CuyZ\Valinor\Compiler\Native\PropertyNode;
use CuyZ\Valinor\Compiler\Native\ReturnNode;
use CuyZ\Valinor\Compiler\Native\ShortClosureNode;
use CuyZ\Valinor\Compiler\Native\TernaryNode;
use CuyZ\Valinor\Compiler\Native\ThrowNode;
use CuyZ\Valinor\Compiler\Native\ValueNode;
use CuyZ\Valinor\Compiler\Native\VariableNode;
use CuyZ\Valinor\Compiler\Native\WrapNode;
use CuyZ\Valinor\Compiler\Native\YieldNode;
/** @internal */
abstract class Node
{
abstract public function compile(Compiler $compiler): Compiler;
public function asExpression(): ExpressionNode
{
return new ExpressionNode($this);
}
public function wrap(): ComplianceNode
{
return new ComplianceNode(new WrapNode($this));
}
/**
* @param array<Node> $assignments
*/
public static function array(array $assignments = []): ArrayNode
{
return new ArrayNode($assignments);
}
public static function anonymousClass(): AnonymousClassNode
{
return new AnonymousClassNode();
}
/**
* @param class-string $name
*/
public static function class(string $name): ClassNode
{
return new ClassNode($name);
}
/**
* @param class-string $className
*/
public static function className(string $className): ClassNameNode
{
return new ClassNameNode($className);
}
public static function closure(Node ...$nodes): ClosureNode
{
return new ClosureNode(...$nodes);
}
/**
* @param non-empty-string $key
* @param non-empty-string $item
*/
public static function forEach(Node $value, string $key, string $item, Node $body): ForEachNode
{
return new ForEachNode($value, $key, $item, $body);
}
/**
* @param non-empty-string $name
* @param array<Node> $arguments
*/
public static function functionCall(string $name, array $arguments = []): ComplianceNode
{
return new ComplianceNode(new FunctionCallNode($name, $arguments));
}
public static function if(Node $condition, Node $body): IfNode
{
return new IfNode($condition, $body);
}
/**
* @no-named-arguments
*/
public static function logicalAnd(Node ...$nodes): ComplianceNode
{
return new ComplianceNode(new LogicalAndNode(...$nodes));
}
/**
* @no-named-arguments
*/
public static function logicalOr(Node ...$nodes): ComplianceNode
{
return new ComplianceNode(new LogicalOrNode(...$nodes));
}
public static function match(Node $value): MatchNode
{
return new MatchNode($value);
}
/**
* @param non-empty-string $name
*/
public static function method(string $name): MethodNode
{
return new MethodNode($name);
}
public static function negate(Node $node): NegateNode
{
return new NegateNode($node);
}
/**
* @param class-string $className
*/
public static function newClass(string $className, Node ...$arguments): NewClassNode
{
return new NewClassNode($className, ...$arguments);
}
/**
* @param non-empty-string $name
*/
public static function parameterDeclaration(string $name, string $type): ParameterDeclarationNode
{
return new ParameterDeclarationNode($name, $type);
}
public static function property(string $name): ComplianceNode
{
return new ComplianceNode(new PropertyNode($name));
}
/**
* @param non-empty-string $name
*/
public static function propertyDeclaration(string $name, string $type): PropertyDeclarationNode
{
return new PropertyDeclarationNode($name, $type);
}
public static function return(Node $node): ReturnNode
{
return new ReturnNode($node);
}
public static function shortClosure(Node $return): ShortClosureNode
{
return new ShortClosureNode($return);
}
public static function ternary(Node $condition, Node $ifTrue, Node $ifFalse): TernaryNode
{
return new TernaryNode($condition, $ifTrue, $ifFalse);
}
public static function this(): ComplianceNode
{
return self::variable('this');
}
public static function throw(Node $node): ThrowNode
{
return new ThrowNode($node);
}
/**
* @param array<mixed>|bool|float|int|string|null $value
*/
public static function value(array|bool|float|int|string|null $value): ComplianceNode
{
return new ComplianceNode(new ValueNode($value));
}
public static function variable(string $name): ComplianceNode
{
return new ComplianceNode(new VariableNode($name));
}
public static function yield(Node $key, Node $value): YieldNode
{
return new YieldNode($key, $value);
}
}