summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJouke Witteveen <j.witteveen@gmail.com>2025-07-24 14:17:55 +0200
committerJussi Pakkanen <jussi.pakkanen@mailbox.org>2025-11-28 12:41:01 +0200
commit0fb9c5a6839205cc32c2632dd20f723f00dc165b (patch)
tree0dab116976dbf400ed9f95860ac683435466d321
parent5e399434f2c3bb791f0c16f01fa85680c3f90fb7 (diff)
downloadmeson-0fb9c5a6839205cc32c2632dd20f723f00dc165b.tar.gz
interpreter: Add a slice() method to arrays
This can come in handy for instance when a custom target creates both headers and sources. Slicing the output of a `to_list()` call provides convenient access to just the headers or just the sources.
-rw-r--r--docs/markdown/snippets/array-slice.md3
-rw-r--r--docs/yaml/elementary/array.yml27
-rw-r--r--mesonbuild/interpreter/primitives/array.py14
-rw-r--r--test cases/common/56 array methods/meson.build10
4 files changed, 54 insertions, 0 deletions
diff --git a/docs/markdown/snippets/array-slice.md b/docs/markdown/snippets/array-slice.md
new file mode 100644
index 000000000..8b503bce9
--- /dev/null
+++ b/docs/markdown/snippets/array-slice.md
@@ -0,0 +1,3 @@
+## Array `.slice()` method
+
+Arrays now have a `.slice()` method which allows for subsetting of arrays.
diff --git a/docs/yaml/elementary/array.yml b/docs/yaml/elementary/array.yml
index 7d0480a04..7183d1aff 100644
--- a/docs/yaml/elementary/array.yml
+++ b/docs/yaml/elementary/array.yml
@@ -37,6 +37,33 @@ methods:
type: any
description: Fallback value that is returned if the index is out of range.
+- name: slice
+ returns: array[any]
+ since: 1.10.0
+ description: |
+ Return a selection of the elements of the array starting at index `start`
+ and continuing with `step` size jumps until `stop`. Negative indices count
+ from the back of the array. The step size cannot be zero, but may be
+ negative. If it is negative, `start` and `stop` default to the end and
+ beginning of the array, respectively. If `step` is positive, `start`
+ defaults to 0 and `stop` defaults to the length of the array. Either both
+ or none of `start` and `stop` must be provided to prevent ambiguity.
+
+ optargs:
+ start:
+ type: int
+ description: The lower bound of the slice
+
+ stop:
+ type: int
+ description: The upper bound of the slice
+
+ kwargs:
+ step:
+ type: int
+ default: 1
+ description: The step size
+
- name: length
returns: int
description: Returns the current size of the array.
diff --git a/mesonbuild/interpreter/primitives/array.py b/mesonbuild/interpreter/primitives/array.py
index d0a244179..86c1ce207 100644
--- a/mesonbuild/interpreter/primitives/array.py
+++ b/mesonbuild/interpreter/primitives/array.py
@@ -7,12 +7,14 @@ import typing as T
from ...interpreterbase import (
InterpreterObject,
IterableObject,
+ KwargInfo,
MesonOperator,
ObjectHolder,
typed_operator,
noKwargs,
noPosargs,
noArgsFlattening,
+ typed_kwargs,
typed_pos_args,
FeatureNew,
@@ -80,6 +82,18 @@ class ArrayHolder(ObjectHolder[T.List[TYPE_var]], IterableObject):
return args[1]
return self.held_object[index]
+ @FeatureNew('array.slice', '1.10.0')
+ @typed_kwargs('array.slice', KwargInfo('step', int, default=1))
+ @typed_pos_args('array.slice', optargs=[int, int])
+ @InterpreterObject.method('slice')
+ def slice_method(self, args: T.Tuple[T.Optional[int], T.Optional[int]], kwargs: T.Dict[str, int]) -> TYPE_var:
+ start, stop = args
+ if start is not None and stop is None:
+ raise InvalidArguments('Providing only one positional slice argument is ambiguous.')
+ if kwargs['step'] == 0:
+ raise InvalidArguments('Slice step cannot be zero.')
+ return self.held_object[start:stop:kwargs['step']]
+
@typed_operator(MesonOperator.PLUS, object)
@InterpreterObject.operator(MesonOperator.PLUS)
def op_plus(self, other: TYPE_var) -> T.List[TYPE_var]:
diff --git a/test cases/common/56 array methods/meson.build b/test cases/common/56 array methods/meson.build
index 3707775ec..3cc1067b9 100644
--- a/test cases/common/56 array methods/meson.build
+++ b/test cases/common/56 array methods/meson.build
@@ -69,6 +69,16 @@ if not combined.contains('ghi')
error('Combined claims not to contain ghi.')
endif
+# test array slicing
+assert(['a', 'b', 'c'].slice() == ['a', 'b', 'c'])
+assert(['a', 'b', 'c'].slice(step : 2) == ['a', 'c'])
+assert(['a', 'b', 'c'].slice(step : -1) == ['c', 'b', 'a'])
+assert(['a', 'b', 'c'].slice(step : -2) == ['c', 'a'])
+assert(['a', 'b', 'c'].slice(1, 2) == ['b'])
+assert(['a', 'b', 'c'].slice(2, -2) == [])
+assert(['a', 'b', 'c'].slice(-9876543, 2) == ['a', 'b'])
+assert(['a', 'b', 'c', 'd', 'e'].slice(1, 12, step : 2) == ['b', 'd'])
+
# test array flattening
x = ['a', ['b'], [[[[[[['c'], 'd']]], 'e']]]]
assert(x.length() == 3)