packages/sql-parser/tests/Unit/Lexer/TerminalIndexTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Lexer;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\Small;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlParser\Grammar\SymbolTable;
12use SqlParser\Lexer\Lexeme;
13use SqlParser\Lexer\LexicalException;
14use SqlParser\Lexer\SourcePosition;
15use SqlParser\Lexer\TerminalIndex;
16use SqlParser\Lexer\Token;
17
18#[CoversClass(TerminalIndex::class)]
19#[UsesClass(Lexeme::class)]
20#[UsesClass(LexicalException::class)]
21#[UsesClass(SourcePosition::class)]
22#[UsesClass(SymbolTable::class)]
23#[UsesClass(Token::class)]
24#[UsesClass(\SqlParser\Lexer\SourceException::class)]
25#[Small]
26final class TerminalIndexTest extends TestCase
27{
28 public function testTokens(): void
29 {
30 $index = new TerminalIndex(new SymbolTable(['$end', 'SELECT', 'NUM'], ['$accept', 'stmt']));
31 $tokens = $index->tokens([new Lexeme('SELECT', 'SELECT', 0), new Lexeme('NUM', '1', 7)], 'SELECT 1');
32
33 self::assertSame([1, 2, 0], array_map(static fn (Token $token): int => $token->symbol, $tokens));
34 self::assertSame('', $tokens[2]->text);
35 self::assertSame(8, $tokens[2]->offset);
36 }
37
38 public function testTokensGivesTheSkippedTextToTheTokenThatFollowsIt(): void
39 {
40 $index = new TerminalIndex(new SymbolTable(['$end', 'SELECT', 'NUM'], ['$accept', 'stmt']));
41 $tokens = $index->tokens([new Lexeme('SELECT', 'SELECT', 2), new Lexeme('NUM', '1', 19)], " SELECT /* one */ 1 -- done\n");
42
43 self::assertSame([' ', ' /* one */ ', " -- done\n"], array_map(static fn (Token $token): string => $token->leading, $tokens));
44 self::assertSame(30, $tokens[2]->offset);
45 }
46
47 public function testToken(): void
48 {
49 $index = new TerminalIndex(new SymbolTable(['$end', 'NUM'], ['$accept', 'stmt']));
50 $token = $index->token(new Lexeme('NUM', '42', 3), 'xx 42', ' ');
51
52 self::assertSame(1, $token->symbol);
53 self::assertSame('42', $token->text);
54 self::assertSame(' ', $token->leading);
55 }
56
57 public function testTokenRejectsAnUnknownTerminal(): void
58 {
59 $this->expectException(LexicalException::class);
60
61 (new TerminalIndex(new SymbolTable(['$end'], ['$accept'])))->token(new Lexeme('NUM', '1', 0), '1');
62 }
63
64 public function testTokenRejectsANonterminalName(): void
65 {
66 $this->expectException(LexicalException::class);
67
68 (new TerminalIndex(new SymbolTable(['$end'], ['$accept', 'stmt'])))->token(new Lexeme('stmt', '', 0), '');
69 }
70}
71