aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Claessens <xavier.claessens@collabora.com>2021-02-06 11:14:25 -0500
committerXavier Claessens <xclaesse@gmail.com>2021-03-16 20:50:16 -0400
commita45f939092cc2c60d49040bdb0147758a1403f61 (patch)
tree76db6837f0ca5cf9a0546f80a8a88dc7a79d24bb
parent6415453f17f85e16d1d2ca3553325b769d0f124d (diff)
downloadmeson-a45f939092cc2c60d49040bdb0147758a1403f61.zip
meson-a45f939092cc2c60d49040bdb0147758a1403f61.tar.gz
meson-a45f939092cc2c60d49040bdb0147758a1403f61.tar.bz2
Add range() function
Fixes: #5026.
-rw-r--r--data/syntax-highlighting/vim/syntax/meson.vim1
-rw-r--r--docs/markdown/Reference-manual.md30
-rw-r--r--docs/markdown/snippets/range.md28
-rw-r--r--mesonbuild/ast/interpreter.py1
-rw-r--r--mesonbuild/interpreter.py26
-rw-r--r--mesonbuild/interpreterbase.py16
-rw-r--r--test cases/common/61 foreach/meson.build20
7 files changed, 119 insertions, 3 deletions
diff --git a/data/syntax-highlighting/vim/syntax/meson.vim b/data/syntax-highlighting/vim/syntax/meson.vim
index 161bcf1..321866f 100644
--- a/data/syntax-highlighting/vim/syntax/meson.vim
+++ b/data/syntax-highlighting/vim/syntax/meson.vim
@@ -119,6 +119,7 @@ syn keyword mesonBuiltin
\ test
\ vcs_tag
\ warning
+ \ range
if exists("meson_space_error_highlight")
" trailing whitespace
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 3af00c4..a70c94b 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -1861,6 +1861,36 @@ which to build the targets.
If you desire more specific behavior than what this command provides,
you should use `custom_target`.
+### range()
+
+``` meson
+ rangeobject range(stop)
+ rangeobject range(start, stop[, step])
+```
+
+*Since 0.58.0*
+
+Return an opaque object that can be only be used in `foreach` statements.
+- `start` must be integer greater or equal to 0. Defaults to 0.
+- `stop` must be integer greater or equal to `start`.
+- `step` must be integer greater or equal to 1. Defaults to 1.
+
+It cause the `foreach` loop to be called with the value from `start` included
+to `stop` excluded with an increment of `step` after each loop.
+
+```meson
+# Loop 15 times with i from 0 to 14 included.
+foreach i : range(15)
+ ...
+endforeach
+```
+
+The range object can also be assigned to a variable and indexed.
+```meson
+r = range(5, 10, 2)
+assert(r[2] == 9)
+```
+
## Built-in objects
These are built-in objects that are always available.
diff --git a/docs/markdown/snippets/range.md b/docs/markdown/snippets/range.md
new file mode 100644
index 0000000..77635d6
--- /dev/null
+++ b/docs/markdown/snippets/range.md
@@ -0,0 +1,28 @@
+## New `range()` function
+
+``` meson
+ rangeobject range(stop)
+ rangeobject range(start, stop[, step])
+```
+
+Return an opaque object that can be only be used in `foreach` statements.
+- `start` must be integer greater or equal to 0. Defaults to 0.
+- `stop` must be integer greater or equal to `start`.
+- `step` must be integer greater or equal to 1. Defaults to 1.
+
+It cause the `foreach` loop to be called with the value from `start` included
+to `stop` excluded with an increment of `step` after each loop.
+
+```meson
+# Loop 15 times with i from 0 to 14 included.
+foreach i : range(15)
+ ...
+endforeach
+```
+
+The range object can also be assigned to a variable and indexed.
+```meson
+r = range(5, 10, 2)
+assert(r[2] == 9)
+```
+
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index 98d141d..71c7f47 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -128,6 +128,7 @@ class AstInterpreter(interpreterbase.InterpreterBase):
'subdir_done': self.func_do_nothing,
'alias_target': self.func_do_nothing,
'summary': self.func_do_nothing,
+ 'range': self.func_do_nothing,
})
def func_do_nothing(self, node: BaseNode, args: T.List[TYPE_nvar], kwargs: T.Dict[str, TYPE_nvar]) -> bool:
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index d554cce..364cf70 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -30,7 +30,7 @@ from .interpreterbase import check_stringlist, flatten, noPosargs, noKwargs, str
from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest
from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler, disablerIfNotFound
from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs, FeatureDeprecatedKwargs
-from .interpreterbase import ObjectHolder, MesonVersionString
+from .interpreterbase import ObjectHolder, MesonVersionString, RangeHolder
from .interpreterbase import TYPE_var, TYPE_nkwargs
from .modules import ModuleReturnValue, ModuleObject, ModuleState
from .cmake import CMakeInterpreter
@@ -2509,7 +2509,8 @@ class Interpreter(InterpreterBase):
'static_library': self.func_static_lib,
'both_libraries': self.func_both_lib,
'test': self.func_test,
- 'vcs_tag': self.func_vcs_tag
+ 'vcs_tag': self.func_vcs_tag,
+ 'range': self.func_range,
})
if 'MESON_UNIT_TEST' in os.environ:
self.funcs.update({'exception': self.func_exception})
@@ -5033,3 +5034,24 @@ This will become a hard error in the future.''', location=self.current_node)
raise InvalidCode('Is_disabler takes one argument.')
varname = args[0]
return isinstance(varname, Disabler)
+
+ @noKwargs
+ @FeatureNew('range', '0.58.0')
+ @typed_pos_args('range', int, optargs=[int, int])
+ def func_range(self, node, args: T.Tuple[int, T.Optional[int], T.Optional[int]], kwargs: T.Dict[str, T.Any]) -> RangeHolder:
+ start, stop, step = args
+ # Just like Python's range, we allow range(stop), range(start, stop), or
+ # range(start, stop, step)
+ if stop is None:
+ stop = start
+ start = 0
+ if step is None:
+ step = 1
+ # This is more strict than Python's range()
+ if start < 0:
+ raise InterpreterException('start cannot be negative')
+ if stop < start:
+ raise InterpreterException('stop cannot be less than start')
+ if step < 1:
+ raise InterpreterException('step must be >=1')
+ return RangeHolder(start, stop, step)
diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py
index 1764713..a364510 100644
--- a/mesonbuild/interpreterbase.py
+++ b/mesonbuild/interpreterbase.py
@@ -70,6 +70,20 @@ class ObjectHolder(T.Generic[TV_InterpreterObject]):
class MesonVersionString(str):
pass
+class RangeHolder(InterpreterObject):
+ def __init__(self, start: int, stop: int, step: int) -> None:
+ super().__init__()
+ self.range = range(start, stop, step)
+
+ def __iter__(self) -> T.Iterator[int]:
+ return iter(self.range)
+
+ def __getitem__(self, key: int) -> int:
+ return self.range[key]
+
+ def __len__(self) -> int:
+ return len(self.range)
+
# Decorators for method calls.
def check_stringlist(a: T.Any, msg: str = 'Arguments must be strings.') -> None:
@@ -950,7 +964,7 @@ The result of this is undefined and will become a hard error in a future Meson r
assert(isinstance(node, mparser.ForeachClauseNode))
items = self.evaluate_statement(node.items)
- if isinstance(items, list):
+ if isinstance(items, (list, RangeHolder)):
if len(node.varnames) != 1:
raise InvalidArguments('Foreach on array does not unpack')
varname = node.varnames[0]
diff --git a/test cases/common/61 foreach/meson.build b/test cases/common/61 foreach/meson.build
index 7084e80..af60e0f 100644
--- a/test cases/common/61 foreach/meson.build
+++ b/test cases/common/61 foreach/meson.build
@@ -31,3 +31,23 @@ foreach i : items
endforeach
assert(result == ['a', 'b'], 'Continue or break in foreach failed')
+
+items = []
+iter = range(2)
+foreach i : iter
+ items += i
+endforeach
+assert(items == [0, 1])
+assert(iter[1] == 1)
+
+items = []
+foreach i : range(1, 2)
+ items += i
+endforeach
+assert(items == [1])
+
+items = []
+foreach i : range(1, 10, 2)
+ items += i
+endforeach
+assert(items == [1, 3, 5, 7, 9])