packages/sql-parser/tests/Unit/Automaton/ClosureIndexTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Automaton;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\Small;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlParser\Automaton\ClosureIndex;
12use SqlParser\Grammar\Grammar;
13use SqlParser\Grammar\GrammarBuilder;
14use SqlParser\Grammar\Rule;
15use SqlParser\Grammar\SymbolTable;
16
17#[CoversClass(ClosureIndex::class)]
18#[UsesClass(Grammar::class)]
19#[UsesClass(GrammarBuilder::class)]
20#[UsesClass(Rule::class)]
21#[UsesClass(SymbolTable::class)]
22#[Small]
23final class ClosureIndexTest extends TestCase
24{
25    public function testReachable(): void
26    {
27        $builder = new GrammarBuilder();
28        $builder->terminal('NUM');
29        $builder->terminal('(');
30        $builder->terminal(')');
31        $builder->rule('expr', ['term']);
32        $builder->rule('term', ['factor']);
33        $builder->rule('factor', ['NUM']);
34        $builder->rule('factor', ['(', 'expr', ')']);
35        $grammar = $builder->build();
36        $index = new ClosureIndex($grammar);
37        $symbols = $grammar->symbols;
38        $reached = $index->reachable($grammar, $symbols->id('expr') ?? -1);
39        sort($reached);
40
41        self::assertSame([$symbols->id('expr'), $symbols->id('term'), $symbols->id('factor')], $reached);
42        self::assertSame([$symbols->id('factor')], $index->reachable($grammar, $symbols->id('factor') ?? -1));
43    }
44
45    public function testStartsWith(): void
46    {
47        $builder = new GrammarBuilder();
48        $builder->terminal('NUM');
49        $builder->terminal('(');
50        $builder->terminal(')');
51        $builder->rule('expr', ['term']);
52        $builder->rule('term', ['NUM']);
53        $builder->rule('term', ['(', 'expr', ')']);
54        $grammar = $builder->build();
55        $index = new ClosureIndex($grammar);
56        $symbols = $grammar->symbols;
57        $width = $index->itemWidth;
58
59        self::assertSame(4, $width);
60        self::assertSame([$symbols->id('term') => [1 * $width + 1], $symbols->id('NUM') => [2 * $width + 1], $symbols->id('(') => [3 * $width + 1]], $index->startsWith($symbols->id('expr') ?? -1));
61        self::assertSame([], $index->startsWith(99));
62    }
63
64    public function testEmptyRules(): void
65    {
66        $builder = new GrammarBuilder();
67        $builder->terminal('A');
68        $builder->rule('s', ['opt', 'A']);
69        $builder->rule('opt', []);
70        $builder->rule('opt', ['A']);
71        $grammar = $builder->build();
72        $index = new ClosureIndex($grammar);
73
74        self::assertSame([2], $index->emptyRules($grammar->symbols->id('s') ?? -1));
75        self::assertSame([2], $index->emptyRules($grammar->symbols->id('opt') ?? -1));
76        self::assertSame([], $index->emptyRules(99));
77    }
78}
79