protected function readToken(Token $prev): Token
    {
        $this->position = $prev->getEnd();

        $this->skipWhitespace();

        $line   = $this->line;
        $column = (1 + $this->position) - $this->lineStart;

        if ($this->position >= $this->bodyLength) {
            return $this->createEndOfFileToken($line, $column, $prev);
        }

        $code = $this->readCharCode($this->position);

        // Punctuation: [!$&:=@|()\[\]{}]{1}
        if (33 === $code || 36 === $code || 38 === $code || 58 === $code || 61 === $code || 64 === $code || 124 === $code ||
            40 === $code || 41 === $code || 91 === $code || 93 === $code || 123 === $code || 125 === $code) {
            return $this->lexPunctuation($code, $line, $column, $prev);
        }

        // Comment: #[\u0009\u0020-\uFFFF]*
        if (35 === $code) {
            return $this->lexComment($line, $column, $prev);
        }

        // Int:   -?(0|[1-9][0-9]*)
        // Float: -?(0|[1-9][0-9]*)(\.[0-9]+)?((E|e)(+|-)?[0-9]+)?
        if (45 === $code || isNumber($code)) {
            return $this->lexNumber($code, $line, $column, $prev);
        }

        // Name: [_A-Za-z][_0-9A-Za-z]*
        if (isAlphaNumeric($code)) {
            return $this->lexName($line, $column, $prev);
        }

        // Spread: ...
        if ($this->bodyLength >= 3 && $this->isSpread($code)) {
            return $this->lexSpread($line, $column, $prev);
        }

        // String: "([^"\\\u000A\u000D]|(\\(u[0-9a-fA-F]{4}|["\\/bfnrt])))*"
        if ($this->isString($code)) {
            return $this->lexString($line, $column, $prev);
        }

        // Block String: """("?"?(\\"""|\\(?!=""")|[^"\\]))*"""
        if ($this->bodyLength >= 3 && $this->isTripleQuote($code)) {
            return $this->lexBlockString($line, $column, $prev);
        }

        throw $this->createSyntaxErrorException();
    }