Skip to content

Commit

Permalink
regex variable %+ wip
Browse files Browse the repository at this point in the history
  • Loading branch information
fglock committed Oct 18, 2024
1 parent 67cf857 commit 30861ca
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 0 deletions.
32 changes: 32 additions & 0 deletions misc/test/regex_named_capture.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 4;

# Test case 1: Simple named capture
my $string1 = 'foo';
if ($string1 =~ /(?<foo>foo)/) {
is($+{foo}, 'foo', 'Test case 1: Named capture for "foo"');
} else {
fail('Test case 1: Pattern did not match');
}

# Test case 2: Multiple named captures
my $string2 = 'barbaz';
if ($string2 =~ /(?<bar>bar)(?<baz>baz)/) {
is($+{bar}, 'bar', 'Test case 2: Named capture for "bar"');
is($+{baz}, 'baz', 'Test case 2: Named capture for "baz"');
} else {
fail('Test case 2: Pattern did not match');
}

# Test case 3: Overlapping named captures
my $string3 = 'foobar';
if ($string3 =~ /(?<foo>foo)(?<bar>bar)|(?<foo>foobar)/) {
is($+{foo}, 'foo', 'Test case 3: Overlapping named capture for "foo"');
} else {
fail('Test case 3: Pattern did not match');
}

done_testing();

77 changes: 77 additions & 0 deletions src/main/java/org/perlonjava/runtime/HashSpecialVariable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.perlonjava.runtime;

import java.util.AbstractMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import static org.perlonjava.runtime.RuntimeScalarCache.scalarUndef;

/**
* HashSpecialVariable provides a dynamic view over named capturing groups
* in a Matcher object, reflecting the current state of the Matcher.
* This implements the Perl special variable %+.
*/
public class HashSpecialVariable extends AbstractMap<String, RuntimeScalar> {

private final Matcher matcher;
private final Map<String, Integer> namedGroups;

/**
* Constructs a HashSpecialVariable for the given Matcher.
*
* @param matcher the Matcher object to query for named capturing groups
*/
public HashSpecialVariable(Matcher matcher) {
this.matcher = matcher;
this.namedGroups = extractNamedGroups(matcher.pattern());
}

/**
* Extracts named groups and their indices from the given pattern.
*
* @param pattern the regex pattern
* @return a map of named group names to their indices
*/
private Map<String, Integer> extractNamedGroups(Pattern pattern) {
Map<String, Integer> namedGroups = new HashMap<>();
String regex = pattern.toString();
Matcher groupMatcher = Pattern.compile("\\(\\?<([a-zA-Z][a-zA-Z0-9]*)>").matcher(regex);
int index = 1; // Group indices start at 1

while (groupMatcher.find()) {
String groupName = groupMatcher.group(1);
namedGroups.put(groupName, index++);
}

return namedGroups;
}

@Override
public Set<Entry<String, RuntimeScalar>> entrySet() {
Set<Entry<String, RuntimeScalar>> entries = new HashSet<>();
for (String name : namedGroups.keySet()) {
int groupIndex = namedGroups.get(name);
if (groupIndex != -1 && matcher.group(name) != null) {
entries.add(new SimpleEntry<>(name, new RuntimeScalar(matcher.group(name))));
}
}
return entries;
}

@Override
public RuntimeScalar get(Object key) {
if (key instanceof String) {
String name = (String) key;
int groupIndex = namedGroups.getOrDefault(name, -1);
if (groupIndex != -1 && matcher.group(groupIndex) != null) {
return new RuntimeScalar(matcher.group(groupIndex));
}
}
return scalarUndef;
}
}

0 comments on commit 30861ca

Please sign in to comment.