Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Implement comparison opcode
We access existing comparison implementations from CPython byte code.
This allows us to provide examples of if-else and while loops. We fix a
minor bug this revealed in PyUnicode.
  • Loading branch information
jeff5 committed Jan 7, 2023
commit 09729baae082ad779dacd6c82bdf5519dc390eb5
11 changes: 10 additions & 1 deletion core/src/main/java/org/python/core/CPython38Frame.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c)2022 Jython Developers.
// Copyright (c)2023 Jython Developers.
// Licensed to PSF under a contributor agreement.
package org.python.core;

Expand Down Expand Up @@ -246,6 +246,15 @@ Object eval() {
s[sp - 1] = Abstract.getAttr(s[sp - 1], name);
break;

case Opcode.COMPARE_OP:
// v | w | -> | op(v,w) |
// -------^sp -----------^sp
w = s[--sp]; // POP
v = s[sp - 1]; // TOP
s[sp - 1] = Comparison.from(opword & 0xff).apply(v, w);
oparg = 0;
break;

case Opcode.JUMP_FORWARD:
ip += (oparg | opword & 0xff) >> 1;
oparg = 0;
Expand Down
39 changes: 24 additions & 15 deletions core/src/main/java/org/python/core/Comparison.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c)2021 Jython Developers.
// Copyright (c)2023 Jython Developers.
// Licensed to PSF under a contributor agreement.
package org.python.core;

Expand Down Expand Up @@ -61,39 +61,48 @@ enum Comparison {
boolean toBool(int c) { return c >= 0; }
},

