Skip to content

Commit 83ba597

Browse files
authored
Refactor RegexGroupParser for more immutability and less pass-by-ref
1 parent a9ec512 commit 83ba597

File tree

2 files changed

+124
-31
lines changed

2 files changed

+124
-31
lines changed

src/Type/Regex/RegexAstWalkResult.php

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Regex;
4+
5+
/** @immutable */
6+
final class RegexAstWalkResult
7+
{
8+
9+
/**
10+
* @param array<int, RegexCapturingGroup> $capturingGroups
11+
* @param list<string> $markVerbs
12+
*/
13+
public function __construct(
14+
private int $alternationId,
15+
private int $captureGroupId,
16+
private array $capturingGroups,
17+
private array $markVerbs,
18+
)
19+
{
20+
}
21+
22+
public static function createEmpty(): self
23+
{
24+
return new self(
25+
-1,
26+
// use different start-index for groups to make it easier to distinguish groupids from other ids
27+
100,
28+
[],
29+
[],
30+
);
31+
}
32+
33+
public function nextAlternationId(): self
34+
{
35+
return new self(
36+
$this->alternationId + 1,
37+
$this->captureGroupId,
38+
$this->capturingGroups,
39+
$this->markVerbs,
40+
);
41+
}
42+
43+
public function nextCaptureGroupId(): self
44+
{
45+
return new self(
46+
$this->alternationId,
47+
$this->captureGroupId + 1,
48+
$this->capturingGroups,
49+
$this->markVerbs,
50+
);
51+
}
52+
53+
public function addCapturingGroup(RegexCapturingGroup $group): self
54+
{
55+
$capturingGroups = $this->capturingGroups;
56+
$capturingGroups[$group->getId()] = $group;
57+
58+
return new self(
59+
$this->alternationId,
60+
$this->captureGroupId,
61+
$capturingGroups,
62+
$this->markVerbs,
63+
);
64+
}
65+
66+
public function markVerb(string $markVerb): self
67+
{
68+
$verbs = $this->markVerbs;
69+
$verbs[] = $markVerb;
70+
71+
return new self(
72+
$this->alternationId,
73+
$this->captureGroupId,
74+
$this->capturingGroups,
75+
$verbs,
76+
);
77+
}
78+
79+
public function getAlternationId(): int
80+
{
81+
return $this->alternationId;
82+
}
83+
84+
public function getCaptureGroupId(): int
85+
{
86+
return $this->captureGroupId;
87+
}
88+
89+
/**
90+
* @return array<int, RegexCapturingGroup>
91+
*/
92+
public function getCapturingGroups(): array
93+
{
94+
return $this->capturingGroups;
95+
}
96+
97+
/**
98+
* @return list<string>
99+
*/
100+
public function getMarkVerbs(): array
101+
{
102+
return $this->markVerbs;
103+
}
104+
105+
}

src/Type/Regex/RegexGroupParser.php

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -73,51 +73,39 @@ public function parseGroups(string $regex): ?array
7373
$captureOnlyNamed = str_contains($modifiers, 'n');
7474
}
7575

76-
$capturingGroups = [];
77-
$alternationId = -1;
78-
$captureGroupId = 100;
79-
$markVerbs = [];
80-
$this->walkRegexAst(
76+
$astWalkResult = $this->walkRegexAst(
8177
$ast,
8278
null,
83-
$alternationId,
8479
0,
8580
false,
8681
null,
87-
$captureGroupId,
88-
$capturingGroups,
89-
$markVerbs,
9082
$captureOnlyNamed,
9183
false,
9284
$modifiers,
85+
RegexAstWalkResult::createEmpty(),
9386
);
9487

95-
return [$capturingGroups, $markVerbs];
88+
return [$astWalkResult->getCapturingGroups(), $astWalkResult->getMarkVerbs()];
9689
}
9790

98-
/**
99-
* @param array<int, RegexCapturingGroup> $capturingGroups
100-
* @param list<string> $markVerbs
101-
*/
10291
private function walkRegexAst(
10392
TreeNode $ast,
10493
?RegexAlternation $alternation,
105-
int &$alternationId,
10694
int $combinationIndex,
10795
bool $inOptionalQuantification,
10896
RegexCapturingGroup|RegexNonCapturingGroup|null $parentGroup,
109-
int &$captureGroupId,
110-
array &$capturingGroups,
111-
array &$markVerbs,
11297
bool $captureOnlyNamed,
11398
bool $repeatedMoreThanOnce,
11499
string $patternModifiers,
115-
): void
100+
RegexAstWalkResult $astWalkResult,
101+
): RegexAstWalkResult
116102
{
117103
$group = null;
118104
if ($ast->getId() === '#capturing') {
105+
$astWalkResult = $astWalkResult->nextCaptureGroupId();
106+
119107
$group = new RegexCapturingGroup(
120-
$captureGroupId++,
108+
$astWalkResult->getCaptureGroupId(),
121109
null,
122110
$alternation,
123111
$inOptionalQuantification,
@@ -130,9 +118,11 @@ private function walkRegexAst(
130118
);
131119
$parentGroup = $group;
132120
} elseif ($ast->getId() === '#namedcapturing') {
121+
$astWalkResult = $astWalkResult->nextCaptureGroupId();
122+
133123
$name = $ast->getChild(0)->getValueValue();
134124
$group = new RegexCapturingGroup(
135-
$captureGroupId++,
125+
$astWalkResult->getCaptureGroupId(),
136126
$name,
137127
$alternation,
138128
$inOptionalQuantification,
@@ -176,40 +166,36 @@ private function walkRegexAst(
176166
}
177167

178168
if ($ast->getId() === '#alternation') {
179-
$alternationId++;
180-
$alternation = new RegexAlternation($alternationId, count($ast->getChildren()));
169+
$astWalkResult = $astWalkResult->nextAlternationId();
170+
$alternation = new RegexAlternation($astWalkResult->getAlternationId(), count($ast->getChildren()));
181171
}
182172

183173
if ($ast->getId() === '#mark') {
184-
$markVerbs[] = $ast->getChild(0)->getValueValue();
185-
return;
174+
return $astWalkResult->markVerb($ast->getChild(0)->getValueValue());
186175
}
187176

188177
if (
189178
$group instanceof RegexCapturingGroup &&
190179
(!$captureOnlyNamed || $group->isNamed())
191180
) {
192-
$capturingGroups[$group->getId()] = $group;
181+
$astWalkResult = $astWalkResult->addCapturingGroup($group);
193182

194183
if ($alternation !== null) {
195184
$alternation->pushGroup($combinationIndex, $group);
196185
}
197186
}
198187

199188
foreach ($ast->getChildren() as $child) {
200-
$this->walkRegexAst(
189+
$astWalkResult = $this->walkRegexAst(
201190
$child,
202191
$alternation,
203-
$alternationId,
204192
$combinationIndex,
205193
$inOptionalQuantification,
206194
$parentGroup,
207-
$captureGroupId,
208-
$capturingGroups,
209-
$markVerbs,
210195
$captureOnlyNamed,
211196
$repeatedMoreThanOnce,
212197
$patternModifiers,
198+
$astWalkResult,
213199
);
214200

215201
if ($ast->getId() !== '#alternation') {
@@ -218,6 +204,8 @@ private function walkRegexAst(
218204

219205
$combinationIndex++;
220206
}
207+
208+
return $astWalkResult;
221209
}
222210

223211
private function allowConstantTypes(

0 commit comments

Comments
 (0)