diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java index e4eabf251c08f14a3b4c68cae04097fbc6f3fcd1..e8ba62ceb910f093217c88d5664abb3c49933115 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java @@ -5,7 +5,7 @@ * * Copyright (c) 1995-2015, The R Core Team * Copyright (c) 2003, The R Foundation - * Copyright (c) 2015, 2016, Oracle and/or its affiliates + * Copyright (c) 2015, 2017, Oracle and/or its affiliates * * All rights reserved. */ @@ -430,27 +430,52 @@ public class GrepFunctions { value = ix < 0 ? input : input.substring(0, ix) + replacement + input.substring(ix + pattern.length()); } } else if (perl) { - int offset = 0; + int lastEndOffset = 0; + int lastEndIndex = 0; int[] ovector = new int[30]; int nmatch = 0; int eflag = 0; int lastEnd = -1; + int[] fromByteMapping = getFromByteMapping(input); // non-null if it's + // necessary + StringBuffer sb = new StringBuffer(); - while (pcreRFFINode.exec(pcre.result, 0, input, offset, eflag, ovector) >= 0) { + while (pcreRFFINode.exec(pcre.result, 0, input, lastEndOffset, eflag, ovector) >= 0) { nmatch++; - for (int j = offset; j < ovector[0]; j++) { + + // offset == byte position + // index == character position + int startOffset = ovector[0]; + int endOffset = ovector[1]; + int startIndex = (fromByteMapping != null) ? fromByteMapping[startOffset] : startOffset; + int endIndex = (fromByteMapping != null) ? fromByteMapping[endOffset] : endOffset; + + for (int j = lastEndIndex; j < startIndex; j++) { sb.append(input.charAt(j)); } - if (ovector[1] > lastEnd) { - pcreStringAdj(sb, input, replacement, ovector); - lastEnd = ovector[1]; + if (endOffset > lastEnd) { + pcreStringAdj(sb, input, replacement, ovector, fromByteMapping); + lastEnd = endOffset; } - offset = ovector[1]; - if (offset >= input.length() || !gsub) { + lastEndIndex = endIndex; + lastEndOffset = endOffset; + if (lastEndIndex >= input.length() || !gsub) { break; } - if (ovector[0] == ovector[1]) { - sb.append(input.charAt(offset++)); + if (startOffset == endOffset) { + sb.append(input.charAt(lastEndIndex)); + if (fromByteMapping != null) { + for (int j = lastEndOffset + 1; j < fromByteMapping.length; j++) { + if (fromByteMapping[j] > 0) { + lastEndOffset = j; + lastEndIndex = fromByteMapping[lastEndOffset]; + break; + } + } + } else { + lastEndOffset++; + lastEndIndex++; + } } eflag |= PCRERFFI.NOTBOL; } @@ -458,7 +483,7 @@ public class GrepFunctions { value = input; } else { /* copy the tail */ - for (int j = offset; j < input.length(); j++) { + for (int j = lastEndIndex; j < input.length(); j++) { sb.append(input.charAt(j)); } value = sb.toString(); @@ -558,7 +583,7 @@ public class GrepFunctions { return nonEmpty; } - private static void pcreStringAdj(StringBuffer sb, String input, String repl, int[] ovector) { + private static void pcreStringAdj(StringBuffer sb, String input, String repl, int[] ovector, int[] fromByteMapping) { boolean upper = false; boolean lower = false; int px = 0; @@ -568,7 +593,9 @@ public class GrepFunctions { char p1 = repl.charAt(px++); if (p1 >= '1' && p1 <= '9') { int k = p1 - '0'; - for (int i = ovector[2 * k]; i < ovector[2 * k + 1]; i++) { + int startIndex = (fromByteMapping != null) ? fromByteMapping[ovector[2 * k]] : ovector[2 * k]; + int endIndex = (fromByteMapping != null) ? fromByteMapping[ovector[2 * k + 1]] : ovector[2 * k + 1]; + for (int i = startIndex; i < endIndex; i++) { char c = input.charAt(i); sb.append(upper ? Character.toUpperCase(c) : (lower ? Character.toLowerCase(c) : c)); } @@ -1203,7 +1230,7 @@ public class GrepFunctions { if (perl) { resultItem = splitPerl(data, pcreSplits[i % splits.length]); } else { - resultItem = splitIntl(data, currentSplit); + resultItem = splitIntl(data, currentSplit, fixed); } if (resultItem.getLength() == 0) { if (fixed) { @@ -1234,9 +1261,36 @@ public class GrepFunctions { } } - private static RStringVector splitIntl(String input, String separator) { + private static RStringVector splitIntl(String input, String separator, boolean fixed) { assert !RRuntime.isNA(input); - return RDataFactory.createStringVector(input.split(separator), true); + + if (fixed) { + ArrayList<String> matches = new ArrayList<>(); + int idx = input.indexOf(separator); + if (idx < 0) { + return RDataFactory.createStringVector(input); + } + int lastIdx = 0; + while (idx > -1) { + matches.add(input.substring(lastIdx, idx)); + lastIdx = idx + separator.length(); + if (lastIdx > input.length()) { + break; + } + idx = input.indexOf(separator, lastIdx); + } + String m = input.substring(lastIdx); + if (!m.isEmpty()) { + matches.add(m); + } + return RDataFactory.createStringVector(matches.toArray(new String[matches.size()]), false); + } else { + if (input.equals(separator)) { + return RDataFactory.createStringVector(""); + } else { + return RDataFactory.createStringVector(input.split(separator), true); + } + } } private static RStringVector emptySplitIntl(String input) { @@ -1274,56 +1328,56 @@ public class GrepFunctions { matches.toArray(result); return RDataFactory.createStringVector(result, RDataFactory.COMPLETE_VECTOR); } + } - private static int getByteLength(String data) { - int byteLength = 0; - int pos = 0; - while (pos < data.length()) { - char c = data.charAt(pos); - if (c < 128) { - byteLength++; - } else if (c < 2048) { - byteLength += 2; + private static int getByteLength(String data) { + int byteLength = 0; + int pos = 0; + while (pos < data.length()) { + char c = data.charAt(pos); + if (c < 128) { + byteLength++; + } else if (c < 2048) { + byteLength += 2; + } else { + if (Character.isHighSurrogate(c)) { + byteLength += 4; + pos++; } else { - if (Character.isHighSurrogate(c)) { - byteLength += 4; - pos++; - } else { - byteLength += 3; - } + byteLength += 3; } - pos++; } - return byteLength; + pos++; } + return byteLength; + } - private static int[] getFromByteMapping(String data) { - int byteLength = getByteLength(data); - if (byteLength == data.length()) { - return null; - } - int[] result = new int[byteLength + 1]; - byteLength = 0; - int pos = 0; - while (pos < data.length()) { - result[byteLength] = pos; - char c = data.charAt(pos); - if (c < 128) { - byteLength++; - } else if (c < 2048) { - byteLength += 2; + private static int[] getFromByteMapping(String data) { + int byteLength = getByteLength(data); + if (byteLength == data.length()) { + return null; + } + int[] result = new int[byteLength + 1]; + byteLength = 0; + int pos = 0; + while (pos < data.length()) { + result[byteLength] = pos; + char c = data.charAt(pos); + if (c < 128) { + byteLength++; + } else if (c < 2048) { + byteLength += 2; + } else { + if (Character.isHighSurrogate(c)) { + byteLength += 4; + pos++; } else { - if (Character.isHighSurrogate(c)) { - byteLength += 4; - pos++; - } else { - byteLength += 3; - } + byteLength += 3; } - pos++; } - result[byteLength] = pos; - return result; + pos++; } + result[byteLength] = pos; + return result; } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index d79ff606a4e607bca7bc573d37156de8ec40df4e..ffa7c051833decdef5b9261fb34927a0b770d61c 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -21888,6 +21888,10 @@ Error: invalid 'pattern' argument #{ .Internal(grep(character(), "7", F, F, F, F, F, F)) } Error: invalid 'pattern' argument +##com.oracle.truffle.r.test.builtins.TestBuiltin_grep.testGrep#Ignored.Unknown# +#{ grep('^ *$', ' \n') } +integer(0) + ##com.oracle.truffle.r.test.builtins.TestBuiltin_grep.testGrep# #{ txt<-c("1+1i", "7", "42.1", "7+42i"); grep("[0-9].*[-+][0-9].*i$", txt) } [1] 1 4 @@ -22114,6 +22118,18 @@ In gsub("a", "aa", "prAgue alley", fixed = TRUE, ignore.case = TRUE) : #{ gsub("h","", c("hello", "hi", "bye"), fixed=TRUE) } [1] "ello" "i" "bye" +##com.oracle.truffle.r.test.builtins.TestBuiltin_gsub.testGsub# +#{ gsub(pattern = 'a*', replacement = 'x', x = 'ÄaaaaÄ', perl = TRUE) } +[1] "xÄxÄx" + +##com.oracle.truffle.r.test.builtins.TestBuiltin_gsub.testGsub# +#{ gsub(pattern = 'a*', replacement = 'x', x = 'ÄaÄ', perl = TRUE) } +[1] "xÄxÄx" + +##com.oracle.truffle.r.test.builtins.TestBuiltin_gsub.testGsub#Ignored.Unknown# +#{ gsub(pattern = 'Ä*', replacement = 'x', x = 'aÄÄÄÄÄb', perl = TRUE) } +[1] "xaxbx" + ##com.oracle.truffle.r.test.builtins.TestBuiltin_gsub.testgsub1# #argv <- list('([[:alnum:]])--([[:alnum:]])', '\\1-\\2', 'Date-Time Classes', FALSE, FALSE, FALSE, FALSE); .Internal(gsub(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]], argv[[7]])) [1] "Date-Time Classes" @@ -48367,6 +48383,30 @@ character(0) #{ strrep(c("A", "B", "C"), 1 : 3) } [1] "A" "BB" "CCC" +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit('1', '1', fixed=FALSE) +[[1]] +[1] "" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit('1', '1', fixed=TRUE) +[[1]] +[1] "" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit('11', '11', fixed=FALSE) +[[1]] +[1] "" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit('11', '11', fixed=TRUE) +[[1]] +[1] "" + + ##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# #strsplit('foo bar baz', '[f z]', perl=TRUE) [[1]] @@ -48393,6 +48433,138 @@ character(0) [1] "Ä" "Ä" +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit('Ä', 'Ä', fixed=FALSE) +[[1]] +[1] "" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit('Ä', 'Ä', fixed=TRUE) +[[1]] +[1] "" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit('ÄÄ', 'Ä', fixed=FALSE) +[[1]] +[1] "" "" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit('ÄÄ', 'Ä', fixed=TRUE) +[[1]] +[1] "" "" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit(c('1', ''), c('1', ''), fixed=FALSE) +[[1]] +[1] "" + +[[2]] +character(0) + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit(c('1', ''), c('1', ''), fixed=TRUE) +[[1]] +[1] "" + +[[2]] +character(0) + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit(c('1', '11'), c('1', '11'), fixed=FALSE) +[[1]] +[1] "" + +[[2]] +[1] "" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit(c('1', '11'), c('1', '11'), fixed=TRUE) +[[1]] +[1] "" + +[[2]] +[1] "" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit(c('1', 'b'), c('1', 'b'), fixed=FALSE) +[[1]] +[1] "" + +[[2]] +[1] "" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit(c('1', 'b'), c('1', 'b'), fixed=TRUE) +[[1]] +[1] "" + +[[2]] +[1] "" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit(c('111', '1'), c('111', '1'), fixed=FALSE) +[[1]] +[1] "" + +[[2]] +[1] "" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit(c('111', '1'), c('111', '1'), fixed=TRUE) +[[1]] +[1] "" + +[[2]] +[1] "" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit(c('a1a', 'a1b'), '1', fixed=FALSE) +[[1]] +[1] "a" "a" + +[[2]] +[1] "a" "b" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit(c('a1a', 'a1b'), '1', fixed=TRUE) +[[1]] +[1] "a" "a" + +[[2]] +[1] "a" "b" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit(c('a1a', 'a1b'), c('1', '1'), fixed=FALSE) +[[1]] +[1] "a" "a" + +[[2]] +[1] "a" "b" + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# +#strsplit(c('a1a', 'a1b'), c('1', '1'), fixed=TRUE) +[[1]] +[1] "a" "a" + +[[2]] +[1] "a" "b" + + ##com.oracle.truffle.r.test.builtins.TestBuiltin_strsplit.testStrSplit# #{ .Internal(strsplit("7", 42, F, F, F)) } Error: non-character argument @@ -48764,6 +48936,10 @@ Error: invalid 'pattern' argument #{ sub('[[:space:]]+$', '', 'R (>= 3.0.3) ') } [1] "R (>= 3.0.3)" +##com.oracle.truffle.r.test.builtins.TestBuiltin_sub.testSub# +#{ sub('\\s*$', '', 'Ä', perl=TRUE) } +[1] "Ä" + ##com.oracle.truffle.r.test.builtins.TestBuiltin_sub.testSub# #{ sub('^([1[:alpha:].]+).*$', '\\1', '1R.ff (>= 3.0.3)') } [1] "1R.ff" @@ -48824,6 +49000,18 @@ Error: invalid 'pattern' argument #{ sub('^[[:space:]]*(.*)', '\\1', 'R (>= 3.0.3)') } [1] "R (>= 3.0.3)" +##com.oracle.truffle.r.test.builtins.TestBuiltin_sub.testSub# +#{ sub(pattern = 'a*', replacement = 'x', x = 'ÄaaaaÄ', perl = TRUE) } +[1] "xÄaaaaÄ" + +##com.oracle.truffle.r.test.builtins.TestBuiltin_sub.testSub# +#{ sub(pattern = 'a*', replacement = 'x', x = 'ÄaÄ', perl = TRUE) } +[1] "xÄaÄ" + +##com.oracle.truffle.r.test.builtins.TestBuiltin_sub.testSub#Ignored.Unknown# +#{ sub(pattern = 'Ä*', replacement = 'x', x = 'aÄÄÄÄÄb', perl = TRUE) } +[1] "xaÄÄÄÄÄb" + ##com.oracle.truffle.r.test.builtins.TestBuiltin_sub.testsub1# #argv <- list('^..dfd.', '', c('aa', '..dfd.row.names'), FALSE, FALSE, FALSE, FALSE); .Internal(sub(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]], argv[[7]])) [1] "aa" "row.names" diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_grep.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_grep.java index c3b9ada54787a3f1327676af89aff57ee31372d6..29f31b22de0b47d61c6d3c54e56641d577185d63 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_grep.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_grep.java @@ -4,7 +4,7 @@ * http://www.gnu.org/licenses/gpl-2.0.html * * Copyright (c) 2012-2014, Purdue University - * Copyright (c) 2013, 2016, Oracle and/or its affiliates + * Copyright (c) 2013, 2017, Oracle and/or its affiliates * * All rights reserved. */ @@ -87,6 +87,9 @@ public class TestBuiltin_grep extends TestBase { assertEval("{ .Internal(grep(character(), \"7\", F, F, F, F, F, F)) }"); assertEval("{ .Internal(grep(\"7\", 7, F, F, F, F, F, F)) }"); + // Expected output: integer(0) + // FastR output: [1] 1 + assertEval(Ignored.Unknown, "{ grep('^ *$', ' \\n') }"); } @Test diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_gsub.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_gsub.java index 3f8abf76cf0e6cf55490d592b7e2fc4ebb83e570..bb252920c177ca247dcb87a4b837234121924516 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_gsub.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_gsub.java @@ -4,7 +4,7 @@ * http://www.gnu.org/licenses/gpl-2.0.html * * Copyright (c) 2012-2014, Purdue University - * Copyright (c) 2013, 2016, Oracle and/or its affiliates + * Copyright (c) 2013, 2017, Oracle and/or its affiliates * * All rights reserved. */ @@ -186,5 +186,12 @@ public class TestBuiltin_gsub extends TestBase { assertEval("{ .Internal(gsub(\"7\", 42, \"7\", F, F, F, F)) }"); assertEval("{ .Internal(gsub(\"7\", character(), \"7\", F, F, F, F)) }"); assertEval("{ .Internal(gsub(\"7\", \"42\", 7, F, F, F, F)) }"); + + assertEval("{ gsub(pattern = 'a*', replacement = 'x', x = 'ÄaÄ', perl = TRUE) }"); + assertEval("{ gsub(pattern = 'a*', replacement = 'x', x = 'ÄaaaaÄ', perl = TRUE) }"); + + // Expected output: [1] "xaxbx" + // FastR output: [1] "axxxxxb" + assertEval(Ignored.Unknown, "{ gsub(pattern = 'Ä*', replacement = 'x', x = 'aÄÄÄÄÄb', perl = TRUE) }"); } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strsplit.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strsplit.java index f6d29dc8ce187711d5ca431f2ccac8c2555620b8..5e0ec4da3093c056a156f131e4ff324d97eefc4d 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strsplit.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strsplit.java @@ -4,7 +4,7 @@ * http://www.gnu.org/licenses/gpl-2.0.html * * Copyright (c) 2012-2014, Purdue University - * Copyright (c) 2013, 2016, Oracle and/or its affiliates + * Copyright (c) 2013, 2017, Oracle and/or its affiliates * * All rights reserved. */ @@ -121,5 +121,30 @@ public class TestBuiltin_strsplit extends TestBase { assertEval("strsplit('oo bar baz', '[f z]', perl=TRUE)"); assertEval("strsplit('foo \u1010ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄbar baz ', '[f z]', perl=TRUE)"); assertEval("strsplit('Ä Ä', '[ ]', perl=TRUE)"); + + assertEval("strsplit('1', '1', fixed=TRUE)"); + assertEval("strsplit('11', '11', fixed=TRUE)"); + assertEval("strsplit(c('1', '11'), c('1', '11'), fixed=TRUE)"); + assertEval("strsplit('Ä', 'Ä', fixed=TRUE)"); + assertEval("strsplit('ÄÄ', 'Ä', fixed=TRUE)"); + + assertEval("strsplit('1', '1', fixed=FALSE)"); + assertEval("strsplit('11', '11', fixed=FALSE)"); + assertEval("strsplit(c('1', '11'), c('1', '11'), fixed=FALSE)"); + assertEval("strsplit('Ä', 'Ä', fixed=FALSE)"); + assertEval("strsplit('ÄÄ', 'Ä', fixed=FALSE)"); + + assertEval("strsplit(c('111', '1'), c('111', '1'), fixed=TRUE)"); + assertEval("strsplit(c('1', ''), c('1', ''), fixed=TRUE)"); + assertEval("strsplit(c('1', 'b'), c('1', 'b'), fixed=TRUE)"); + assertEval("strsplit(c('a1a', 'a1b'), c('1', '1'), fixed=TRUE)"); + assertEval("strsplit(c('a1a', 'a1b'), '1', fixed=TRUE)"); + + assertEval("strsplit(c('111', '1'), c('111', '1'), fixed=FALSE)"); + assertEval("strsplit(c('1', ''), c('1', ''), fixed=FALSE)"); + assertEval("strsplit(c('1', 'b'), c('1', 'b'), fixed=FALSE)"); + assertEval("strsplit(c('a1a', 'a1b'), c('1', '1'), fixed=FALSE)"); + assertEval("strsplit(c('a1a', 'a1b'), '1', fixed=FALSE)"); + } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sub.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sub.java index 1a6d875d9a3b5ef7e2081b0ecba6042601e96ac7..9dc30129fa86232485473ccea19d4092effe7c7c 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sub.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sub.java @@ -4,7 +4,7 @@ * http://www.gnu.org/licenses/gpl-2.0.html * * Copyright (c) 2012-2014, Purdue University - * Copyright (c) 2013, 2016, Oracle and/or its affiliates + * Copyright (c) 2013, 2017, Oracle and/or its affiliates * * All rights reserved. */ @@ -148,5 +148,13 @@ public class TestBuiltin_sub extends TestBase { assertEval("{ .Internal(sub(\"7\", character(), \"7\", F, F, F, F)) }"); assertEval("{ .Internal(sub(\"7\", \"42\", 7, F, F, F, F)) }"); + assertEval("{ sub('\\\\s*$', '', 'Ä', perl=TRUE) }"); + + assertEval("{ sub(pattern = 'a*', replacement = 'x', x = 'ÄaÄ', perl = TRUE) }"); + assertEval("{ sub(pattern = 'a*', replacement = 'x', x = 'ÄaaaaÄ', perl = TRUE) }"); + + // Expected output: [1] "xaÄÄÄÄÄb" + // FastR output: [1] "axÄÄÄÄb" + assertEval(Ignored.Unknown, "{ sub(pattern = 'Ä*', replacement = 'x', x = 'aÄÄÄÄÄb', perl = TRUE) }"); } }