phplrt/phplrt

View on GitHub
libs/parser/src/Buffer/ArrayBuffer.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

declare(strict_types=1);

namespace Phplrt\Parser\Buffer;

use Phplrt\Contracts\Lexer\TokenInterface;
use Phplrt\Parser\Exception\BufferPositionOverflowException;
use Phplrt\Parser\Exception\BufferPositionUnderflowException;

final class ArrayBuffer implements BufferInterface
{
    /**
     * @var list<TokenInterface>
     */
    private readonly array $buffer;

    /**
     * @var int<0, max>
     */
    private readonly int $size;

    /**
     * @var int<0, max>
     */
    private readonly int $first;

    /**
     * @var int<0, max>
     */
    private int $current;

    /**
     * @param iterable<int<0, max>, TokenInterface> $stream
     */
    public function __construct(iterable $stream)
    {
        $this->buffer = $this->iterableToArray($stream);
        $this->size = \count($this->buffer);

        \assert($this->size > 0, 'Buffer size must be greater than 0');

        $this->first = $this->current = \array_key_first($this->buffer);
    }

    /**
     * @param iterable<int<0, max>, TokenInterface> $tokens
     * @return list<TokenInterface>
     */
    private function iterableToArray(iterable $tokens): array
    {
        if ($tokens instanceof \Traversable) {
            return \iterator_to_array($tokens, false);
        }

        return $tokens;
    }

    public function rewind(): void
    {
        $this->seek($this->first);
    }

    public function key(): int
    {
        return $this->current;
    }

    public function seek(int $offset): void
    {
        if ($offset < $this->first) {
            throw BufferPositionUnderflowException::fromOffsetUnderflow($offset, $this->first);
        }

        if ($offset >= $this->size) {
            throw BufferPositionOverflowException::fromOffsetOverflow($offset, $this->size - 1);
        }

        $this->current = $offset;
    }

    public function current(): TokenInterface
    {
        if (isset($this->buffer[$this->current])) {
            return $this->buffer[$this->current];
        }

        return $this->buffer[$this->size - 1];
    }

    public function next(): void
    {
        if ($this->current < $this->size) {
            ++$this->current;
        }
    }

    public function valid(): bool
    {
        return $this->current < $this->size;
    }
}