1<?php 2 3declare(strict_types=1); 4 5namespace Antlr\Antlr4\Runtime; 6 7/** 8 * This class provides a default implementation of the {@see Vocabulary} 9 * interface. 10 * 11 * @author Sam Harwell 12 */ 13final class VocabularyImpl implements Vocabulary 14{ 15 /** @var array<string|null> */ 16 private $literalNames; 17 18 /** @var array<string|null> */ 19 private $symbolicNames; 20 21 /** @var array<string|null> */ 22 private $displayNames; 23 24 /** @var int */ 25 private $maxTokenType; 26 27 /** 28 * Constructs a new instance from the specified literal, symbolic 29 * and display token names. 30 * 31 * @param array<string|null> $literalNames The literal names assigned 32 * to tokens, or `null` if no 33 * literal names are assigned. 34 * @param array<string|null> $symbolicNames The symbolic names assigned 35 * to tokens, or `null` if 36 * no symbolic names are assigned. 37 * @param array<string|null> $displayNames The display names assigned 38 * to tokens, or `null` to use 39 * the values in literalNames` and 40 * `symbolicNames` as the source 41 * of display names, as described 42 * in {@see VocabularyImpl::getDisplayName()}. 43 */ 44 public function __construct(array $literalNames = [], array $symbolicNames = [], array $displayNames = []) 45 { 46 $this->literalNames = $literalNames; 47 $this->symbolicNames = $symbolicNames; 48 $this->displayNames = $displayNames; 49 50 // See note here on -1 part: https://github.com/antlr/antlr4/pull/1146 51 $this->maxTokenType = \max( 52 \count($this->displayNames), 53 \count($this->literalNames), 54 \count($this->symbolicNames) 55 ) - 1; 56 } 57 58 /** 59 * Gets an empty {@see Vocabulary} instance. 60 * 61 * No literal or symbol names are assigned to token types, so 62 * {@see Vocabulary::getDisplayName()} returns the numeric value for 63 * all tokens except {@see Token::EOF}. 64 */ 65 public static function emptyVocabulary() : self 66 { 67 static $empty; 68 69 return $empty ?? ($empty = new self()); 70 } 71 72 /** 73 * Returns a {@see VocabularyImpl} instance from the specified set 74 * of token names. This method acts as a compatibility layer for the single 75 * `tokenNames` array generated by previous releases of ANTLR. 76 * 77 * The resulting vocabulary instance returns `null` for 78 * {@see VocabularyImpl::getLiteralName()} and {@see VocabularyImpl::getSymbolicName()}, 79 * and the value from `tokenNames` for the display names. 80 * 81 * @param array<string|null> $tokenNames The token names, or `null` if 82 * no token names are available. 83 * 84 * @return Vocabulary A {@see Vocabulary} instance which uses `tokenNames` 85 * for the display names of tokens. 86 */ 87 public static function fromTokenNames(array $tokenNames = []) : Vocabulary 88 { 89 if (\count($tokenNames) === 0) { 90 return self::emptyVocabulary(); 91 } 92 93 $literalNames = $tokenNames; // copy array 94 $symbolicNames = $tokenNames; // copy array 95 96 foreach ($tokenNames as $i => $tokenName) { 97 if ($tokenName === null) { 98 continue; 99 } 100 101 if ($tokenName !== '') { 102 $firstChar = $tokenName[0]; 103 104 if ($firstChar === '\'') { 105 $symbolicNames[$i] = null; 106 107 continue; 108 } 109 110 if (\ctype_upper($firstChar)) { 111 $literalNames[$i] = null; 112 113 continue; 114 } 115 } 116 117 // wasn't a literal or symbolic name 118 $literalNames[$i] = null; 119 $symbolicNames[$i] = null; 120 } 121 122 return new VocabularyImpl($literalNames, $symbolicNames, $tokenNames); 123 } 124 125 public function getMaxTokenType() : int 126 { 127 return $this->maxTokenType; 128 } 129 130 public function getLiteralName(int $tokenType) : ?string 131 { 132 if ($tokenType >= 0 && $tokenType < \count($this->literalNames)) { 133 return $this->literalNames[$tokenType]; 134 } 135 136 return null; 137 } 138 139 public function getSymbolicName(int $tokenType) : ?string 140 { 141 if ($tokenType >= 0 && $tokenType < \count($this->symbolicNames)) { 142 return $this->symbolicNames[$tokenType]; 143 } 144 145 if ($tokenType === Token::EOF) { 146 return 'EOF'; 147 } 148 149 return null; 150 } 151 152 public function getDisplayName(int $tokenType) : string 153 { 154 if ($tokenType >= 0 && $tokenType < \count($this->displayNames)) { 155 $displayName = $this->displayNames[$tokenType]; 156 157 if ($displayName !== null) { 158 return $displayName; 159 } 160 } 161 162 $literalName = $this->getLiteralName($tokenType); 163 164 if ($literalName !== null) { 165 return $literalName; 166 } 167 168 $symbolicName = $this->getSymbolicName($tokenType); 169 170 if ($symbolicName !== null) { 171 return $symbolicName; 172 } 173 174 return (string) $tokenType; 175 } 176} 177