Skip to content

Commit 30861ca

Browse files
committed
regex variable %+ wip
1 parent 67cf857 commit 30861ca

File tree

2 files changed

+109
-0
lines changed

2 files changed

+109
-0
lines changed

misc/test/regex_named_capture.pl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/perl
2+
use strict;
3+
use warnings;
4+
use Test::More tests => 4;
5+
6+
# Test case 1: Simple named capture
7+
my $string1 = 'foo';
8+
if ($string1 =~ /(?<foo>foo)/) {
9+
is($+{foo}, 'foo', 'Test case 1: Named capture for "foo"');
10+
} else {
11+
fail('Test case 1: Pattern did not match');
12+
}
13+
14+
# Test case 2: Multiple named captures
15+
my $string2 = 'barbaz';
16+
if ($string2 =~ /(?<bar>bar)(?<baz>baz)/) {
17+
is($+{bar}, 'bar', 'Test case 2: Named capture for "bar"');
18+
is($+{baz}, 'baz', 'Test case 2: Named capture for "baz"');
19+
} else {
20+
fail('Test case 2: Pattern did not match');
21+
}
22+
23+
# Test case 3: Overlapping named captures
24+
my $string3 = 'foobar';
25+
if ($string3 =~ /(?<foo>foo)(?<bar>bar)|(?<foo>foobar)/) {
26+
is($+{foo}, 'foo', 'Test case 3: Overlapping named capture for "foo"');
27+
} else {
28+
fail('Test case 3: Pattern did not match');
29+
}
30+
31+
done_testing();
32+
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package org.perlonjava.runtime;
2+
3+
import java.util.AbstractMap;
4+
import java.util.HashMap;
5+
import java.util.HashSet;
6+
import java.util.Map;
7+
import java.util.Set;
8+
import java.util.regex.Matcher;
9+
import java.util.regex.Pattern;
10+
import java.util.regex.PatternSyntaxException;
11+
12+
import static org.perlonjava.runtime.RuntimeScalarCache.scalarUndef;
13+
14+
/**
15+
* HashSpecialVariable provides a dynamic view over named capturing groups
16+
* in a Matcher object, reflecting the current state of the Matcher.
17+
* This implements the Perl special variable %+.
18+
*/
19+
public class HashSpecialVariable extends AbstractMap<String, RuntimeScalar> {
20+
21+
private final Matcher matcher;
22+
private final Map<String, Integer> namedGroups;
23+
24+
/**
25+
* Constructs a HashSpecialVariable for the given Matcher.
26+
*
27+
* @param matcher the Matcher object to query for named capturing groups
28+
*/
29+
public HashSpecialVariable(Matcher matcher) {
30+
this.matcher = matcher;
31+
this.namedGroups = extractNamedGroups(matcher.pattern());
32+
}
33+
34+
/**
35+
* Extracts named groups and their indices from the given pattern.
36+
*
37+
* @param pattern the regex pattern
38+
* @return a map of named group names to their indices
39+
*/
40+
private Map<String, Integer> extractNamedGroups(Pattern pattern) {
41+
Map<String, Integer> namedGroups = new HashMap<>();
42+
String regex = pattern.toString();
43+
Matcher groupMatcher = Pattern.compile("\\(\\?<([a-zA-Z][a-zA-Z0-9]*)>").matcher(regex);
44+
int index = 1; // Group indices start at 1
45+
46+
while (groupMatcher.find()) {
47+
String groupName = groupMatcher.group(1);
48+
namedGroups.put(groupName, index++);
49+
}
50+
51+
return namedGroups;
52+
}
53+
54+
@Override
55+
public Set<Entry<String, RuntimeScalar>> entrySet() {
56+
Set<Entry<String, RuntimeScalar>> entries = new HashSet<>();
57+
for (String name : namedGroups.keySet()) {
58+
int groupIndex = namedGroups.get(name);
59+
if (groupIndex != -1 && matcher.group(name) != null) {
60+
entries.add(new SimpleEntry<>(name, new RuntimeScalar(matcher.group(name))));
61+
}
62+
}
63+
return entries;
64+
}
65+
66+
@Override
67+
public RuntimeScalar get(Object key) {
68+
if (key instanceof String) {
69+
String name = (String) key;
70+
int groupIndex = namedGroups.getOrDefault(name, -1);
71+
if (groupIndex != -1 && matcher.group(groupIndex) != null) {
72+
return new RuntimeScalar(matcher.group(groupIndex));
73+
}
74+
}
75+
return scalarUndef;
76+
}
77+
}

0 commit comments

Comments
 (0)