• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nette / latte / 22368407632

24 Feb 2026 08:17PM UTC coverage: 94.825% (-0.2%) from 95.054%
22368407632

push

github

dg
phpstan

135 of 147 new or added lines in 38 files covered. (91.84%)

186 existing lines in 70 files now uncovered.

5534 of 5836 relevant lines covered (94.83%)

0.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.92
/src/Latte/Compiler/TokenStream.php
1
<?php declare(strict_types=1);
2

3
/**
4
 * This file is part of the Latte (https://latte.nette.org)
5
 * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
6
 */
7

8
namespace Latte\Compiler;
9

10
use Latte\CompileException;
11
use function array_map, count, implode, is_int, is_string, trim;
12

13

14
/**
15
 * TokenStream loads tokens from $source iterator on-demand, and places them in a buffer to provide access
16
 * to any previous token by index.
17
 */
18
final class TokenStream
19
{
20
        /** @var Token[] */
21
        private array $tokens = [];
22
        private int $index = 0;
23

24

25
        public function __construct(
1✔
26
                private readonly \Iterator $source,
27
        ) {
28
        }
1✔
29

30

31
        /**
32
         * Tells whether the token at current position is of given kind.
33
         */
34
        public function is(int|string ...$kind): bool
1✔
35
        {
36
                return $this->peek()->is(...$kind);
1✔
37
        }
38

39

40
        /**
41
         * Gets the token at $offset from the current position or throws.
42
         */
43
        public function peek(int $offset = 0): Token
1✔
44
        {
45
                return $this->tryPeek($offset) ?? throw new CompileException('Unexpected end');
1✔
46
        }
47

48

49
        /**
50
         * Gets the token at $offset from the current position.
51
         */
52
        public function tryPeek(int $offset = 0): ?Token
1✔
53
        {
54
                $pos = $this->index + $offset;
1✔
55
                while ($pos >= 0 && !isset($this->tokens[$pos]) && $this->source->valid()) {
1✔
56
                        if ($this->tokens) {
1✔
57
                                $this->source->next();
1✔
58
                        }
59

60
                        if ($this->source->valid()) {
1✔
61
                                $this->tokens[] = $this->source->current();
1✔
62
                        }
63
                }
64

65
                return $this->tokens[$pos] ?? null;
1✔
66
        }
67

68

69
        /**
70
         * Consumes the current token (if is of given kind) or throws exception on end.
71
         * @throws CompileException
72
         */
73
        public function consume(int|string ...$kind): Token
1✔
74
        {
75
                $token = $this->peek();
1✔
76
                if ($kind && !$token->is(...$kind)) {
1✔
77
                        $kind = array_map(fn($item) => is_string($item) ? "'$item'" : Token::Names[$item], $kind);
1✔
78
                        $this->throwUnexpectedException($kind);
1✔
79
                } elseif (!$token->isEnd()) {
1✔
80
                        $this->index++;
1✔
81
                }
82
                return $token;
1✔
83
        }
84

85

86
        /**
87
         * Consumes the current token of given kind or returns null.
88
         */
89
        public function tryConsume(int|string ...$kind): ?Token
1✔
90
        {
91
                $token = $this->tryPeek();
1✔
92
                if (!$token?->is(...$kind)) {
1✔
93
                        return null;
1✔
94
                } elseif (!$token->isEnd()) {
1✔
95
                        $this->index++;
1✔
96
                }
97
                return $token;
1✔
98
        }
99

100

101
        /**
102
         * Sets the input cursor to the position.
103
         */
104
        public function seek(int $index): void
1✔
105
        {
106
                if ($index >= count($this->tokens) || $index < 0) {
1✔
107
                        throw new \InvalidArgumentException('The position is out of range.');
1✔
108
                }
109
                $this->index = $index;
1✔
110
        }
1✔
111

112

113
        /**
114
         * Returns the cursor position.
115
         */
116
        public function getIndex(): int
117
        {
118
                return $this->index;
1✔
119
        }
120

121

122
        /**
123
         * @throws CompileException
124
         * @param  array<string|int>  $expected
125
         */
126
        public function throwUnexpectedException(array $expected = [], string $addendum = '', string $excerpt = ''): never
1✔
127
        {
128
                $token = ($this->tryPeek()->text ?? '') . $excerpt;
1✔
129
                $expected = array_map(fn($item) => is_int($item) ? Token::Names[$item] : $item, $expected);
1✔
130
                throw new CompileException(
1✔
131
                        'Unexpected '
132
                        . ($token === ''
1✔
133
                                ? 'end'
1✔
134
                                : "'" . trim($token, "\n") . "'")
1✔
135
                        . ($expected && count($expected) < 5
1✔
136
                                ? ', expecting ' . implode(', ', $expected)
1✔
137
                                : '')
1✔
138
                        . $addendum,
1✔
139
                        $this->tryPeek()?->position,
1✔
140
                );
UNCOV
141
        }
×
142
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc