Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions core/src/main/java/org/jruby/RubyArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import java.util.ListIterator;
import java.util.RandomAccess;
import java.util.Stack;
import java.util.stream.Stream;

import org.jcodings.specific.USASCIIEncoding;
import org.jruby.anno.JRubyClass;
Expand Down Expand Up @@ -5962,4 +5963,12 @@ public IRubyObject sample(ThreadContext context, IRubyObject[] args) {
throw context.runtime.newArgumentError(args.length, 0, 1);
}
}

/**
* Returns a stream of each IRubyObject
* @return
*/
public Stream<IRubyObject> rubyStream() {
return Stream.iterate(0, i -> i + 1).limit(realLength).map(this::eltInternal);
}
}
141 changes: 100 additions & 41 deletions core/src/main/java/org/jruby/RubyMatchData.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@

import java.util.Arrays;
import java.util.Iterator;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.jcodings.Encoding;
import org.joni.Matcher;
Expand All @@ -59,6 +61,8 @@
import org.jruby.util.RubyStringBuilder;
import org.jruby.util.StringSupport;

import static org.jruby.util.RubyStringBuilder.str;

/**
* @author olabini
*/
Expand Down Expand Up @@ -301,27 +305,26 @@ private RubyRegexp getRegexp() {
return this.regexp = RubyRegexp.newRegexp(metaClass.runtime, (ByteList) pattern.getUserObject(), pattern);
}

