Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php declare(strict_types=1);
2
 
3
namespace PhpParser\Internal;
4
 
5
/**
6
 * Provides operations on token streams, for use by pretty printer.
7
 *
8
 * @internal
9
 */
10
class TokenStream
11
{
12
    /** @var array Tokens (in token_get_all format) */
13
    private $tokens;
14
    /** @var int[] Map from position to indentation */
15
    private $indentMap;
16
 
17
    /**
18
     * Create token stream instance.
19
     *
20
     * @param array $tokens Tokens in token_get_all() format
21
     */
22
    public function __construct(array $tokens) {
23
        $this->tokens = $tokens;
24
        $this->indentMap = $this->calcIndentMap();
25
    }
26
 
27
    /**
28
     * Whether the given position is immediately surrounded by parenthesis.
29
     *
30
     * @param int $startPos Start position
31
     * @param int $endPos   End position
32
     *
33
     * @return bool
34
     */
35
    public function haveParens(int $startPos, int $endPos) : bool {
36
        return $this->haveTokenImmediatelyBefore($startPos, '(')
37
            && $this->haveTokenImmediatelyAfter($endPos, ')');
38
    }
39
 
40
    /**
41
     * Whether the given position is immediately surrounded by braces.
42
     *
43
     * @param int $startPos Start position
44
     * @param int $endPos   End position
45
     *
46
     * @return bool
47
     */
48
    public function haveBraces(int $startPos, int $endPos) : bool {
49
        return ($this->haveTokenImmediatelyBefore($startPos, '{')
50
                || $this->haveTokenImmediatelyBefore($startPos, T_CURLY_OPEN))
51
            && $this->haveTokenImmediatelyAfter($endPos, '}');
52
    }
53
 
54
    /**
55
     * Check whether the position is directly preceded by a certain token type.
56
     *
57
     * During this check whitespace and comments are skipped.
58
     *
59
     * @param int        $pos               Position before which the token should occur
60
     * @param int|string $expectedTokenType Token to check for
61
     *
62
     * @return bool Whether the expected token was found
63
     */
64
    public function haveTokenImmediatelyBefore(int $pos, $expectedTokenType) : bool {
65
        $tokens = $this->tokens;
66
        $pos--;
67
        for (; $pos >= 0; $pos--) {
68
            $tokenType = $tokens[$pos][0];
69
            if ($tokenType === $expectedTokenType) {
70
                return true;
71
            }
72
            if ($tokenType !== \T_WHITESPACE
73
                && $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
74
                break;
75
            }
76
        }
77
        return false;
78
    }
79
 
80
    /**
81
     * Check whether the position is directly followed by a certain token type.
82
     *
83
     * During this check whitespace and comments are skipped.
84
     *
85
     * @param int        $pos               Position after which the token should occur
86
     * @param int|string $expectedTokenType Token to check for
87
     *
88
     * @return bool Whether the expected token was found
89
     */
90
    public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType) : bool {
91
        $tokens = $this->tokens;
92
        $pos++;
93
        for (; $pos < \count($tokens); $pos++) {
94
            $tokenType = $tokens[$pos][0];
95
            if ($tokenType === $expectedTokenType) {
96
                return true;
97
            }
98
            if ($tokenType !== \T_WHITESPACE
99
                && $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
100
                break;
101
            }
102
        }
103
        return false;
104
    }
105
 
106
    public function skipLeft(int $pos, $skipTokenType) {
107
        $tokens = $this->tokens;
108
 
109
        $pos = $this->skipLeftWhitespace($pos);
110
        if ($skipTokenType === \T_WHITESPACE) {
111
            return $pos;
112
        }
113
 
114
        if ($tokens[$pos][0] !== $skipTokenType) {
115
            // Shouldn't happen. The skip token MUST be there
116
            throw new \Exception('Encountered unexpected token');
117
        }
118
        $pos--;
119
 
120
        return $this->skipLeftWhitespace($pos);
121
    }
122
 
123
    public function skipRight(int $pos, $skipTokenType) {
124
        $tokens = $this->tokens;
125
 
126
        $pos = $this->skipRightWhitespace($pos);
127
        if ($skipTokenType === \T_WHITESPACE) {
128
            return $pos;
129
        }
130
 
131
        if ($tokens[$pos][0] !== $skipTokenType) {
132
            // Shouldn't happen. The skip token MUST be there
133
            throw new \Exception('Encountered unexpected token');
134
        }
135
        $pos++;
136
 
137
        return $this->skipRightWhitespace($pos);
138
    }
139
 
140
    /**
141
     * Return first non-whitespace token position smaller or equal to passed position.
142
     *
143
     * @param int $pos Token position
144
     * @return int Non-whitespace token position
145
     */
