xref: /plugin/combo/vendor/antlr/antlr4-php-runtime/src/IntervalSet.php (revision 37748cd8654635afbeca80942126742f0f4cc346)
1*37748cd8SNickeau<?php
2*37748cd8SNickeau
3*37748cd8SNickeaudeclare(strict_types=1);
4*37748cd8SNickeau
5*37748cd8SNickeaunamespace Antlr\Antlr4\Runtime;
6*37748cd8SNickeau
7*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Comparison\Equality;
8*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Comparison\Equatable;
9*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Utils\StringUtils;
10*37748cd8SNickeau
11*37748cd8SNickeau/**
12*37748cd8SNickeau * This class implements the {@see IntSet} backed by a sorted array of
13*37748cd8SNickeau * non-overlapping intervals. It is particularly efficient for representing
14*37748cd8SNickeau * large collections of numbers, where the majority of elements appear as part
15*37748cd8SNickeau * of a sequential range of numbers that are all part of the set. For example,
16*37748cd8SNickeau * the set { 1, 2, 3, 4, 7, 8 } may be represented as { [1, 4], [7, 8] }.
17*37748cd8SNickeau *
18*37748cd8SNickeau * This class is able to represent sets containing any combination of values in
19*37748cd8SNickeau * the range {@see Integer::MIN_VALUE} to {@see Integer::MAX_VALUE}
20*37748cd8SNickeau * (inclusive).
21*37748cd8SNickeau */
22*37748cd8SNickeaufinal class IntervalSet implements Equatable
23*37748cd8SNickeau{
24*37748cd8SNickeau    /** @var array<Interval> */
25*37748cd8SNickeau    protected $intervals = [];
26*37748cd8SNickeau
27*37748cd8SNickeau    /** @var bool */
28*37748cd8SNickeau    protected $readOnly = false;
29*37748cd8SNickeau
30*37748cd8SNickeau    /**
31*37748cd8SNickeau     * Create a set with a single element, el.
32*37748cd8SNickeau     */
33*37748cd8SNickeau    public static function fromInt(int $number) : self
34*37748cd8SNickeau    {
35*37748cd8SNickeau        $set = new self();
36*37748cd8SNickeau
37*37748cd8SNickeau        $set->addOne($number);
38*37748cd8SNickeau
39*37748cd8SNickeau        return $set;
40*37748cd8SNickeau    }
41*37748cd8SNickeau
42*37748cd8SNickeau    /**
43*37748cd8SNickeau     * Create a set with all ints within range [start..end] (inclusive).
44*37748cd8SNickeau     */
45*37748cd8SNickeau    public static function fromRange(int $start, int $end) : self
46*37748cd8SNickeau    {
47*37748cd8SNickeau        $set = new self();
48*37748cd8SNickeau
49*37748cd8SNickeau        $set->addRange($start, $end);
50*37748cd8SNickeau
51*37748cd8SNickeau        return $set;
52*37748cd8SNickeau    }
53*37748cd8SNickeau
54*37748cd8SNickeau    public function complement(IntervalSet $set) : ?self
55*37748cd8SNickeau    {
56*37748cd8SNickeau        if ($set->isNull()) {
57*37748cd8SNickeau            return null;
58*37748cd8SNickeau        }
59*37748cd8SNickeau
60*37748cd8SNickeau        return $set->subtract($this);
61*37748cd8SNickeau    }
62*37748cd8SNickeau
63*37748cd8SNickeau    public function subtract(IntervalSet $set) : self
64*37748cd8SNickeau    {
65*37748cd8SNickeau        if ($set->isNull()) {
66*37748cd8SNickeau            return $this;
67*37748cd8SNickeau        }
68*37748cd8SNickeau
69*37748cd8SNickeau        return self::subtractSets($this, $set);
70*37748cd8SNickeau    }
71*37748cd8SNickeau
72*37748cd8SNickeau    public function orSet(IntervalSet $other) : self
73*37748cd8SNickeau    {
74*37748cd8SNickeau        $result = new self();
75*37748cd8SNickeau
76*37748cd8SNickeau        $result->addSet($this);
77*37748cd8SNickeau        $result->addSet($other);
78*37748cd8SNickeau
79*37748cd8SNickeau        return $result;
80*37748cd8SNickeau    }
81*37748cd8SNickeau
82*37748cd8SNickeau    /**
83*37748cd8SNickeau     * Compute the set difference between two interval sets. The specific
84*37748cd8SNickeau     * operation is `left - right`. If either of the input sets is `null`,
85*37748cd8SNickeau     * it is treated as though it was an empty set.
86*37748cd8SNickeau     */
87*37748cd8SNickeau    public static function subtractSets(IntervalSet $left, IntervalSet $right) : self
88*37748cd8SNickeau    {
89*37748cd8SNickeau        if ($left->isNull()) {
90*37748cd8SNickeau            return new self();
91*37748cd8SNickeau        }
92*37748cd8SNickeau
93*37748cd8SNickeau        if ($right->isNull()) {
94*37748cd8SNickeau            // right set has no elements; just return the copy of the current set
95*37748cd8SNickeau            return $left;
96*37748cd8SNickeau        }
97*37748cd8SNickeau
98*37748cd8SNickeau        $result = $left;
99*37748cd8SNickeau        $resultI = 0;
100*37748cd8SNickeau        $rightI = 0;
101*37748cd8SNickeau        while ($resultI < \count($result->intervals) && $rightI < \count($right->intervals)) {
102*37748cd8SNickeau            $resultInterval = $result->intervals[$resultI];
103*37748cd8SNickeau            $rightInterval = $right->intervals[$rightI];
104*37748cd8SNickeau
105*37748cd8SNickeau            // operation: (resultInterval - rightInterval) and update indexes
106*37748cd8SNickeau
107*37748cd8SNickeau            if ($rightInterval->stop < $resultInterval->start) {
108*37748cd8SNickeau                $rightI++;
109*37748cd8SNickeau
110*37748cd8SNickeau                continue;
111*37748cd8SNickeau            }
112*37748cd8SNickeau
113*37748cd8SNickeau            if ($rightInterval->start > $resultInterval->stop) {
114*37748cd8SNickeau                $resultI++;
115*37748cd8SNickeau
116*37748cd8SNickeau                continue;
117*37748cd8SNickeau            }
118*37748cd8SNickeau
119*37748cd8SNickeau            $beforeCurrent = null;
120*37748cd8SNickeau            $afterCurrent = null;
121*37748cd8SNickeau            if ($rightInterval->start > $resultInterval->start) {
122*37748cd8SNickeau                $beforeCurrent = new Interval($resultInterval->start, $rightInterval->start - 1);
123*37748cd8SNickeau            }
124*37748cd8SNickeau
125*37748cd8SNickeau            if ($rightInterval->stop < $resultInterval->stop) {
126*37748cd8SNickeau                $afterCurrent = new Interval($rightInterval->stop + 1, $resultInterval->stop);
127*37748cd8SNickeau            }
128*37748cd8SNickeau
129*37748cd8SNickeau            if ($beforeCurrent !== null) {
130*37748cd8SNickeau                if ($afterCurrent !== null) {
131*37748cd8SNickeau                    // split the current interval into two
132*37748cd8SNickeau                    $result->intervals[$resultI] = $beforeCurrent;
133*37748cd8SNickeau                    $result->intervals[$resultI + 1] = $afterCurrent;
134*37748cd8SNickeau                    $resultI++;
135*37748cd8SNickeau                    $rightI++;
136*37748cd8SNickeau
137*37748cd8SNickeau                    continue;
138*37748cd8SNickeau                }
139*37748cd8SNickeau
140*37748cd8SNickeau                // replace the current interval
141*37748cd8SNickeau                $result->intervals[$resultI] = $beforeCurrent;
142*37748cd8SNickeau                $resultI++;
143*37748cd8SNickeau                continue;
144*37748cd8SNickeau            }
145*37748cd8SNickeau
146*37748cd8SNickeau            if ($afterCurrent !== null) {
147*37748cd8SNickeau                // replace the current interval
148*37748cd8SNickeau                $result->intervals[$resultI] = $afterCurrent;
149*37748cd8SNickeau                $rightI++;
150*37748cd8SNickeau
151*37748cd8SNickeau                continue;
152*37748cd8SNickeau            }
153*37748cd8SNickeau
154*37748cd8SNickeau            // remove the current interval (thus no need to increment resultI)
155*37748cd8SNickeau            \array_splice($result->intervals, $resultI, 1);
156*37748cd8SNickeau
157*37748cd8SNickeau            continue;
158*37748cd8SNickeau        }
159*37748cd8SNickeau
160*37748cd8SNickeau        // If rightI reached right.intervals.size(), no more intervals to subtract from result.
161*37748cd8SNickeau        // If resultI reached result.intervals.size(), we would be subtracting from an empty set.
162*37748cd8SNickeau        // Either way, we are done.
163*37748cd8SNickeau        return $result;
164*37748cd8SNickeau    }
165*37748cd8SNickeau
166*37748cd8SNickeau    public function isReadOnly() : bool
167*37748cd8SNickeau    {
168*37748cd8SNickeau        return $this->readOnly;
169*37748cd8SNickeau    }
170*37748cd8SNickeau
171*37748cd8SNickeau    public function setReadOnly(bool $readOnly) : void
172*37748cd8SNickeau    {
173*37748cd8SNickeau        $this->readOnly = $readOnly;
174*37748cd8SNickeau    }
175*37748cd8SNickeau
176*37748cd8SNickeau    public function first() : int
177*37748cd8SNickeau    {
178*37748cd8SNickeau        if ($this->intervals === null || \count($this->intervals) === 0) {
179*37748cd8SNickeau            return Token::INVALID_TYPE;
180*37748cd8SNickeau        }
181*37748cd8SNickeau
182*37748cd8SNickeau        return $this->intervals[0]->start;
183*37748cd8SNickeau    }
184*37748cd8SNickeau
185*37748cd8SNickeau    public function addOne(int $value) : void
186*37748cd8SNickeau    {
187*37748cd8SNickeau        $this->addInterval(new Interval($value, $value));
188*37748cd8SNickeau    }
189*37748cd8SNickeau
190*37748cd8SNickeau    public function addRange(int $left, int $right) : void
191*37748cd8SNickeau    {
192*37748cd8SNickeau        $this->addInterval(new Interval($left, $right));
193*37748cd8SNickeau    }
194*37748cd8SNickeau
195*37748cd8SNickeau    protected function addInterval(Interval $addition) : void
196*37748cd8SNickeau    {
197*37748cd8SNickeau        if ($this->readOnly) {
198*37748cd8SNickeau            throw new \InvalidArgumentException('Can\'t alter readonly IntervalSet.');
199*37748cd8SNickeau        }
200*37748cd8SNickeau
201*37748cd8SNickeau        if ($addition->stop < $addition->start) {
202*37748cd8SNickeau            return;
203*37748cd8SNickeau        }
204*37748cd8SNickeau
205*37748cd8SNickeau        // find position in list
206*37748cd8SNickeau        // Use iterators as we modify list in place
207*37748cd8SNickeau        for ($i = 0, $count = \count($this->intervals); $i < $count; $i++) {
208*37748cd8SNickeau            /** @var Interval $resilt */
209*37748cd8SNickeau            $resilt = $this->intervals[$i];
210*37748cd8SNickeau
211*37748cd8SNickeau            if ($addition->equals($resilt)) {
212*37748cd8SNickeau                return;
213*37748cd8SNickeau            }
214*37748cd8SNickeau
215*37748cd8SNickeau            if ($addition->adjacent($resilt) || !$addition->disjoint($resilt)) {
216*37748cd8SNickeau                // next to each other, make a single larger interval
217*37748cd8SNickeau                $bigger = $addition->union($resilt);
218*37748cd8SNickeau                $this->intervals[$i] = $bigger;
219*37748cd8SNickeau
220*37748cd8SNickeau                // make sure we didn't just create an interval that
221*37748cd8SNickeau                // should be merged with next interval in list
222*37748cd8SNickeau                $i++;
223*37748cd8SNickeau                while ($i < \count($this->intervals)) {
224*37748cd8SNickeau                    $next = $this->intervals[$i];
225*37748cd8SNickeau
226*37748cd8SNickeau                    if (!$bigger->adjacent($next) && $bigger->disjoint($next)) {
227*37748cd8SNickeau                        break;
228*37748cd8SNickeau                    }
229*37748cd8SNickeau
230*37748cd8SNickeau                    // if we bump up against or overlap next, merge
231*37748cd8SNickeau                    \array_splice($this->intervals, $i, 1); // remove this one
232*37748cd8SNickeau
233*37748cd8SNickeau                    $i--; // move backwards to what we just set
234*37748cd8SNickeau
235*37748cd8SNickeau                    $this->intervals[$i] = $bigger->union($next); // set to 3 merged ones
236*37748cd8SNickeau
237*37748cd8SNickeau                    $i++; // first call to next after previous duplicates the result
238*37748cd8SNickeau                }
239*37748cd8SNickeau
240*37748cd8SNickeau                return;
241*37748cd8SNickeau            }
242*37748cd8SNickeau
243*37748cd8SNickeau            if ($addition->startsBeforeDisjoint($resilt)) {
244*37748cd8SNickeau                // insert before r
245*37748cd8SNickeau                \array_splice($this->intervals, $i, 0, [$addition]);
246*37748cd8SNickeau
247*37748cd8SNickeau                return;
248*37748cd8SNickeau            }
249*37748cd8SNickeau
250*37748cd8SNickeau            // if disjoint and after r, a future iteration will handle it
251*37748cd8SNickeau        }
252*37748cd8SNickeau
253*37748cd8SNickeau        // ok, must be after last interval (and disjoint from last interval) just add it
254*37748cd8SNickeau        $this->intervals[] = $addition;
255*37748cd8SNickeau    }
256*37748cd8SNickeau
257*37748cd8SNickeau    public function addSet(IntervalSet $other) : self
258*37748cd8SNickeau    {
259*37748cd8SNickeau        if ($other->intervals !== null) {
260*37748cd8SNickeau            foreach ($other->intervals as $i) {
261*37748cd8SNickeau                $this->addInterval(new Interval($i->start, $i->stop));
262*37748cd8SNickeau            }
263*37748cd8SNickeau        }
264*37748cd8SNickeau
265*37748cd8SNickeau        return $this;
266*37748cd8SNickeau    }
267*37748cd8SNickeau
268*37748cd8SNickeau    public function contains(int $item) : bool
269*37748cd8SNickeau    {
270*37748cd8SNickeau        $count = \count($this->intervals);
271*37748cd8SNickeau        $left = 0;
272*37748cd8SNickeau        $right = $count - 1;
273*37748cd8SNickeau        // Binary search for the element in the (sorted, disjoint) array of intervals.
274*37748cd8SNickeau
275*37748cd8SNickeau        while ($left <= $right) {
276*37748cd8SNickeau            $m = \intval($left + $right, 2);
277*37748cd8SNickeau
278*37748cd8SNickeau            $interval = $this->intervals[$m];
279*37748cd8SNickeau            $start = $interval->start;
280*37748cd8SNickeau            $stop = $interval->stop;
281*37748cd8SNickeau
282*37748cd8SNickeau            if ($stop < $item) {
283*37748cd8SNickeau                $left = $m + 1;
284*37748cd8SNickeau            } elseif ($start > $item) {
285*37748cd8SNickeau                $right = $m - 1;
286*37748cd8SNickeau            } else { // item >= start && item <= stop
287*37748cd8SNickeau                return true;
288*37748cd8SNickeau            }
289*37748cd8SNickeau        }
290*37748cd8SNickeau
291*37748cd8SNickeau        return false;
292*37748cd8SNickeau    }
293*37748cd8SNickeau
294*37748cd8SNickeau    public function length() : int
295*37748cd8SNickeau    {
296*37748cd8SNickeau        $length = 0;
297*37748cd8SNickeau
298*37748cd8SNickeau        foreach ($this->intervals as $i) {
299*37748cd8SNickeau            $length += $i->getLength();
300*37748cd8SNickeau        }
301*37748cd8SNickeau
302*37748cd8SNickeau        return $length;
303*37748cd8SNickeau    }
304*37748cd8SNickeau
305*37748cd8SNickeau    public function removeOne(int $v) : void
306*37748cd8SNickeau    {
307*37748cd8SNickeau        foreach ($this->intervals as $k => $i) {
308*37748cd8SNickeau            // intervals is ordered
309*37748cd8SNickeau            if ($v < $i->start) {
310*37748cd8SNickeau                return;
311*37748cd8SNickeau            }
312*37748cd8SNickeau
313*37748cd8SNickeau            // check for single value range
314*37748cd8SNickeau            if ($v === $i->start && $v === $i->stop) {
315*37748cd8SNickeau                \array_splice($this->intervals, $k, 1);
316*37748cd8SNickeau
317*37748cd8SNickeau                return;
318*37748cd8SNickeau            }
319*37748cd8SNickeau
320*37748cd8SNickeau            // check for lower boundary
321*37748cd8SNickeau            if ($v === $i->start) {
322*37748cd8SNickeau                $this->intervals[$k] = new Interval($i->start + 1, $i->stop);
323*37748cd8SNickeau
324*37748cd8SNickeau                return;
325*37748cd8SNickeau            }
326*37748cd8SNickeau
327*37748cd8SNickeau            // check for upper boundary
328*37748cd8SNickeau            if ($v === $i->stop - 1) {
329*37748cd8SNickeau                $this->intervals[$k] = new Interval($i->start, $i->stop - 1);
330*37748cd8SNickeau
331*37748cd8SNickeau                return;
332*37748cd8SNickeau            }
333*37748cd8SNickeau
334*37748cd8SNickeau            // split existing range
335*37748cd8SNickeau            if ($v < $i->stop - 1) {
336*37748cd8SNickeau                $x = new Interval($i->start, $v);
337*37748cd8SNickeau
338*37748cd8SNickeau                $i->start = $v + 1;
339*37748cd8SNickeau
340*37748cd8SNickeau                \array_splice($this->intervals, $k, 0, [$x]);
341*37748cd8SNickeau
342*37748cd8SNickeau                return;
343*37748cd8SNickeau            }
344*37748cd8SNickeau        }
345*37748cd8SNickeau    }
346*37748cd8SNickeau
347*37748cd8SNickeau    public function isNull() : bool
348*37748cd8SNickeau    {
349*37748cd8SNickeau        return \count($this->intervals) === 0;
350*37748cd8SNickeau    }
351*37748cd8SNickeau
352*37748cd8SNickeau    /**
353*37748cd8SNickeau     * Returns the maximum value contained in the set if not isNull().
354*37748cd8SNickeau     *
355*37748cd8SNickeau     * @return int The maximum value contained in the set.
356*37748cd8SNickeau     *
357*37748cd8SNickeau     * @throws \RuntimeException If set is empty.
358*37748cd8SNickeau     */
359*37748cd8SNickeau    public function getMaxElement() : int
360*37748cd8SNickeau    {
361*37748cd8SNickeau        if ($this->isNull()) {
362*37748cd8SNickeau            throw new \RuntimeException('The set is empty.');
363*37748cd8SNickeau        }
364*37748cd8SNickeau
365*37748cd8SNickeau        return $this->intervals[\count($this->intervals)-1]->stop;
366*37748cd8SNickeau    }
367*37748cd8SNickeau
368*37748cd8SNickeau    /**
369*37748cd8SNickeau     * Returns the minimum value contained in the set if not isNull().
370*37748cd8SNickeau     *
371*37748cd8SNickeau     * @return int The minimum value contained in the set.
372*37748cd8SNickeau     *
373*37748cd8SNickeau     * @throws \RuntimeException If set is empty.
374*37748cd8SNickeau     */
375*37748cd8SNickeau    public function getMinElement() : int
376*37748cd8SNickeau    {
377*37748cd8SNickeau        if ($this->isNull()) {
378*37748cd8SNickeau            throw new \RuntimeException('The set is empty.');
379*37748cd8SNickeau        }
380*37748cd8SNickeau
381*37748cd8SNickeau        return $this->intervals[0]->start;
382*37748cd8SNickeau    }
383*37748cd8SNickeau
384*37748cd8SNickeau    public function toStringChars(bool $elemAreChar) : string
385*37748cd8SNickeau    {
386*37748cd8SNickeau        if (!$this->intervals) {
387*37748cd8SNickeau            return '{}';
388*37748cd8SNickeau        }
389*37748cd8SNickeau
390*37748cd8SNickeau        $buf = '';
391*37748cd8SNickeau
392*37748cd8SNickeau        if ($this->length() > 1) {
393*37748cd8SNickeau            $buf .= '{';
394*37748cd8SNickeau        }
395*37748cd8SNickeau
396*37748cd8SNickeau        $iter = new \ArrayIterator($this->intervals);
397*37748cd8SNickeau
398*37748cd8SNickeau        while ($iter->valid()) {
399*37748cd8SNickeau            $interval = $iter->current();
400*37748cd8SNickeau            $iter->next();
401*37748cd8SNickeau            $start = $interval->start;
402*37748cd8SNickeau            $stop = $interval->stop;
403*37748cd8SNickeau
404*37748cd8SNickeau            if ($start === $stop) {
405*37748cd8SNickeau                if ($start === Token::EOF) {
406*37748cd8SNickeau                    $buf .= '<EOF>';
407*37748cd8SNickeau                } elseif ($elemAreChar) {
408*37748cd8SNickeau                    $buf .= '\'' . StringUtils::char($start) . '\'';
409*37748cd8SNickeau                } else {
410*37748cd8SNickeau                    $buf .= $start;
411*37748cd8SNickeau                }
412*37748cd8SNickeau            } else {
413*37748cd8SNickeau                if ($elemAreChar) {
414*37748cd8SNickeau                    $buf .= \sprintf(
415*37748cd8SNickeau                        '\'%s\'..\'%s\'',
416*37748cd8SNickeau                        StringUtils::char($start),
417*37748cd8SNickeau                        StringUtils::char($stop)
418*37748cd8SNickeau                    );
419*37748cd8SNickeau                } else {
420*37748cd8SNickeau                    $buf .= \sprintf('%s..%s', $start, $stop);
421*37748cd8SNickeau                }
422*37748cd8SNickeau            }
423*37748cd8SNickeau
424*37748cd8SNickeau            if ($iter->valid()) {
425*37748cd8SNickeau                $buf .= ', ';
426*37748cd8SNickeau            }
427*37748cd8SNickeau        }
428*37748cd8SNickeau
429*37748cd8SNickeau        if ($this->length() > 1) {
430*37748cd8SNickeau            $buf .= '}';
431*37748cd8SNickeau        }
432*37748cd8SNickeau
433*37748cd8SNickeau        return $buf;
434*37748cd8SNickeau    }
435*37748cd8SNickeau
436*37748cd8SNickeau    public function toStringVocabulary(Vocabulary $vocabulary) : string
437*37748cd8SNickeau    {
438*37748cd8SNickeau        if (!$this->intervals) {
439*37748cd8SNickeau            return '{}';
440*37748cd8SNickeau        }
441*37748cd8SNickeau
442*37748cd8SNickeau        $buf = '';
443*37748cd8SNickeau        if ($this->length() > 1) {
444*37748cd8SNickeau            $buf .= '{';
445*37748cd8SNickeau        }
446*37748cd8SNickeau
447*37748cd8SNickeau        $iterator = new \ArrayIterator($this->intervals);
448*37748cd8SNickeau
449*37748cd8SNickeau        while ($iterator->valid()) {
450*37748cd8SNickeau            $interval = $iterator->current();
451*37748cd8SNickeau            $iterator->next();
452*37748cd8SNickeau            $start = $interval->start;
453*37748cd8SNickeau            $stop = $interval->stop;
454*37748cd8SNickeau
455*37748cd8SNickeau            if ($start === $stop) {
456*37748cd8SNickeau                $buf .= $this->elementName($vocabulary, $start);
457*37748cd8SNickeau            } else {
458*37748cd8SNickeau                for ($i = $start; $i <= $stop; $i++) {
459*37748cd8SNickeau                    if ($i > $start) {
460*37748cd8SNickeau                        $buf .= ', ';
461*37748cd8SNickeau                    }
462*37748cd8SNickeau
463*37748cd8SNickeau                    $buf .= $this->elementName($vocabulary, $i);
464*37748cd8SNickeau                }
465*37748cd8SNickeau            }
466*37748cd8SNickeau
467*37748cd8SNickeau            if ($iterator->valid()) {
468*37748cd8SNickeau                $buf .= ', ';
469*37748cd8SNickeau            }
470*37748cd8SNickeau        }
471*37748cd8SNickeau
472*37748cd8SNickeau        if ($this->length() > 1) {
473*37748cd8SNickeau            $buf .= '}';
474*37748cd8SNickeau        }
475*37748cd8SNickeau
476*37748cd8SNickeau        return $buf;
477*37748cd8SNickeau    }
478*37748cd8SNickeau
479*37748cd8SNickeau    protected function elementName(Vocabulary $vocabulary, int $a) : string
480*37748cd8SNickeau    {
481*37748cd8SNickeau        if ($a === Token::EOF) {
482*37748cd8SNickeau            return '<EOF>';
483*37748cd8SNickeau        }
484*37748cd8SNickeau
485*37748cd8SNickeau        if ($a === Token::EPSILON) {
486*37748cd8SNickeau            return '<EPSILON>';
487*37748cd8SNickeau        }
488*37748cd8SNickeau
489*37748cd8SNickeau        return $vocabulary->getDisplayName($a);
490*37748cd8SNickeau    }
491*37748cd8SNickeau
492*37748cd8SNickeau    public function equals(object $other) : bool
493*37748cd8SNickeau    {
494*37748cd8SNickeau        if ($this === $other) {
495*37748cd8SNickeau            return true;
496*37748cd8SNickeau        }
497*37748cd8SNickeau
498*37748cd8SNickeau        if (!$other instanceof self) {
499*37748cd8SNickeau            return false;
500*37748cd8SNickeau        }
501*37748cd8SNickeau
502*37748cd8SNickeau        return $this->readOnly === $other->readOnly
503*37748cd8SNickeau            && Equality::equals($this->intervals, $other->intervals);
504*37748cd8SNickeau    }
505*37748cd8SNickeau
506*37748cd8SNickeau    /**
507*37748cd8SNickeau     * @return array<int>
508*37748cd8SNickeau     */
509*37748cd8SNickeau    public function toArray() : array
510*37748cd8SNickeau    {
511*37748cd8SNickeau        $values = [];
512*37748cd8SNickeau        foreach ($this->intervals as $interval) {
513*37748cd8SNickeau            $start = $interval->start;
514*37748cd8SNickeau            $stop = $interval->stop;
515*37748cd8SNickeau
516*37748cd8SNickeau            for ($value = $start; $value <= $stop; $value++) {
517*37748cd8SNickeau                $values[] = $value;
518*37748cd8SNickeau            }
519*37748cd8SNickeau        }
520*37748cd8SNickeau
521*37748cd8SNickeau        return $values;
522*37748cd8SNickeau    }
523*37748cd8SNickeau
524*37748cd8SNickeau    public function __toString() : string
525*37748cd8SNickeau    {
526*37748cd8SNickeau        return $this->toStringChars(false);
527*37748cd8SNickeau    }
528*37748cd8SNickeau}
529