packages/sql-faker/tests/Unit/Compiler/Bison/Rule/BisonRuleReaderTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\Compiler\Bison\Rule;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use SqlFaker\Compiler\Bison\Ast\BisonAlternativeNode;
11use SqlFaker\Compiler\Bison\Ast\BisonRuleNode;
12use SqlFaker\Compiler\Bison\Ast\BisonSymbolNode;
13use SqlFaker\Compiler\Bison\Lexer\ActionScanner;
14use SqlFaker\Compiler\Bison\Lexer\BisonLexeme;
15use SqlFaker\Compiler\Bison\Lexer\BisonLexer;
16use SqlFaker\Compiler\Bison\Lexer\BisonScannerChain;
17use SqlFaker\Compiler\Bison\Lexer\BisonToken;
18use SqlFaker\Compiler\Bison\Lexer\BisonTokenStream;
19use SqlFaker\Compiler\Bison\Lexer\BisonTrivia;
20use SqlFaker\Compiler\Bison\Lexer\DirectiveScanner;
21use SqlFaker\Compiler\Bison\Lexer\IdentifierScanner;
22use SqlFaker\Compiler\Bison\Lexer\NumberScanner;
23use SqlFaker\Compiler\Bison\Lexer\PunctuationScanner;
24use SqlFaker\Compiler\Bison\Lexer\QuotedLiteralScanner;
25use SqlFaker\Compiler\Bison\Lexer\SourceCursor;
26use SqlFaker\Compiler\Bison\Lexer\TypeTagScanner;
27use SqlFaker\Compiler\Bison\Rule\BisonAlternativeDraft;
28use SqlFaker\Compiler\Bison\Rule\BisonAlternativeReader;
29use SqlFaker\Compiler\Bison\Rule\BisonRuleReader;
30
31#[CoversClass(BisonRuleReader::class)]
32#[UsesClass(ActionScanner::class)]
33#[UsesClass(DirectiveScanner::class)]
34#[UsesClass(IdentifierScanner::class)]
35#[UsesClass(NumberScanner::class)]
36#[UsesClass(QuotedLiteralScanner::class)]
37#[UsesClass(TypeTagScanner::class)]
38#[UsesClass(PunctuationScanner::class)]
39#[UsesClass(BisonAlternativeDraft::class)]
40#[UsesClass(BisonAlternativeNode::class)]
41#[UsesClass(BisonAlternativeReader::class)]
42#[UsesClass(BisonLexeme::class)]
43#[UsesClass(BisonLexer::class)]
44#[UsesClass(BisonRuleNode::class)]
45#[UsesClass(BisonScannerChain::class)]
46#[UsesClass(BisonSymbolNode::class)]
47#[UsesClass(BisonToken::class)]
48#[UsesClass(BisonTokenStream::class)]
49#[UsesClass(BisonTrivia::class)]
50#[UsesClass(SourceCursor::class)]
51#[UsesClass(\SqlFaker\Compiler\Bison\Ast\BisonSymbolForm::class)]
52#[UsesClass(\SqlFaker\Compiler\GrammarParseException::class)]
53final class BisonRuleReaderTest extends TestCase
54{
55 public function testReadAllKeepsTheRulesInTheOrderTheyWereDeclared(): void
56 {
57 $rules = (new BisonRuleReader())->readAll(BisonTokenStream::over('a : x ; b : y ; c : z ;'));
58
59 self::assertSame(
60 ['a', 'b', 'c'],
61 array_map(static fn (BisonRuleNode $rule): string => $rule->name, $rules),
62 );
63 }
64
65 public function testReadAllStopsAtTheSectionSeparator(): void
66 {
67 $stream = BisonTokenStream::over('a : x ; %% epilogue');
68
69 $rules = (new BisonRuleReader())->readAll($stream);
70
71 self::assertCount(1, $rules);
72 self::assertSame('epilogue', $stream->next()->value);
73 }
74
75 public function testReadAllFindsNoRulesInAnEmptySection(): void
76 {
77 self::assertSame([], (new BisonRuleReader())->readAll(BisonTokenStream::over('')));
78 }
79
80 public function testReadAllSkipsWhatDoesNotOpenARule(): void
81 {
82 $rules = (new BisonRuleReader())->readAll(BisonTokenStream::over('42 ; a : x ;'));
83
84 self::assertCount(1, $rules);
85 self::assertSame('a', $rules[0]->name);
86 }
87
88 public function testReadTakesTheRuleNameAndItsAlternatives(): void
89 {
90 $rule = (new BisonRuleReader())->read(BisonTokenStream::over('expr : a | b ;'));
91
92 self::assertInstanceOf(BisonRuleNode::class, $rule);
93 self::assertSame('expr', $rule->name);
94 self::assertCount(2, $rule->alternatives);
95 }
96
97 public function testReadFindsNoRuleWhenNoColonFollowsTheName(): void
98 {
99 self::assertNull((new BisonRuleReader())->read(BisonTokenStream::over('expr expr')));
100 }
101
102 public function testReadConsumesTheNameItRejectedSoTheScanCanAdvance(): void
103 {
104 $stream = BisonTokenStream::over('expr next : a ;');
105
106 (new BisonRuleReader())->read($stream);
107
108 self::assertSame('next', $stream->next()->value);
109 }
110}
111