/** The (reflected) {@code __contains__} operation. */
/**
* The {@code in} operation (reflected {@code __contains__}). Note
* that "{@code v in seq}" compiles to<pre>
* LOAD_NAME 0 (v)
* LOAD_NAME 1 (seq)
* COMPARE_OP 6 (in)
* </pre> which must lead to {@code seq.__contains__(v)}.
*/
IN("in", Slot.op_contains) {

@Override
boolean toBool(int c) { return c >= 0; }

@Override
Object apply(Object v, Object w) throws Throwable {
Operations vOps = Operations.of(v);
Object apply(Object v, Object seq) throws Throwable {
Operations ops = Operations.of(seq);
try {
MethodHandle contains = slot.getSlot(vOps);
return (boolean)contains.invokeExact(w, v);
MethodHandle contains = slot.getSlot(ops);
return (boolean)contains.invokeExact(seq, v);
} catch (Slot.EmptyException e) {
throw new TypeError(NOT_CONTAINER, vOps.type(v).name);
throw new TypeError(NOT_CONTAINER, ops.type(seq).name);
}
}
},

/** The inverted (reflected) {@code __contains__} operation. */
/**
* The inverted {@code in} operation (reflected
* {@code __contains__}).
*/
NOT_IN("not in", Slot.op_contains) {

@Override
boolean toBool(int c) { return c < 0; }

@Override
Object apply(Object v, Object w) throws Throwable {
Operations vOps = Operations.of(v);
;
Object apply(Object v, Object seq) throws Throwable {
Operations ops = Operations.of(seq);
try {
MethodHandle contains = slot.getSlot(vOps);
return (boolean)contains.invokeExact(w, v);
MethodHandle contains = slot.getSlot(ops);
return !(boolean)contains.invokeExact(seq, v);
} catch (Slot.EmptyException e) {
throw new TypeError(NOT_CONTAINER, vOps.type(v).name);
throw new TypeError(NOT_CONTAINER, ops.type(seq).name);
}
}
},
Expand Down Expand Up @@ -161,7 +170,7 @@ Object apply(Object v, Object w) throws Throwable {
public String toString() { return text; }

/**
* Translate CPython {@code Opcode.COMPARE_OP} opcode argument to
* Translate CPython {@link Opcode#COMPARE_OP} opcode argument to
* Comparison constant.
*
* @param oparg opcode argument
Expand Down
17 changes: 10 additions & 7 deletions core/src/main/java/org/python/core/PyUnicode.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c)2021 Jython Developers.
// Copyright (c)2023 Jython Developers.
// Licensed to PSF under a contributor agreement.
package org.python.core;

Expand Down Expand Up @@ -409,14 +409,17 @@ private static boolean __contains__(String self, Object o) {

private static boolean contains(CodepointDelegate s, Object o) {
try {
CodepointDelegate p = adapt(s);
CodepointDelegate p = adapt(o);
PySlice.Indices slice = getSliceIndices(s, null, null);
return find(s, p, slice) >= 0;
} catch (NoConversion nc) {
throw Abstract.requiredTypeError("a string", o);
throw Abstract.typeError(IN_STRING_TYPE, o);
}
}

private static final String IN_STRING_TYPE =
"'in <string>' requires string as left operand, not %s";

/*
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.unicode___add___doc)
*/
Expand Down Expand Up @@ -2730,7 +2733,7 @@ static final Object __format__(Object self, Object formatSpec) {
* object to format and output. If {@code field_name} is not
* {@code None}, it is looked up, formatted with {@code format_spec}
* and {@code conversion} and then used.
*
*
* @param formatString to parse
* @return an iterator of format {@code tuple}s
*/
Expand All @@ -2745,7 +2748,7 @@ static Object formatter_parser(Object formatString) {

/**
* Implementation of {@code _string.formatter_field_name_split}.
*
*
* @param fieldName to split into components
* @return a tuple of the first field name component and the rest
*/
Expand Down Expand Up @@ -4476,7 +4479,7 @@ public double atof() {
*/
@Deprecated // See _PyUnicode_TransformDecimalAndSpaceToASCII
private String encodeDecimal() {

// XXX This all has a has a Jython 2 smell: bytes/str confusion.
// XXX Also, String and PyUnicode implementations are needed.
// XXX Follow CPython _PyUnicode_TransformDecimalAndSpaceToASCII
Expand Down Expand Up @@ -4910,7 +4913,7 @@ public static PyLong atol(String self, int base) {
* @param value the format string when {@code enclosingIterator} is not null
* @return the formatted string based on the arguments
* @throws TypeError if {@code __repr__} or {@code __str__} conversions returned a non-string.
* @throws Throwable from other errors in
* @throws Throwable from other errors in {@code __repr__} or {@code __str__}
*/
// XXX make this support format(String) too
private String buildFormattedString(Object[] args, String[] keywords,
Expand Down
7 changes: 4 additions & 3 deletions core/src/test/java/org/python/core/CPython38CodeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ void co_consts() {
@DisplayName("We can execute simple ...")
@ParameterizedTest(name = "{0}.py")
@ValueSource(strings = {"load_store_name", "unary_op", "binary_op", "bool_left_arith",
"bool_right_arith", "tuple_index", "list_index", "call_method_builtin"})
"bool_right_arith", "comparison", "tuple_index", "list_index", "call_method_builtin"})
void executeSimple(String name) {
CPython38Code code = readCode(name);
PyDict globals = new PyDict();
Expand All @@ -140,9 +140,10 @@ void executeSimple(String name) {
* @param name of the Python example
*/
@SuppressWarnings("static-method")
@DisplayName("We can execute branches ...")
@DisplayName("We can execute branches and while loops ...")
@ParameterizedTest(name = "{0}.py")
@ValueSource(strings = {"simple_if", "multi_if"})
@ValueSource(strings = {"simple_if", "multi_if", "simple_loop", "tuple_dot_product",
"list_dot_product"})
void executeBranchAndLoop(String name) {
CPython38Code code = readCode(name);
PyDict globals = new PyDict();
Expand Down
6 changes: 4 additions & 2 deletions core/src/test/java/org/python/core/PyListTest.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Copyright (c)2023 Jython Developers.
// Licensed to PSF under a contributor agreement.
package org.python.core;

import static org.junit.jupiter.api.Assertions.assertEquals;
Expand Down Expand Up @@ -159,7 +161,7 @@ private static Arguments sortExample(List<Object> sorted, long seed,

/**
* A toString that limits the array size
*
*
* @param a the array to return as a string
* @param n maximum number of array elements to show
* @return string representation of {@code a}
Expand All @@ -176,7 +178,7 @@ private static String shortString(ArrayList<Object> a, int n) {

/**
* Randomise the order of elements in a list.
*
*
* @param m to randomise
* @param r random generator
*/
Expand Down
55 changes: 55 additions & 0 deletions core/src/test/pythonExample/comparison.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# comparison.py

# Tests of the order comparisons

a = 2
b = 4

lt = a < b
le = a <= b
eq = a == b
ne = a != b
ge = a >= b
gt = a > b


a = 4
b = 2

lt1 = a < b
le1 = a <= b
eq1 = a == b
ne1 = a != b
ge1 = a >= b
gt1 = a > b


a = 2
b = 2

lt2 = a < b
le2 = a <= b
eq2 = a == b
ne2 = a != b
ge2 = a >= b
gt2 = a > b

# Tests of 'in'

t = ("cow", 2, "pig", None, 42.0)
f0 = 1 in t
f1 = "c" in t
t1x = "c" not in t
f2 = 42.1 in t
f3 = (2,) in t
f4 = "c" in t[2]


t0 = 2 in t
t1 = "pig" in t
f1x = "pig" not in t
t2 = None in t
t3 = 42 in t
t4 = "p" in t[2]


17 changes: 17 additions & 0 deletions core/src/test/pythonExample/list_dot_product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# list_dot_product.py

# Multiply-add of float vectors (without for loops)
# Also, multiplication as repetition.

n = 2

a = [1.2, 3.4, 5.6, 7.8] * (3 * n)
b = (4 * n) * [1.2, 4.5, 7.8]
n = 12 * n # lists are this long

i = 0
sum = 0.0

while i < n:
sum = sum + a[i] * b[i]
i = i + 1
10 changes: 10 additions & 0 deletions core/src/test/pythonExample/simple_loop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# simple_loop.py

n = 6

# ? n, sum
sum = 0
while n > 0:
sum = sum + n
n = n - 1

25 changes: 25 additions & 0 deletions core/src/test/pythonExample/tuple_dot_product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# tuple_dot_product.py

# Multiply-add of int and float vectors (without for loops)

a = (2, 3, 4)
b = (3, 4, 6)
n = 3

# ? sum

sum = a[0] * b[0]
i = 1
while i < n:
sum = sum + a[i] * b[i]
i = i + 1

a= (1., 2., 3., 4.)
b = (4., 3., 4., 5.)
n = 4

sum2 = a[0] * b[0]
i = 1
while i < n:
sum2 = sum2 + a[i] * b[i]
i = i + 1