146
    public function skipLeftWhitespace(int $pos) {
147
        $tokens = $this->tokens;
148
        for (; $pos >= 0; $pos--) {
149
            $type = $tokens[$pos][0];
150
            if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
151
                break;
152
            }
153
        }
154
        return $pos;
155
    }
156
 
157
    /**
158
     * Return first non-whitespace position greater or equal to passed position.
159
     *
160
     * @param int $pos Token position
161
     * @return int Non-whitespace token position
162
     */
163
    public function skipRightWhitespace(int $pos) {
164
        $tokens = $this->tokens;
165
        for ($count = \count($tokens); $pos < $count; $pos++) {
166
            $type = $tokens[$pos][0];
167
            if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
168
                break;
169
            }
170
        }
171
        return $pos;
172
    }
173
 
174
    public function findRight(int $pos, $findTokenType) {
175
        $tokens = $this->tokens;
176
        for ($count = \count($tokens); $pos < $count; $pos++) {
177
            $type = $tokens[$pos][0];
178
            if ($type === $findTokenType) {
179
                return $pos;
180
            }
181
        }
182
        return -1;
183
    }
184
 
185
    /**
186
     * Whether the given position range contains a certain token type.
187
     *
188
     * @param int $startPos Starting position (inclusive)
189
     * @param int $endPos Ending position (exclusive)
190
     * @param int|string $tokenType Token type to look for
191
     * @return bool Whether the token occurs in the given range
192
     */
193
    public function haveTokenInRange(int $startPos, int $endPos, $tokenType) {
194
        $tokens = $this->tokens;
195
        for ($pos = $startPos; $pos < $endPos; $pos++) {
196
            if ($tokens[$pos][0] === $tokenType) {
197
                return true;
198
            }
199
        }
200
        return false;
201
    }
202
 
203
    public function haveBracesInRange(int $startPos, int $endPos) {
204
        return $this->haveTokenInRange($startPos, $endPos, '{')
205
            || $this->haveTokenInRange($startPos, $endPos, T_CURLY_OPEN)
206
            || $this->haveTokenInRange($startPos, $endPos, '}');
207
    }
208
 
209
    public function haveTagInRange(int $startPos, int $endPos): bool {
210
        return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG)
211
            || $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG);
212
    }
213
 
214
    /**
215
     * Get indentation before token position.
216
     *
217
     * @param int $pos Token position
218
     *
219
     * @return int Indentation depth (in spaces)
220
     */
221
    public function getIndentationBefore(int $pos) : int {
222
        return $this->indentMap[$pos];
223
    }
224
 
225
    /**
226
     * Get the code corresponding to a token offset range, optionally adjusted for indentation.
227
     *
228
     * @param int $from   Token start position (inclusive)
229
     * @param int $to     Token end position (exclusive)
230
     * @param int $indent By how much the code should be indented (can be negative as well)
231
     *
232
     * @return string Code corresponding to token range, adjusted for indentation
233
     */
234
    public function getTokenCode(int $from, int $to, int $indent) : string {
235
        $tokens = $this->tokens;
236
        $result = '';
237
        for ($pos = $from; $pos < $to; $pos++) {
238
            $token = $tokens[$pos];
239
            if (\is_array($token)) {
240
                $type = $token[0];
241
                $content = $token[1];
242
                if ($type === \T_CONSTANT_ENCAPSED_STRING || $type === \T_ENCAPSED_AND_WHITESPACE) {
243
                    $result .= $content;
244
                } else {
245
                    // TODO Handle non-space indentation
246
                    if ($indent < 0) {
247
                        $result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $content);
248
                    } elseif ($indent > 0) {
249
                        $result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $content);
250
                    } else {
251
                        $result .= $content;
252
                    }
253
                }
254
            } else {
255
                $result .= $token;
256
            }
257
        }
258
        return $result;
259
    }
260
 
261
    /**
262
     * Precalculate the indentation at every token position.
263
     *
264
     * @return int[] Token position to indentation map
265
     */
266
    private function calcIndentMap() {
267
        $indentMap = [];
268
        $indent = 0;
269
        foreach ($this->tokens as $token) {
270
            $indentMap[] = $indent;
271
 
272
            if ($token[0] === \T_WHITESPACE) {
273
                $content = $token[1];
274
                $newlinePos = \strrpos($content, "\n");
275
                if (false !== $newlinePos) {
276
                    $indent = \strlen($content) - $newlinePos - 1;
277
                }
278
            }
279
        }
280
 
281
        // Add a sentinel for one past end of the file
282
        $indentMap[] = $indent;
283
 
284
        return $indentMap;
285
    }
286
}