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