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