Skip to content

Commit 396e1d6

Browse files
committed
Implement missing parts of maketrans
1 parent 20eac37 commit 396e1d6

File tree

4 files changed

+62
-23
lines changed

4 files changed

+62
-23
lines changed

graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_unicode.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
*graalpython.lib-python.3.test.test_unicode.UnicodeTest.test_literals
6363
*graalpython.lib-python.3.test.test_unicode.UnicodeTest.test_ljust
6464
*graalpython.lib-python.3.test.test_unicode.UnicodeTest.test_lower
65+
*graalpython.lib-python.3.test.test_unicode.UnicodeTest.test_maketrans_translate
6566
*graalpython.lib-python.3.test.test_unicode.UnicodeTest.test_mul
6667
*graalpython.lib-python.3.test.test_unicode.UnicodeTest.test_none_arguments
6768
*graalpython.lib-python.3.test.test_unicode.UnicodeTest.test_partition

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
import com.oracle.graal.python.builtins.objects.bytes.BytesUtils;
7878
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
7979
import com.oracle.graal.python.builtins.objects.common.FormatNodeBase;
80+
import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes;
8081
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
8182
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary;
8283
import com.oracle.graal.python.builtins.objects.common.SequenceNodes.GetObjectArrayNode;
@@ -773,50 +774,80 @@ private static String toUpperCase(String str) {
773774
}
774775
}
775776

