packages/bison-parser/tests/Unit/Syntax/TokenStreamTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Syntax;
6
7use BisonParser\Ast\Location;
8use BisonParser\Scanner\Token;
9use BisonParser\Scanner\TokenKind;
10use BisonParser\Syntax\TokenStream;
11use BisonParser\SyntaxException;
12use PHPUnit\Framework\Attributes\CoversClass;
13use PHPUnit\Framework\Attributes\Small;
14use PHPUnit\Framework\Attributes\UsesClass;
15use PHPUnit\Framework\TestCase;
16
17#[CoversClass(TokenStream::class)]
18#[UsesClass(Location::class)]
19#[UsesClass(SyntaxException::class)]
20#[UsesClass(Token::class)]
21#[UsesClass(TokenKind::class)]
22#[Small]
23final class TokenStreamTest extends TestCase
24{
25 public function testPeek(): void
26 {
27 $stream = new TokenStream([new Token(TokenKind::Colon, ':', new Location(1, 1)), new Token(TokenKind::End, '', new Location(1, 2))]);
28
29 self::assertSame(TokenKind::Colon, $stream->peek()->kind);
30 self::assertSame(TokenKind::End, $stream->peek(1)->kind);
31 self::assertSame(TokenKind::End, $stream->peek(5)->kind);
32 }
33
34 public function testNext(): void
35 {
36 $stream = new TokenStream([new Token(TokenKind::Colon, ':', new Location(1, 1)), new Token(TokenKind::End, '', new Location(1, 2))]);
37
38 self::assertSame(TokenKind::Colon, $stream->next()->kind);
39 self::assertSame(TokenKind::End, $stream->next()->kind);
40 self::assertSame(TokenKind::End, $stream->next()->kind);
41 }
42
43 public function testIs(): void
44 {
45 $stream = new TokenStream([new Token(TokenKind::Pipe, '|', new Location(1, 1)), new Token(TokenKind::End, '', new Location(1, 2))]);
46
47 self::assertTrue($stream->is(TokenKind::Pipe));
48 self::assertFalse($stream->is(TokenKind::End));
49 }
50
51 public function testIsDirective(): void
52 {
53 $stream = new TokenStream([new Token(TokenKind::Directive, 'left', new Location(1, 1), '%left'), new Token(TokenKind::End, '', new Location(1, 6))]);
54
55 self::assertTrue($stream->isDirective('left'));
56 self::assertFalse($stream->isDirective('right'));
57 }
58
59 public function testAccept(): void
60 {
61 $stream = new TokenStream([new Token(TokenKind::Pipe, '|', new Location(1, 1)), new Token(TokenKind::End, '', new Location(1, 2))]);
62
63 self::assertNull($stream->accept(TokenKind::Colon));
64 self::assertSame('|', $stream->accept(TokenKind::Pipe)?->text);
65 self::assertTrue($stream->is(TokenKind::End));
66 }
67
68 public function testExpect(): void
69 {
70 $stream = new TokenStream([new Token(TokenKind::Integer, '3', new Location(1, 1)), new Token(TokenKind::End, '', new Location(1, 2))]);
71
72 self::assertSame('3', $stream->expect(TokenKind::Integer, 'a number')->text);
73 self::assertTrue($stream->is(TokenKind::End));
74 }
75
76 public function testExpectRejectsAnotherToken(): void
77 {
78 $stream = new TokenStream([new Token(TokenKind::Identifier, 'x', new Location(2, 5)), new Token(TokenKind::End, '', new Location(2, 6))]);
79
80 $this->expectException(SyntaxException::class);
81 $this->expectExceptionMessage("Expected a number but found 'x' at 2:5");
82
83 $stream->expect(TokenKind::Integer, 'a number');
84 }
85}
86