packages/sql-formatter/src/Core/Compact/Grouping.php

1<?php
2
3declare(strict_types=1);
4
5namespace SqlFormatter\Core\Compact;
6
7use SqlParser\Lexer\Token;
8use SqlParser\Parser\Node;
9
10/**
11 * Distinguishes expression grouping from row constructors, subqueries, and argument lists.
12 *
13 * @visibility SqlFormatter
14 */
15final class Grouping
16{
17    /**
18     * @param array<string, list<string>> $rules Parenthesis owner to expression children
19     */
20    public function __construct(private readonly array $rules)
21    {
22    }
23
24    /**
25     * Returns expression parentheses from the innermost group outward.
26     *
27     * @return list<array{int, int}>
28     */
29    public function pairs(Node $tree): array
30    {
31        $pairs = [];
32        foreach ($tree->children as $child) {
33            if ($child instanceof Node) {
34                array_push($pairs, ...$this->pairs($child));
35            }
36        }
37        $children = array_values(array_filter($tree->children, static fn (Node|Token $child): bool => $child instanceof Token || !$child->isEmpty()));
38        if (count($children) === 3 && isset($this->rules[$tree->name])
39            && $children[0] instanceof Token && $children[0]->text === '('
40            && $children[1] instanceof Node && in_array($children[1]->name, $this->rules[$tree->name], true)
41            && $children[2] instanceof Token && $children[2]->text === ')') {
42            $pairs[] = [$children[0]->offset, $children[2]->offset];
43        }
44        return $pairs;
45    }
46}
47