776-
// static str.maketrans()
777+
// str.maketrans()
777778
@Builtin(name = "maketrans", minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 4, isClassmethod = true)
778779
@GenerateNodeFactory
779780
public abstract static class MakeTransNode extends PythonQuaternaryBuiltinNode {
780781

781782
@Specialization(guards = "!isNoValue(to)")
782783
@SuppressWarnings("unused")
783-
PDict doString(VirtualFrame frame, Object cls, Object from, Object to, Object z,
784+
PDict doString(Object cls, Object from, Object to, Object z,
784785
@Cached CastToJavaStringCheckedNode castFromNode,
785786
@Cached CastToJavaStringCheckedNode castToNode,
787+
@Cached CastToJavaStringCheckedNode castZNode,
788+
@Cached ConditionProfile hasZProfile,
786789
@CachedLibrary(limit = "2") HashingStorageLibrary lib) {
787790

788791
String toStr = castToNode.cast(to, "argument 2 must be str, not %p", to);
789792
String fromStr = castFromNode.cast(from, "first maketrans argument must be a string if there is a second argument");
790-
if (fromStr.length() != toStr.length()) {
791-
throw raise(PythonBuiltinClassType.ValueError, ErrorMessages.FIRST_TWO_MAKETRANS_ARGS_MUST_HAVE_EQ_LENGTH);
793+
boolean hasZ = hasZProfile.profile(z != PNone.NO_VALUE);
794+
String zString = null;
795+
if (hasZ) {
796+
zString = castZNode.cast(z, ErrorMessages.ARG_D_MUST_BE_S_NOT_P, "maketrans()", 3, "str", z);
792797
}
793798

794799
HashingStorage storage = PDict.createNewStorage(false, fromStr.length());
795-
PDict translation = factory().createDict(storage);
796-
for (int i = 0; i < fromStr.length(); i++) {
797-
int key = fromStr.charAt(i);
798-
int value = toStr.charAt(i);
800+
int i, j;
801+
for (i = 0, j = 0; i < fromStr.length() && j < toStr.length();) {
802+
int key = PString.codePointAt(fromStr, i);
803+
int value = PString.codePointAt(toStr, j);
799804
storage = lib.setItem(storage, key, value);
805+
i += PString.charCount(key);
806+
j += PString.charCount(value);
807+
}
808+
if (i < fromStr.length() || j < toStr.length()) {
809+
throw raise(PythonBuiltinClassType.ValueError, ErrorMessages.FIRST_TWO_MAKETRANS_ARGS_MUST_HAVE_EQ_LENGTH);
810+
}
811+
if (hasZ) {
812+
for (i = 0; i < zString.length();) {
813+
int key = PString.codePointAt(zString, i);
814+
storage = lib.setItem(storage, key, PNone.NONE);
815+
i += PString.charCount(key);
816+
}
800817
}
801-
translation.setDictStorage(storage);
802-
803-
// TODO implement character deletion specified with 'z'
804818

805-
return translation;
819+
return factory().createDict(storage);
806820
}
807821

808-
@Specialization(guards = "isNoValue(to)")
822+
@Specialization(guards = {"isNoValue(to)", "isNoValue(z)"})
809823
@SuppressWarnings("unused")
810-
static PDict doDict(PDict from, Object cls, Object to, Object z) {
811-
// TODO implement dict case; see CPython 'unicodeobject.c' function
812-
// 'unicode_maketrans_impl'
813-
CompilerDirectives.transferToInterpreterAndInvalidate();
814-
throw new IllegalStateException("not yet implemented");
824+
PDict doDict(VirtualFrame frame, Object cls, PDict from, Object to, Object z,
825+
@Cached HashingCollectionNodes.GetHashingStorageNode getHashingStorageNode,
826+
@Cached CastToJavaStringCheckedNode cast,
827+
@CachedLibrary(limit = "3") HashingStorageLibrary hlib) {
828+
HashingStorage srcStorage = getHashingStorageNode.execute(frame, from);
829+
HashingStorage destStorage = PDict.createNewStorage(false, hlib.length(srcStorage));
830+
for (HashingStorage.DictEntry entry : hlib.entries(srcStorage)) {
831+
if (PGuards.isInteger(entry.key) || PGuards.isPInt(entry.key)) {
832+
hlib.setItem(destStorage, entry.key, entry.value);
833+
} else {
834+
String strKey = cast.cast(entry.key, ErrorMessages.KEYS_IN_TRANSLATE_TABLE_MUST_BE_STRINGS_OR_INTEGERS);
835+
if (strKey.isEmpty()) {
836+
throw raise(ValueError, ErrorMessages.STRING_KEYS_MUST_BE_LENGHT_1);
837+
}
838+
int codePoint = PString.codePointAt(strKey, 0);
839+
if (strKey.length() != PString.charCount(codePoint)) {
840+
throw raise(ValueError, ErrorMessages.STRING_KEYS_MUST_BE_LENGHT_1);
841+
}
842+
hlib.setItem(destStorage, codePoint, entry.value);
843+
}
844+
}
845+
return factory().createDict(destStorage);
815846
}
816847

817848
@Specialization(guards = {"!isDict(from)", "isNoValue(to)"})
818849
@SuppressWarnings("unused")
819-
PDict doFail(Object from, Object cls, Object to, Object z) {
850+
PDict doFail(Object cls, Object from, Object to, Object z) {
820851
throw raise(PythonBuiltinClassType.TypeError, ErrorMessages.IF_YOU_GIVE_ONLY_ONE_ARG_TO_DICT);
821852
}
822853
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringNodes.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -366,16 +366,21 @@ static void doNone(StringBuilder sb, PNone none) {
366366

367367
@Specialization
368368
@TruffleBoundary(allowInlining = true)
369-
static void doInt(StringBuilder sb, int translated) {
370-
sb.appendCodePoint(translated);
369+
static void doInt(StringBuilder sb, int translated,
370+
@Shared("raise") @Cached PRaiseNode raise) {
371+
if (Character.isValidCodePoint(translated)) {
372+
sb.appendCodePoint(translated);
373+
} else {
374+
throw raise.raise(ValueError, "invalid unicode code poiont");
375+
}
371376
}
372377

373378
@Specialization
374379
static void doLong(StringBuilder sb, long translated,
375380
@Shared("raise") @Cached PRaiseNode raise,
376381
@Shared("overflow") @Cached BranchProfile ovf) {
377382
try {
378-
doInt(sb, PInt.intValueExact(translated));
383+
doInt(sb, PInt.intValueExact(translated), raise);
379384
} catch (OverflowException e) {
380385
ovf.enter();
381386
throw raiseError(raise);
@@ -387,7 +392,7 @@ static void doPInt(StringBuilder sb, PInt translated,
387392
@Shared("raise") @Cached PRaiseNode raise,
388393
@Shared("overflow") @Cached BranchProfile ovf) {
389394
try {
390-
doInt(sb, translated.intValueExact());
395+
doInt(sb, translated.intValueExact(), raise);
391396
} catch (OverflowException e) {
392397
ovf.enter();
393398
throw raiseError(raise);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,4 +561,6 @@ public abstract class ErrorMessages {
561561
public static final String UNMAPPABLE_CHARACTER = "unmappable character";
562562
public static final String MALFORMED_INPUT = "malformed input";
563563
public static final String SHOULD_HAVE_RETURNED_EXCEPTION = "calling %N should have returned an instance of BaseException, not %p";
564+
public static final String STRING_KEYS_MUST_BE_LENGHT_1 = "string keys in translate table must be of length 1";
565+
public static final String KEYS_IN_TRANSLATE_TABLE_MUST_BE_STRINGS_OR_INTEGERS = "keys in translate table must be strings or integers";
564566
}

0 commit comments

Comments
 (0)