1: | <?php declare(strict_types = 1); |
2: | |
3: | namespace PHPStan\PhpDocParser\Parser; |
4: | |
5: | use PHPStan\PhpDocParser\Ast; |
6: | use PHPStan\PhpDocParser\Lexer\Lexer; |
7: | use function strtolower; |
8: | use function trim; |
9: | |
10: | class ConstExprParser |
11: | { |
12: | |
13: | public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\ConstExpr\ConstExprNode |
14: | { |
15: | if ($tokens->isCurrentTokenType(Lexer::TOKEN_FLOAT)) { |
16: | $value = $tokens->currentTokenValue(); |
17: | $tokens->next(); |
18: | return new Ast\ConstExpr\ConstExprFloatNode($value); |
19: | } |
20: | |
21: | if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) { |
22: | $value = $tokens->currentTokenValue(); |
23: | $tokens->next(); |
24: | return new Ast\ConstExpr\ConstExprIntegerNode($value); |
25: | } |
26: | |
27: | if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) { |
28: | $value = $tokens->currentTokenValue(); |
29: | if ($trimStrings) { |
30: | $value = trim($tokens->currentTokenValue(), "'"); |
31: | } |
32: | $tokens->next(); |
33: | return new Ast\ConstExpr\ConstExprStringNode($value); |
34: | |
35: | } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { |
36: | $value = $tokens->currentTokenValue(); |
37: | if ($trimStrings) { |
38: | $value = trim($tokens->currentTokenValue(), '"'); |
39: | } |
40: | $tokens->next(); |
41: | return new Ast\ConstExpr\ConstExprStringNode($value); |
42: | |
43: | } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { |
44: | $identifier = $tokens->currentTokenValue(); |
45: | $tokens->next(); |
46: | |
47: | switch (strtolower($identifier)) { |
48: | case 'true': |
49: | return new Ast\ConstExpr\ConstExprTrueNode(); |
50: | case 'false': |
51: | return new Ast\ConstExpr\ConstExprFalseNode(); |
52: | case 'null': |
53: | return new Ast\ConstExpr\ConstExprNullNode(); |
54: | case 'array': |
55: | $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); |
56: | return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_PARENTHESES); |
57: | } |
58: | |
59: | if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) { |
60: | $classConstantName = ''; |
61: | $lastType = null; |
62: | while (true) { |
63: | if ($lastType !== Lexer::TOKEN_IDENTIFIER && $tokens->currentTokenType() === Lexer::TOKEN_IDENTIFIER) { |
64: | $classConstantName .= $tokens->currentTokenValue(); |
65: | $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); |
66: | $lastType = Lexer::TOKEN_IDENTIFIER; |
67: | |
68: | continue; |
69: | } |
70: | |
71: | if ($lastType !== Lexer::TOKEN_WILDCARD && $tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) { |
72: | $classConstantName .= '*'; |
73: | $lastType = Lexer::TOKEN_WILDCARD; |
74: | |
75: | if ($tokens->getSkippedHorizontalWhiteSpaceIfAny() !== '') { |
76: | break; |
77: | } |
78: | |
79: | continue; |
80: | } |
81: | |
82: | if ($lastType === null) { |
83: | |
84: | $tokens->consumeTokenType(Lexer::TOKEN_WILDCARD); |
85: | } |
86: | |
87: | break; |
88: | } |
89: | |
90: | return new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName); |
91: | |
92: | } |
93: | |
94: | return new Ast\ConstExpr\ConstFetchNode('', $identifier); |
95: | |
96: | } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { |
97: | return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_SQUARE_BRACKET); |
98: | } |
99: | |
100: | throw new ParserException( |
101: | $tokens->currentTokenValue(), |
102: | $tokens->currentTokenType(), |
103: | $tokens->currentTokenOffset(), |
104: | Lexer::TOKEN_IDENTIFIER |
105: | ); |
106: | } |
107: | |
108: | |
109: | private function parseArray(TokenIterator $tokens, int $endToken): Ast\ConstExpr\ConstExprArrayNode |
110: | { |
111: | $items = []; |
112: | |
113: | if (!$tokens->tryConsumeTokenType($endToken)) { |
114: | do { |
115: | $items[] = $this->parseArrayItem($tokens); |
116: | } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA) && !$tokens->isCurrentTokenType($endToken)); |
117: | $tokens->consumeTokenType($endToken); |
118: | } |
119: | |
120: | return new Ast\ConstExpr\ConstExprArrayNode($items); |
121: | } |
122: | |
123: | |
124: | private function parseArrayItem(TokenIterator $tokens): Ast\ConstExpr\ConstExprArrayItemNode |
125: | { |
126: | $expr = $this->parse($tokens); |
127: | |
128: | if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_ARROW)) { |
129: | $key = $expr; |
130: | $value = $this->parse($tokens); |
131: | |
132: | } else { |
133: | $key = null; |
134: | $value = $expr; |
135: | } |
136: | |
137: | return new Ast\ConstExpr\ConstExprArrayItemNode($key, $value); |
138: | } |
139: | |
140: | } |
141: | |