packages/sql-faker/tests/Unit/Compiler/Bison/Directive/BisonDirectiveReaderChainTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\Compiler\Bison\Directive;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlFaker\Compiler\Bison\Ast\BisonDeclaration;
12use SqlFaker\Compiler\Bison\Ast\BisonDefineDeclaration;
13use SqlFaker\Compiler\Bison\Ast\BisonExpectDeclaration;
14use SqlFaker\Compiler\Bison\Ast\BisonParamDeclaration;
15use SqlFaker\Compiler\Bison\Ast\BisonPrecedenceDeclaration;
16use SqlFaker\Compiler\Bison\Ast\BisonStartDeclaration;
17use SqlFaker\Compiler\Bison\Ast\BisonTokenDeclaration;
18use SqlFaker\Compiler\Bison\Ast\BisonTokenDefinition;
19use SqlFaker\Compiler\Bison\Ast\BisonTypeDeclaration;
20use SqlFaker\Compiler\Bison\Ast\BisonUnknownDeclaration;
21use SqlFaker\Compiler\Bison\Directive\BisonDeclarationBoundary;
22use SqlFaker\Compiler\Bison\Directive\BisonDirectiveReaderChain;
23use SqlFaker\Compiler\Bison\Directive\DefineDirectiveReader;
24use SqlFaker\Compiler\Bison\Directive\ExpectDirectiveReader;
25use SqlFaker\Compiler\Bison\Directive\ParamDirectiveReader;
26use SqlFaker\Compiler\Bison\Directive\PrecedenceDirectiveReader;
27use SqlFaker\Compiler\Bison\Directive\StartDirectiveReader;
28use SqlFaker\Compiler\Bison\Directive\TokenDirectiveReader;
29use SqlFaker\Compiler\Bison\Directive\TypeDirectiveReader;
30use SqlFaker\Compiler\Bison\Directive\UnknownDirectiveReader;
31use SqlFaker\Compiler\Bison\Lexer\ActionScanner;
32use SqlFaker\Compiler\Bison\Lexer\BisonLexeme;
33use SqlFaker\Compiler\Bison\Lexer\BisonLexer;
34use SqlFaker\Compiler\Bison\Lexer\BisonScannerChain;
35use SqlFaker\Compiler\Bison\Lexer\BisonToken;
36use SqlFaker\Compiler\Bison\Lexer\BisonTokenStream;
37use SqlFaker\Compiler\Bison\Lexer\BisonTrivia;
38use SqlFaker\Compiler\Bison\Lexer\DirectiveScanner;
39use SqlFaker\Compiler\Bison\Lexer\IdentifierScanner;
40use SqlFaker\Compiler\Bison\Lexer\NumberScanner;
41use SqlFaker\Compiler\Bison\Lexer\QuotedLiteralScanner;
42use SqlFaker\Compiler\Bison\Lexer\SourceCursor;
43use SqlFaker\Compiler\Bison\Lexer\TypeTagScanner;
44
45#[CoversClass(BisonDirectiveReaderChain::class)]
46#[UsesClass(ActionScanner::class)]
47#[UsesClass(BisonDeclarationBoundary::class)]
48#[UsesClass(BisonDefineDeclaration::class)]
49#[UsesClass(BisonExpectDeclaration::class)]
50#[UsesClass(BisonLexeme::class)]
51#[UsesClass(BisonLexer::class)]
52#[UsesClass(BisonParamDeclaration::class)]
53#[UsesClass(BisonPrecedenceDeclaration::class)]
54#[UsesClass(BisonScannerChain::class)]
55#[UsesClass(BisonStartDeclaration::class)]
56#[UsesClass(BisonToken::class)]
57#[UsesClass(BisonTokenDeclaration::class)]
58#[UsesClass(BisonTokenDefinition::class)]
59#[UsesClass(BisonTokenStream::class)]
60#[UsesClass(BisonTrivia::class)]
61#[UsesClass(BisonTypeDeclaration::class)]
62#[UsesClass(BisonUnknownDeclaration::class)]
63#[UsesClass(DefineDirectiveReader::class)]
64#[UsesClass(ExpectDirectiveReader::class)]
65#[UsesClass(ParamDirectiveReader::class)]
66#[UsesClass(PrecedenceDirectiveReader::class)]
67#[UsesClass(SourceCursor::class)]
68#[UsesClass(StartDirectiveReader::class)]
69#[UsesClass(TokenDirectiveReader::class)]
70#[UsesClass(TypeDirectiveReader::class)]
71#[UsesClass(UnknownDirectiveReader::class)]
72#[UsesClass(DirectiveScanner::class)]
73#[UsesClass(IdentifierScanner::class)]
74#[UsesClass(NumberScanner::class)]
75#[UsesClass(QuotedLiteralScanner::class)]
76#[UsesClass(TypeTagScanner::class)]
77#[UsesClass(\SqlFaker\Compiler\Bison\Lexer\PunctuationScanner::class)]
78#[UsesClass(\SqlFaker\Compiler\GrammarParseException::class)]
79final class BisonDirectiveReaderChainTest extends TestCase
80{
81 /**
82 * @param class-string<BisonDeclaration> $expected
83 */
84 #[DataProvider('providerDirective')]
85 public function testReadRoutesEachDirectiveToTheReaderThatKnowsIt(
86 string $directive,
87 string $arguments,
88 string $expected,
89 ): void {
90 $declaration = BisonDirectiveReaderChain::forBisonGrammar()
91 ->read(BisonTokenStream::over($arguments), $directive);
92
93 self::assertInstanceOf($expected, $declaration);
94 }
95
96 /**
97 * @return iterable<string, array{string, string, class-string<BisonDeclaration>}>
98 */
99 public static function providerDirective(): iterable
100 {
101 yield 'start' => ['%start', 'statement', BisonStartDeclaration::class];
102 yield 'token' => ['%token', 'IDENT', BisonTokenDeclaration::class];
103 yield 'type' => ['%type', '<num> expr', BisonTypeDeclaration::class];
104 yield 'left' => ['%left', 'OR_SYM', BisonPrecedenceDeclaration::class];
105 yield 'right' => ['%right', 'NOT_SYM', BisonPrecedenceDeclaration::class];
106 yield 'parse-param' => ['%parse-param', '{ THD *thd }', BisonParamDeclaration::class];
107 yield 'expect' => ['%expect', '3', BisonExpectDeclaration::class];
108 yield 'define' => ['%define', 'api.pure', BisonDefineDeclaration::class];
109 yield 'anything else' => ['%glr-parser', '', BisonUnknownDeclaration::class];
110 }
111
112 public function testReadFallsBackToTheUnknownReaderRatherThanDroppingADirective(): void
113 {
114 $declaration = BisonDirectiveReaderChain::forBisonGrammar()
115 ->read(BisonTokenStream::over('a b'), '%no-such-directive');
116
117 self::assertInstanceOf(BisonUnknownDeclaration::class, $declaration);
118 self::assertSame('a b', $declaration->content);
119 }
120
121 public function testReadDeclaresNothingWhenNoReaderClaimsTheDirective(): void
122 {
123 self::assertNull(
124 (new BisonDirectiveReaderChain([]))->read(BisonTokenStream::over('x'), '%start'),
125 );
126 }
127
128 public function testForBisonGrammarConsultsTheUnknownReaderLast(): void
129 {
130 $declaration = BisonDirectiveReaderChain::forBisonGrammar()
131 ->read(BisonTokenStream::over('statement'), '%start');
132
133 self::assertInstanceOf(BisonStartDeclaration::class, $declaration);
134 }
135}
136