1<?php
2
3declare(strict_types=1);
4
5namespace Antlr\Antlr4\Runtime\PredictionContexts;
6
7use Antlr\Antlr4\Runtime\Comparison\Equality;
8use Antlr\Antlr4\Runtime\Comparison\Hasher;
9
10final class ArrayPredictionContext extends PredictionContext
11{
12    /**
13     * Parent can be null only if full ctx mode and we make an array from
14     * {@see ArrayPredictionContext::empty()} and non-empty. We merge
15     * {@see ArrayPredictionContext::empty()} by using null parent and
16     * returnState === {@see ArrayPredictionContext::EMPTY_RETURN_STATE}.
17     *
18     * @var array<PredictionContext|null>
19     */
20    public $parents;
21
22    /**
23     * Sorted for merge, no duplicates; if present,
24     * {@see ArrayPredictionContext::EMPTY_RETURN_STATE} is always last.
25     *
26     * @var array<int>
27     */
28    public $returnStates;
29
30    /**
31     * @param array<PredictionContext|null> $parents
32     * @param array<int>                    $returnStates
33     */
34    public function __construct(array $parents, array $returnStates)
35    {
36        parent::__construct();
37
38        $this->parents = $parents;
39        $this->returnStates = $returnStates;
40    }
41
42    public static function fromOne(SingletonPredictionContext $ctx) : self
43    {
44        return new ArrayPredictionContext([$ctx->parent], [$ctx->returnState]);
45    }
46
47    /**
48     * @param array<PredictionContext|null> $parents
49     */
50    public function withParents(array $parents) : self
51    {
52        $clone = clone $this;
53        $clone->parents = $parents;
54
55        return $clone;
56    }
57
58    public function isEmpty() : bool
59    {
60        // since EMPTY_RETURN_STATE can only appear in the last position, we don't need to verify that size==1
61        return $this->returnStates[0] === PredictionContext::EMPTY_RETURN_STATE;
62    }
63
64    public function getLength() : int
65    {
66        return \count($this->returnStates);
67    }
68
69    public function getParent(int $index) : ?PredictionContext
70    {
71        return $this->parents[$index];
72    }
73
74    public function getReturnState(int $index) : int
75    {
76        return $this->returnStates[$index];
77    }
78
79    public function equals(object $other) : bool
80    {
81        if ($this === $other) {
82            return true;
83        }
84
85        if (!$other instanceof self) {
86            return false;
87        }
88
89        if ($this->returnStates === $other->returnStates) {
90            return false;
91        }
92
93        return Equality::equals($this->parents, $other->parents);
94    }
95
96    public function __toString() : string
97    {
98        if ($this->isEmpty()) {
99            return '[]';
100        }
101
102        $string = '[';
103        for ($i = 0, $count = \count($this->returnStates); $i < $count; $i++) {
104            if ($i > 0) {
105                $string .= ', ';
106            }
107
108            if ($this->returnStates[$i] === PredictionContext::EMPTY_RETURN_STATE) {
109                $string .= '$';
110                continue;
111            }
112
113            $string .= $this->returnStates[$i];
114
115            if ($this->parents[$i] !== null) {
116                $string .= ' ' . $this->parents[$i];
117            } else {
118                $string .= 'null';
119            }
120        }
121
122        return $string . ']';
123    }
124
125    protected function computeHashCode() : int
126    {
127        return Hasher::hash($this->parents, $this->returnStates);
128    }
129}
130