private RubyArray match_array(Ruby runtime, int start) {
private RubyArray match_array(ThreadContext context, int start) {
Ruby runtime = context.runtime;
check();
IRubyObject nil = runtime.getNil();

if (regs == null) {
if (start != 0) return runtime.newEmptyArray();
if (begin == -1) {
return runtime.newArray(nil);
return runtime.newArray(context.nil);
} else {
RubyString ss = str.makeSharedString(runtime, begin, end - begin);
return runtime.newArray(ss);
}
} else {
RubyArray arr = RubyArray.newBlankArray(runtime, regs.getNumRegs() - start);
int index = 0;
for (int i=start; i < regs.getNumRegs(); i++) {
if (regs.getBeg(i) == -1) {
arr.storeInternal(index++, nil);
} else {
RubyString ss = str.makeSharedString(runtime, regs.getBeg(i), regs.getEnd(i) - regs.getBeg(i));
arr.storeInternal(index++, ss);
}
int count = regs.getNumRegs() - start;
RubyArray arr = RubyArray.newBlankArray(runtime, count);
for (int i=0; i < count; i++) {
int beg = regs.getBeg(i+start);
arr.storeInternal(i, beg == -1 ?
context.nil :
str.makeSharedString(runtime, beg, regs.getEnd(i+start) - beg));
}
return arr;
}
Expand Down Expand Up @@ -439,7 +442,7 @@ public IRubyObject names(ThreadContext context, Block block) {
@JRubyMethod
@Override
public RubyArray to_a(ThreadContext context) {
return match_array(context.runtime, 0);
return match_array(context, 0);
}

@JRubyMethod(rest = true)
Expand Down Expand Up @@ -474,7 +477,7 @@ public IRubyObject values_at(IRubyObject[] args) {
*/
@JRubyMethod
public IRubyObject captures(ThreadContext context) {
return match_array(context.runtime, 1);
return match_array(context, 1);
}

private int nameToBackrefNumber(RubyString str) {
Expand All @@ -489,13 +492,21 @@ private static int nameToBackrefNumber(Ruby runtime, Regex pattern, Region regs,
return pattern.nameToBackrefNumber(value.getUnsafeBytes(), value.getBegin(), value.getBegin() + value.getRealSize(), regs);
} catch (JOniException je) {
if (je instanceof ValueException) {
throw runtime.newIndexError(RubyStringBuilder.str(runtime, "undefined group name reference: ", runtime.newString(value)));
throw runtime.newIndexError(str(runtime, "undefined group name reference: ", runtime.newString(value)));
}
// FIXME: I think we could only catch ValueException here, but someone needs to audit that.
throw runtime.newIndexError(je.getMessage());
}
}

private static int nameToBackrefNumber(Regex pattern, Region regs, ByteList name) {
try {
return pattern.nameToBackrefNumber(name.getUnsafeBytes(), name.getBegin(), name.getBegin() + name.getRealSize(), regs);
} catch (JOniException je) {
return -1;
}
}

public final int backrefNumber(Ruby runtime, IRubyObject obj) {
check();
return backrefNumber(runtime, getPattern(), regs, obj);
Expand Down Expand Up @@ -823,11 +834,8 @@ public IRubyObject initialize_copy(IRubyObject original) {

checkFrozen();

if (!(original instanceof RubyMatchData)) {
throw getRuntime().newTypeError("wrong argument class");
}
if (!(original instanceof RubyMatchData origMatchData)) throw getRuntime().newTypeError("wrong argument class");

RubyMatchData origMatchData = (RubyMatchData)original;
str = origMatchData.str;
regs = origMatchData.regs;

Expand All @@ -837,9 +845,7 @@ public IRubyObject initialize_copy(IRubyObject original) {
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (!(other instanceof RubyMatchData)) return false;

final RubyMatchData that = (RubyMatchData) other;
if (!(other instanceof RubyMatchData that)) return false;

return (this.str == that.str || (this.str != null && this.str.equals(that.str))) &&
(this.regexp == that.regexp || (this.getRegexp().equals(that.getRegexp()))) &&
Expand Down Expand Up @@ -875,31 +881,24 @@ public RubyHash named_captures(ThreadContext context, IRubyObject[] args) {
RubyHash hash = RubyHash.newSmallHash(runtime);
if (regexp == context.nil) return hash;

boolean symbolizeNames = false;
final boolean symbolizeNames;
if (argc == 1) {
final IRubyObject opts = args[0];
if (opts instanceof RubyHash) {
symbolizeNames = ArgsUtil.extractKeywordArg(context, (RubyHash) opts, "symbolize_names").isTrue();
} else {
throw runtime.newArgumentError(1, 0);
}
if (!(args[0] instanceof RubyHash)) throw runtime.newArgumentError(1, 0);
IRubyObject value = ArgsUtil.extractKeywordArg(context, (RubyHash) args[0], "symbolize_names");
symbolizeNames = value != null && value.isTrue();
} else {
symbolizeNames = false;
}

for (Iterator<NameEntry> i = getPattern().namedBackrefIterator(); i.hasNext();) {
NameEntry entry = i.next();
IRubyObject key;
if (symbolizeNames) {
key = runtime.newSymbol(new ByteList(entry.name, entry.nameP, entry.nameEnd - entry.nameP, regexp.getEncoding(), false));
} else {
key = RubyString.newStringShared(runtime, new ByteList(entry.name, entry.nameP, entry.nameEnd - entry.nameP, regexp.getEncoding(), false));
}

getNamedBackrefKeys(context).forEach(entry -> {
IRubyObject key = symbolizeNames ?
symbolFromNameEntry(context, entry) : stringFromNameEntry(context, entry);
boolean found = false;

for (int b : entry.getBackRefs()) {
IRubyObject value = RubyRegexp.nth_match(b, this);
if (value.isTrue()) {

if (value.isTrue()) {
hash.op_aset(context, key, value);
found = true;
}
Expand All @@ -908,11 +907,71 @@ public RubyHash named_captures(ThreadContext context, IRubyObject[] args) {
if (!found) {
hash.op_aset(context, key, context.nil);
}
}
});

return hash;
}

@JRubyMethod
public IRubyObject deconstruct(ThreadContext context) {
return match_array(context, 1);
}

@JRubyMethod
public IRubyObject deconstruct_keys(ThreadContext context, IRubyObject what) {
Ruby runtime = context.runtime;
RubyHash hash = RubyHash.newSmallHash(runtime);

if (what.isNil()) {
getNamedBackrefKeys(context).forEach(entry -> {
RubySymbol key = symbolFromNameEntry(context, entry);

for (int b : entry.getBackRefs()) {
IRubyObject value = RubyRegexp.nth_match(b, this);
if (value.isTrue()) hash.op_aset(context, key, value);
}
});
} else if (what instanceof RubyArray arr) {
if (getPattern().numberOfNames() < arr.size()) return hash;

Iterable<IRubyObject> iterable = () -> arr.rubyStream().iterator();
for (IRubyObject obj : iterable) {
if (!(obj instanceof RubySymbol)) {
throw runtime.newTypeError(str(runtime, "wrong argument type ", obj.getMetaClass(), " (expected Symbol)"));
}
RubySymbol requestedKey = (RubySymbol) obj;

int index = nameToBackrefNumber(getPattern(), regs, requestedKey.getBytes());
if (index == -1) break;

IRubyObject value = RubyRegexp.nth_match(index, this);
if (!value.isTrue()) break;

hash.op_aset(context, requestedKey, value);
}
} else {
throw context.runtime.newTypeError(str(runtime, "wrong argument type ", what.getMetaClass(), " (expected Array)"));
}
return hash;
}

private Stream<NameEntry> getNamedBackrefKeys(ThreadContext context) {
Iterable<NameEntry> iterable = () -> getPattern().namedBackrefIterator();
return StreamSupport.stream(iterable.spliterator(), false);
}

private static ByteList byteListFromNameEntry(ThreadContext context, NameEntry entry, Encoding encoding) {
return new ByteList(entry.name, entry.nameP, entry.nameEnd - entry.nameP, encoding, false);
}

private RubySymbol symbolFromNameEntry(ThreadContext context, NameEntry entry) {
return context.runtime.newSymbol(byteListFromNameEntry(context, entry, regexp.getEncoding()));
}

private RubyString stringFromNameEntry(ThreadContext context, NameEntry entry) {
return RubyString.newStringShared(context.runtime, byteListFromNameEntry(context, entry, regexp.getEncoding()));
}

/**
* Get the begin offset of the given region, or -1 if the region does not exist.
*
Expand Down Expand Up @@ -955,7 +1014,7 @@ public int numRegs() {
@Deprecated
@Override
public RubyArray to_a() {
return match_array(getRuntime(), 0);
return match_array(getRuntime().getCurrentContext(), 0);
}

@Deprecated
Expand Down