Skip to content

Commit d82bb62

Browse files
feat: list indexing using STC methods (pyccel#1926)
Add support for indexing a list in C. This PR fixes pyccel#1876 . Further it allows the declarations to be tested properly --------- Co-authored-by: EmilyBourne <[email protected]>
1 parent 5742bca commit d82bb62

File tree

7 files changed

+100
-10
lines changed

7 files changed

+100
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ All notable changes to this project will be documented in this file.
1616
- #1750 : Add Python support for set method `remove()`.
1717
- #1743 : Add Python support for set method `discard()`.
1818
- #1754 : Add Python support for set method `update()`.
19+
- #1893 : Add Python support for set initialisation with `set()`.
1920
- #1787 : Ensure STC is installed with Pyccel.
2021
- #1656 : Ensure gFTL is installed with Pyccel.
2122
- #1844 : Add line numbers and code to errors from built-in function calls.
2223
- #1655 : Add the appropriate C language equivalent for declaring a Python `list` container using the STC library.
24+
- #1876 : Add C support for indexing lists.
2325
- #1659 : Add the appropriate C language equivalent for declaring a Python `set` container using the STC library.
24-
- #1893 : Add Python support for set initialisation with `set()`.
2526
- #1877 : Add C Support for set method `pop()`.
2627
- #1895 : Add Python support for dict initialisation with `{}`.
2728
- #1895 : Add Python support for dict initialisation with `dict()`.

docs/builtin-functions.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,25 @@ Python contains a limited number of builtin functions defined [here](https://doc
7474
| **`zip`** | as a loop iterable |
7575
| \_\_`import`\_\_ | No
7676

77+
## List methods
78+
79+
| Method | Supported |
80+
|----------|-----------|
81+
| `append` | Python-only |
82+
| `clear` | Python-only |
83+
| `copy` | Python-only |
84+
| `count` | No |
85+
| `extend` | Python-only |
86+
| `index` | No |
87+
| `insert` | Python-only |
88+
| `max` | No |
89+
| `min` | No |
90+
| `pop` | Python-only |
91+
| `remove` | Python-only |
92+
| `reverse` | No |
93+
| `sort` | Python-only |
94+
95+
7796
## Dictionary methods
7897

7998
:warning: The dictionary support provided by Pyccel only covers unordered dictionaries.

pyccel/codegen/printing/ccode.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,16 +1472,9 @@ def _print_IndexedElement(self, expr):
14721472
inds = list(expr.indices)
14731473
base_shape = base.shape
14741474
allow_negative_indexes = expr.allows_negative_indexes
1475-
if isinstance(base.class_type, NumpyNDArrayType):
1476-
#set dtype to the C struct types
1477-
dtype = self.find_in_ndarray_type_registry(expr.dtype)
1478-
elif isinstance(base.class_type, HomogeneousContainerType):
1479-
dtype = self.find_in_ndarray_type_registry(numpy_precision_map[(expr.dtype.primitive_type, expr.dtype.precision)])
1480-
else:
1481-
raise NotImplementedError(f"Don't know how to index {expr.class_type} type")
14821475

14831476
if isinstance(base, IndexedElement):
1484-
while isinstance(base, IndexedElement) and isinstance(base.class_type, HomogeneousContainerType):
1477+
while isinstance(base, IndexedElement) and isinstance(base.class_type, (NumpyNDArrayType, HomogeneousTupleType)):
14851478
inds = list(base.indices) + inds
14861479
base = base.base
14871480

@@ -1497,7 +1490,27 @@ def _print_IndexedElement(self, expr):
14971490
inds[i] = IfTernaryOperator(PyccelLt(ind, LiteralInteger(0)),
14981491
PyccelAdd(base_shape[i], ind, simplify = True), ind)
14991492

1493+
if isinstance(base.class_type, HomogeneousListType):
1494+
assign = expr.get_user_nodes(Assign)
1495+
index = self._print(inds[0])
1496+
list_var = self._print(ObjectAddress(base))
1497+
container_type = self.get_c_type(base.class_type)
1498+
if assign:
1499+
assert len(assign) == 1
1500+
assign_node = assign[0]
1501+
lhs = assign_node.lhs
1502+
if lhs == expr or lhs.is_user_of(expr):
1503+
return f"(*{container_type}_at_mut({list_var},{index}))"
1504+
return f"(*{container_type}_at({list_var},{index}))"
1505+
15001506
base_name = self._print(base)
1507+
if isinstance(base.class_type, NumpyNDArrayType):
1508+
#set dtype to the C struct types
1509+
dtype = self.find_in_ndarray_type_registry(expr.dtype)
1510+
elif isinstance(base.class_type, HomogeneousTupleType):
1511+
dtype = self.find_in_ndarray_type_registry(numpy_precision_map[(expr.dtype.primitive_type, expr.dtype.precision)])
1512+
else:
1513+
raise NotImplementedError(f"Don't know how to index {expr.class_type} type")
15011514
if expr.rank > 0:
15021515
#managing the Slice input
15031516
for i , ind in enumerate(inds):

tests/epyccel/test_epyccel_decorators.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,22 @@ def my_sum(v: 'T[:]'):
169169

170170
assert python_cmplx == pyccel_cmplx
171171
assert isinstance(python_cmplx, type(pyccel_cmplx))
172+
173+
@pytest.mark.parametrize("language", (
174+
pytest.param("fortran", marks = [
175+
pytest.mark.skip(reason="lists not implemented in fortran"),
176+
pytest.mark.fortran]),
177+
pytest.param("c", marks = pytest.mark.c),
178+
pytest.param("python", marks = pytest.mark.python)
179+
)
180+
)
181+
def test_allow_negative_index_list(language):
182+
def allow_negative_index_annotation():
183+
a = [1,2,3,4]
184+
return a[-1], a[-2], a[-3], a[0]
185+
186+
epyc_allow_negative_index_annotation = epyccel(allow_negative_index_annotation, language=language)
187+
188+
assert epyc_allow_negative_index_annotation() == allow_negative_index_annotation()
189+
assert isinstance(epyc_allow_negative_index_annotation(), type(allow_negative_index_annotation()))
190+

tests/epyccel/test_epyccel_lists.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@
1717
def language(request):
1818
return request.param
1919

20+
@pytest.fixture( params=[
21+
pytest.param("c", marks = pytest.mark.c),
22+
pytest.param("fortran", marks = [
23+
pytest.mark.skip(reason="lists not implemented in fortran"),
24+
pytest.mark.fortran]),
25+
pytest.param("python", marks = pytest.mark.python)
26+
],
27+
scope = "module"
28+
)
29+
def stc_language(request):
30+
return request.param
31+
2032
def test_pop_last_element(language) :
2133
def pop_last_element():
2234
a = [1,3,45]
@@ -658,3 +670,23 @@ def g():
658670
epyc_f = epyccel(f, language=language)
659671
assert f() == epyc_f()
660672

673+
def test_mutable_indexing(stc_language):
674+
def f():
675+
a = [1,2,3,4]
676+
a[0] = 5
677+
a[2] += 6
678+
return a[0], a[1], a[2], a[3]
679+
680+
epyc_f = epyccel(f, language=stc_language)
681+
assert f() == epyc_f()
682+
683+
def test_mutable_multi_level_indexing(stc_language):
684+
def f():
685+
a = [1,2,3,4]
686+
b = [a]
687+
b[0][0] = 5
688+
b[0][2] = 6
689+
return a[0], a[1], a[2], a[3]
690+
691+
epyc_f = epyccel(f, language=stc_language)
692+
assert f() == epyc_f()

tests/epyccel/test_epyccel_variable_annotations.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,13 +229,15 @@ def test_homogeneous_list_annotation_int(stc_language):
229229
def homogeneous_list_annotation():
230230
a: 'list[int]' # pylint: disable=unused-variable
231231
a = [1, 2, 3, 4]
232+
return a[0], a[1], a[2], a[3]
232233
epyc_homogeneous_list_annotation = epyccel(homogeneous_list_annotation, language=stc_language)
233234
assert epyc_homogeneous_list_annotation() == homogeneous_list_annotation()
234235
assert isinstance(epyc_homogeneous_list_annotation(), type(homogeneous_list_annotation()))
235236

236237
def test_homogeneous_list_without_annotation(stc_language):
237238
def homogeneous_list():
238239
a = [1, 2, 3, 4] # pylint: disable=unused-variable
240+
return a[0], a[1], a[2], a[3]
239241
epyc_homogeneous_list = epyccel(homogeneous_list, language=stc_language)
240242
assert epyc_homogeneous_list() == homogeneous_list()
241243
assert isinstance(epyc_homogeneous_list(), type(homogeneous_list()))
@@ -244,6 +246,7 @@ def test_homogeneous_list_annotation_float(stc_language):
244246
def homogeneous_list_annotation():
245247
a: 'list[float]' # pylint: disable=unused-variable
246248
a = [1.1, 2.2, 3.3, 4.4]
249+
return a[0], a[1], a[2], a[3]
247250
epyc_homogeneous_list_annotation = epyccel(homogeneous_list_annotation, language=stc_language)
248251
assert epyc_homogeneous_list_annotation() == homogeneous_list_annotation()
249252
assert isinstance(epyc_homogeneous_list_annotation(), type(homogeneous_list_annotation()))
@@ -252,6 +255,7 @@ def test_homogeneous_list_annotation_bool(stc_language):
252255
def homogeneous_list_annotation():
253256
a: 'list[bool]' # pylint: disable=unused-variable
254257
a = [False, True, True, False]
258+
return a[0], a[1], a[2], a[3]
255259
epyc_homogeneous_list_annotation = epyccel(homogeneous_list_annotation, language=stc_language)
256260
assert epyc_homogeneous_list_annotation() == homogeneous_list_annotation()
257261
assert isinstance(epyc_homogeneous_list_annotation(), type(homogeneous_list_annotation()))
@@ -260,6 +264,7 @@ def test_homogeneous_list_annotation_complex(stc_language):
260264
def homogeneous_list_annotation():
261265
a: 'list[complex]' # pylint: disable=unused-variable
262266
a = [1+1j, 2+2j, 3+3j, 4+4j]
267+
return a[0], a[1], a[2], a[3]
263268
epyc_homogeneous_list_annotation = epyccel(homogeneous_list_annotation, language=stc_language)
264269
assert epyc_homogeneous_list_annotation() == homogeneous_list_annotation()
265270
assert isinstance(epyc_homogeneous_list_annotation(), type(homogeneous_list_annotation()))
@@ -268,6 +273,7 @@ def test_homogeneous_list_annotation_embedded_complex(stc_language):
268273
def homogeneous_list_annotation():
269274
a : 'list[complex]' = [1j, 2j]
270275
b = [a] # pylint: disable=unused-variable
276+
return b[0][0]
271277
epyc_homogeneous_list_annotation = epyccel(homogeneous_list_annotation, language=stc_language)
272278
assert epyc_homogeneous_list_annotation() == homogeneous_list_annotation()
273279
assert isinstance(epyc_homogeneous_list_annotation(), type(homogeneous_list_annotation()))

tests/epyccel/test_functionals.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
@pytest.fixture( params=[
1111
pytest.param("fortran", marks = pytest.mark.fortran),
1212
pytest.param("c", marks = [
13-
pytest.mark.xfail(reason="C does not support list indexing yet, related issue #1876"),
13+
pytest.mark.skip(reason="C does not support list comprehensions. See #1948"),
1414
pytest.mark.c]),
1515
pytest.param("python", marks = pytest.mark.python)
1616
],

0 commit comments

Comments
 (0)