1<?php 2 3/* 4 * This file is part of Twig. 5 * 6 * (c) Fabien Potencier 7 * (c) Armin Ronacher 8 * 9 * For the full copyright and license information, please view the LICENSE 10 * file that was distributed with this source code. 11 */ 12 13namespace Twig; 14 15use Twig\Error\SyntaxError; 16 17/** 18 * Represents a token stream. 19 * 20 * @author Fabien Potencier <fabien@symfony.com> 21 */ 22final class TokenStream 23{ 24 private $tokens; 25 private $current = 0; 26 private $source; 27 28 public function __construct(array $tokens, Source $source = null) 29 { 30 $this->tokens = $tokens; 31 $this->source = $source ?: new Source('', ''); 32 } 33 34 public function __toString() 35 { 36 return implode("\n", $this->tokens); 37 } 38 39 public function injectTokens(array $tokens) 40 { 41 $this->tokens = array_merge(\array_slice($this->tokens, 0, $this->current), $tokens, \array_slice($this->tokens, $this->current)); 42 } 43 44 /** 45 * Sets the pointer to the next token and returns the old one. 46 */ 47 public function next(): Token 48 { 49 if (!isset($this->tokens[++$this->current])) { 50 throw new SyntaxError('Unexpected end of template.', $this->tokens[$this->current - 1]->getLine(), $this->source); 51 } 52 53 return $this->tokens[$this->current - 1]; 54 } 55 56 /** 57 * Tests a token, sets the pointer to the next one and returns it or throws a syntax error. 58 * 59 * @return Token|null The next token if the condition is true, null otherwise 60 */ 61 public function nextIf($primary, $secondary = null) 62 { 63 if ($this->tokens[$this->current]->test($primary, $secondary)) { 64 return $this->next(); 65 } 66 } 67 68 /** 69 * Tests a token and returns it or throws a syntax error. 70 */ 71 public function expect($type, $value = null, string $message = null): Token 72 { 73 $token = $this->tokens[$this->current]; 74 if (!$token->test($type, $value)) { 75 $line = $token->getLine(); 76 throw new SyntaxError(sprintf('%sUnexpected token "%s"%s ("%s" expected%s).', 77 $message ? $message.'. ' : '', 78 Token::typeToEnglish($token->getType()), 79 $token->getValue() ? sprintf(' of value "%s"', $token->getValue()) : '', 80 Token::typeToEnglish($type), $value ? sprintf(' with value "%s"', $value) : ''), 81 $line, 82 $this->source 83 ); 84 } 85 $this->next(); 86 87 return $token; 88 } 89 90 /** 91 * Looks at the next token. 92 */ 93 public function look(int $number = 1): Token 94 { 95 if (!isset($this->tokens[$this->current + $number])) { 96 throw new SyntaxError('Unexpected end of template.', $this->tokens[$this->current + $number - 1]->getLine(), $this->source); 97 } 98 99 return $this->tokens[$this->current + $number]; 100 } 101 102 /** 103 * Tests the current token. 104 */ 105 public function test($primary, $secondary = null): bool 106 { 107 return $this->tokens[$this->current]->test($primary, $secondary); 108 } 109 110 /** 111 * Checks if end of stream was reached. 112 */ 113 public function isEOF(): bool 114 { 115 return /* Token::EOF_TYPE */ -1 === $this->tokens[$this->current]->getType(); 116 } 117 118 public function getCurrent(): Token 119 { 120 return $this->tokens[$this->current]; 121 } 122 123 /** 124 * Gets the source associated with this stream. 125 * 126 * @internal 127 */ 128 public function getSourceContext(): Source 129 { 130 return $this->source; 131 } 132} 133 134class_alias('Twig\TokenStream', 'Twig_TokenStream'); 135