packages/deriver/src/Deriver/Evaluator/Memory/Elements.php
1<?php
2
3declare(strict_types=1);
4
5namespace Deriver\Deriver\Evaluator\Memory;
6
7use Deriver\Deriver\Evaluator\Evaluator;
8use Deriver\Deriver\Evaluator\Term;
9
10/**
11 * Reconstructs nested updates without discarding unaffected array entries.
12 * @visibility parent
13 */
14final class Elements
15{
16 /**
17 * Reuses ordinary array projection and update rules.
18 */
19 public function __construct(private readonly Evaluator $engine)
20 {
21 }
22
23 /**
24
25 * @param non-empty-list<Term> $path
26
27 */
28 public function write(Term $array, array $path, Term $value): Term
29 {
30 $key = array_shift($path);
31 if ($path !== []) {
32 $child = $this->engine->operation('array-read', '', [$array, $key]);
33 $value = $this->write($child, $path, $value);
34 }
35 return $this->engine->operation('array-set', '', [$array, $key, $value]);
36 }
37 /**
38 * Applies the leaf mutation and rebuilds only its containing arrays.
39 * @param non-empty-list<Term> $path Nested element keys
40 */
41 public function mutate(Term $array, array $path, \Deriver\Deriver\Lowerer\Graph\Instruction $write, Term $right): Term
42 {
43 $key = array_shift($path);
44 $before = $this->engine->element($array, $key);
45 if ($path !== []) {
46 return $this->engine->operation('array-set', '', [$array, $key, $this->mutate($before, $path, $write, $right)]);
47 }
48 if ($write->operation === 'unset') {
49 return (new \Deriver\Deriver\Evaluator\Enumeration\Choices())->apply('array-unset', [$array, $key], static function (array $values): Term {
50 [$array, $key] = $values;
51 if ($array->kind !== \Deriver\Deriver\Evaluator\TermKind::Array || $key->kind !== \Deriver\Deriver\Evaluator\TermKind::Constant) {
52 return new Term(\Deriver\Deriver\Evaluator\TermKind::ArrayUnset, operands: $values, attributes: ['reason' => 'UNRESOLVED_ELEMENT']);
53 }
54 $entries = $array->operands;
55 unset($entries[(string) $key->literal]);
56 return new Term(\Deriver\Deriver\Evaluator\TermKind::Array, operands: $entries, attributes: $array->attributes);
57 }, $this->engine->context->budget->partitions);
58 }
59 $value = match ($write->operation) {
60 'increment' => Mutations::increment($this->engine, $write, $before),
61 'compound' => $this->engine->operation('binary', $write->name, [$before, $right]),
62 default => $right,
63 };
64 return $this->engine->operation('array-set', '', [$array, $key, $value]);
65 }
66
67}
68