1*37748cd8SNickeau<?php 2*37748cd8SNickeau 3*37748cd8SNickeaudeclare(strict_types=1); 4*37748cd8SNickeau 5*37748cd8SNickeaunamespace Antlr\Antlr4\Runtime; 6*37748cd8SNickeau 7*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Utils\StringUtils; 8*37748cd8SNickeau 9*37748cd8SNickeau/** 10*37748cd8SNickeau * Vacuum all input from a string and then treat it like a buffer. 11*37748cd8SNickeau */ 12*37748cd8SNickeaufinal class InputStream implements CharStream 13*37748cd8SNickeau{ 14*37748cd8SNickeau /** @var int */ 15*37748cd8SNickeau protected $index = 0; 16*37748cd8SNickeau 17*37748cd8SNickeau /** @var int */ 18*37748cd8SNickeau protected $size = 0; 19*37748cd8SNickeau 20*37748cd8SNickeau /** @var string */ 21*37748cd8SNickeau public $name = '<empty>'; 22*37748cd8SNickeau 23*37748cd8SNickeau /** @var string */ 24*37748cd8SNickeau public $input; 25*37748cd8SNickeau 26*37748cd8SNickeau /** @var array<string> */ 27*37748cd8SNickeau public $characters = []; 28*37748cd8SNickeau 29*37748cd8SNickeau /** 30*37748cd8SNickeau * @param array<string> $characters 31*37748cd8SNickeau */ 32*37748cd8SNickeau private function __construct(string $input, array $characters) 33*37748cd8SNickeau { 34*37748cd8SNickeau $this->input = $input; 35*37748cd8SNickeau $this->characters = $characters; 36*37748cd8SNickeau $this->size = \count($this->characters); 37*37748cd8SNickeau } 38*37748cd8SNickeau 39*37748cd8SNickeau public static function fromString(string $input) : InputStream 40*37748cd8SNickeau { 41*37748cd8SNickeau $chars = \preg_split('//u', $input, -1, \PREG_SPLIT_NO_EMPTY); 42*37748cd8SNickeau 43*37748cd8SNickeau return new self($input, $chars === false ? [] : $chars); 44*37748cd8SNickeau } 45*37748cd8SNickeau 46*37748cd8SNickeau public static function fromPath(string $path) : InputStream 47*37748cd8SNickeau { 48*37748cd8SNickeau $content = \file_get_contents($path); 49*37748cd8SNickeau 50*37748cd8SNickeau if ($content === false) { 51*37748cd8SNickeau throw new \InvalidArgumentException(\sprintf('File not found at %s.', $path)); 52*37748cd8SNickeau } 53*37748cd8SNickeau 54*37748cd8SNickeau return self::fromString($content); 55*37748cd8SNickeau } 56*37748cd8SNickeau 57*37748cd8SNickeau public function getIndex() : int 58*37748cd8SNickeau { 59*37748cd8SNickeau return $this->index; 60*37748cd8SNickeau } 61*37748cd8SNickeau 62*37748cd8SNickeau public function getLength() : int 63*37748cd8SNickeau { 64*37748cd8SNickeau return $this->size; 65*37748cd8SNickeau } 66*37748cd8SNickeau 67*37748cd8SNickeau public function consume() : void 68*37748cd8SNickeau { 69*37748cd8SNickeau if ($this->index >= $this->size) { 70*37748cd8SNickeau // assert this.LA(1) == Token.EOF 71*37748cd8SNickeau throw new \RuntimeException('Cannot consume EOF.'); 72*37748cd8SNickeau } 73*37748cd8SNickeau 74*37748cd8SNickeau $this->index++; 75*37748cd8SNickeau } 76*37748cd8SNickeau 77*37748cd8SNickeau public function LA(int $offset) : int 78*37748cd8SNickeau { 79*37748cd8SNickeau if ($offset === 0) { 80*37748cd8SNickeau return 0;// undefined 81*37748cd8SNickeau } 82*37748cd8SNickeau 83*37748cd8SNickeau if ($offset < 0) { 84*37748cd8SNickeau // e.g., translate LA(-1) to use offset=0 85*37748cd8SNickeau $offset++; 86*37748cd8SNickeau } 87*37748cd8SNickeau 88*37748cd8SNickeau $pos = $this->index + $offset - 1; 89*37748cd8SNickeau 90*37748cd8SNickeau if ($pos < 0 || $pos >= $this->size) { 91*37748cd8SNickeau // invalid 92*37748cd8SNickeau return Token::EOF; 93*37748cd8SNickeau } 94*37748cd8SNickeau 95*37748cd8SNickeau return StringUtils::codePoint($this->characters[$pos]); 96*37748cd8SNickeau } 97*37748cd8SNickeau 98*37748cd8SNickeau public function LT(int $offset) : int 99*37748cd8SNickeau { 100*37748cd8SNickeau return $this->LA($offset); 101*37748cd8SNickeau } 102*37748cd8SNickeau 103*37748cd8SNickeau /** 104*37748cd8SNickeau * Mark/release do nothing; we have entire buffer 105*37748cd8SNickeau */ 106*37748cd8SNickeau public function mark() : int 107*37748cd8SNickeau { 108*37748cd8SNickeau return -1; 109*37748cd8SNickeau } 110*37748cd8SNickeau 111*37748cd8SNickeau public function release(int $marker) : void 112*37748cd8SNickeau { 113*37748cd8SNickeau } 114*37748cd8SNickeau 115*37748cd8SNickeau /** 116*37748cd8SNickeau * {@see self::consume()} ahead until `$p === $this->index`; Can't just set 117*37748cd8SNickeau * `$p = $this->index` as we must update line and column. If we seek 118*37748cd8SNickeau * backwards, just set `$p`. 119*37748cd8SNickeau */ 120*37748cd8SNickeau public function seek(int $index) : void 121*37748cd8SNickeau { 122*37748cd8SNickeau if ($index <= $this->index) { 123*37748cd8SNickeau $this->index = $index; // just jump; don't update stream state (line, ...) 124*37748cd8SNickeau 125*37748cd8SNickeau return; 126*37748cd8SNickeau } 127*37748cd8SNickeau 128*37748cd8SNickeau // seek forward 129*37748cd8SNickeau $this->index = \min($index, $this->size); 130*37748cd8SNickeau } 131*37748cd8SNickeau 132*37748cd8SNickeau public function getText(int $start, int $stop) : string 133*37748cd8SNickeau { 134*37748cd8SNickeau if ($stop >= $this->size) { 135*37748cd8SNickeau $stop = $this->size - 1; 136*37748cd8SNickeau } 137*37748cd8SNickeau 138*37748cd8SNickeau if ($start >= $this->size) { 139*37748cd8SNickeau return ''; 140*37748cd8SNickeau } 141*37748cd8SNickeau 142*37748cd8SNickeau return \implode(\array_slice($this->characters, $start, $stop - $start + 1)); 143*37748cd8SNickeau } 144*37748cd8SNickeau 145*37748cd8SNickeau public function getSourceName() : string 146*37748cd8SNickeau { 147*37748cd8SNickeau return ''; 148*37748cd8SNickeau } 149*37748cd8SNickeau 150*37748cd8SNickeau public function __toString() : string 151*37748cd8SNickeau { 152*37748cd8SNickeau return $this->input; 153*37748cd8SNickeau } 154*37748cd8SNickeau} 155