aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Mensinger <daniel@mensinger-ka.de>2021-09-01 23:18:28 +0200
committerDaniel Mensinger <daniel@mensinger-ka.de>2021-09-25 12:44:11 +0200
commitd93d01b6c5c98a77e057b914e8d66b01d2a10771 (patch)
treeca759dff3424ad09519fada202bbbbe4ad9eaf14
parent5fcb0e6525e2044e0f82bda488a51350e0f7f29f (diff)
downloadmeson-d93d01b6c5c98a77e057b914e8d66b01d2a10771.zip
meson-d93d01b6c5c98a77e057b914e8d66b01d2a10771.tar.gz
meson-d93d01b6c5c98a77e057b914e8d66b01d2a10771.tar.bz2
interpreter: Introduce StringHolder
Another commit in my quest to rid InterpreterBase from all higher level object processing logic. Additionally, there is a a logic change here, since `str.join` now uses varargs and can now accept more than one argument (and supports list flattening).
-rw-r--r--docs/markdown/Reference-manual.md1
-rw-r--r--docs/markdown/snippets/str_join.md5
-rw-r--r--mesonbuild/ast/interpreter.py3
-rw-r--r--mesonbuild/interpreter/__init__.py2
-rw-r--r--mesonbuild/interpreter/interpreter.py4
-rw-r--r--mesonbuild/interpreter/mesonmain.py5
-rw-r--r--mesonbuild/interpreter/primitives/__init__.py4
-rw-r--r--mesonbuild/interpreter/primitives/string.py174
-rw-r--r--mesonbuild/interpreterbase/__init__.py3
-rw-r--r--mesonbuild/interpreterbase/_unholder.py4
-rw-r--r--mesonbuild/interpreterbase/baseobjects.py4
-rw-r--r--mesonbuild/interpreterbase/interpreterbase.py135
-rw-r--r--test cases/common/35 string operations/meson.build1
-rw-r--r--test cases/failing/11 object arithmetic/test.json2
-rw-r--r--test cases/failing/12 string arithmetic/test.json3
15 files changed, 216 insertions, 134 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 8ef36de..31ed77e 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -2268,6 +2268,7 @@ are immutable, all operations return their results as a new string.
- `join(list_of_strings)`: the opposite of split, for example
`'.'.join(['a', 'b', 'c']` yields `'a.b.c'`.
+ *(Since 0.60.0)* more than one argument is supported and lists will be flattened.
- `replace('old_substr', 'new_str')` *(since 0.58.0)*: replaces instances of
`old_substr` in the string with `new_str` and returns a new string
diff --git a/docs/markdown/snippets/str_join.md b/docs/markdown/snippets/str_join.md
new file mode 100644
index 0000000..b430d66
--- /dev/null
+++ b/docs/markdown/snippets/str_join.md
@@ -0,0 +1,5 @@
+## Relax restrictions of `str.join()`
+
+Since 0.60.0, the [[str.join]] method can take an arbitrary number of arguments
+instead of just one list. Additionally, all lists past to [[str.join]] will now
+be flattened.
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index 4fd1378..5998e5b 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -364,7 +364,8 @@ class AstInterpreter(InterpreterBase):
mkwargs = {} # type: T.Dict[str, TYPE_nvar]
try:
if isinstance(src, str):
- result = self.string_method_call(src, node.name, margs, mkwargs)
+ from ..interpreter import Interpreter, StringHolder
+ result = StringHolder(src, T.cast(Interpreter, self)).method_call(node.name, margs, mkwargs)
elif isinstance(src, bool):
from ..interpreter import Interpreter, BooleanHolder
result = BooleanHolder(src, T.cast(Interpreter, self)).method_call(node.name, margs, mkwargs)
diff --git a/mesonbuild/interpreter/__init__.py b/mesonbuild/interpreter/__init__.py
index 90d7faf..c93dbc9 100644
--- a/mesonbuild/interpreter/__init__.py
+++ b/mesonbuild/interpreter/__init__.py
@@ -37,6 +37,7 @@ __all__ = [
'BooleanHolder',
'IntegerHolder',
+ 'StringHolder',
]
from .interpreter import Interpreter, permitted_dependency_kwargs
@@ -50,4 +51,5 @@ from .interpreterobjects import (ExecutableHolder, BuildTargetHolder, CustomTarg
from .primitives import (
BooleanHolder,
IntegerHolder,
+ StringHolder,
)
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index f94ed2d..7a935da 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -376,6 +376,8 @@ class Interpreter(InterpreterBase, HoldableObject):
# Primitives
int: P_OBJ.IntegerHolder,
bool: P_OBJ.BooleanHolder,
+ str: P_OBJ.StringHolder,
+ P_OBJ.MesonVersionString: P_OBJ.MesonVersionStringHolder,
# Meson types
mesonlib.File: OBJ.FileHolder,
@@ -2399,7 +2401,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
@typed_pos_args('join_paths', varargs=str, min_varargs=1)
@noKwargs
def func_join_paths(self, node: mparser.BaseNode, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> str:
- return self.join_path_strings(args[0])
+ return os.path.join(*args[0]).replace('\\', '/')
def run(self) -> None:
super().run()
diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py
index 637ca72..15c1082 100644
--- a/mesonbuild/interpreter/mesonmain.py
+++ b/mesonbuild/interpreter/mesonmain.py
@@ -14,8 +14,9 @@ from ..mesonlib import MachineChoice, OptionKey
from ..programs import OverrideProgram, ExternalProgram
from ..interpreter.type_checking import ENV_KW
from ..interpreterbase import (MesonInterpreterObject, FeatureNew, FeatureDeprecated,
- typed_pos_args, noArgsFlattening, noPosargs, noKwargs,
- typed_kwargs, KwargInfo, MesonVersionString, InterpreterException)
+ typed_pos_args, noArgsFlattening, noPosargs, noKwargs,
+ typed_kwargs, KwargInfo, InterpreterException)
+from .primitives import MesonVersionString
from .type_checking import NATIVE_KW, NoneType
if T.TYPE_CHECKING:
diff --git a/mesonbuild/interpreter/primitives/__init__.py b/mesonbuild/interpreter/primitives/__init__.py
index 5d16744..d6c0795 100644
--- a/mesonbuild/interpreter/primitives/__init__.py
+++ b/mesonbuild/interpreter/primitives/__init__.py
@@ -4,7 +4,11 @@
__all__ = [
'BooleanHolder',
'IntegerHolder',
+ 'StringHolder',
+ 'MesonVersionString',
+ 'MesonVersionStringHolder',
]
from .boolean import BooleanHolder
from .integer import IntegerHolder
+from .string import StringHolder, MesonVersionString, MesonVersionStringHolder
diff --git a/mesonbuild/interpreter/primitives/string.py b/mesonbuild/interpreter/primitives/string.py
new file mode 100644
index 0000000..6b7155a
--- /dev/null
+++ b/mesonbuild/interpreter/primitives/string.py
@@ -0,0 +1,174 @@
+# Copyright 2021 The Meson development team
+# SPDX-license-identifier: Apache-2.0
+
+import re
+from pathlib import PurePath
+
+import typing as T
+
+from ...mesonlib import version_compare
+from ...interpreterbase import (
+ ObjectHolder,
+ MesonOperator,
+ FeatureNew,
+ typed_operator,
+ noKwargs,
+ noPosargs,
+ typed_pos_args,
+
+ TYPE_var,
+ TYPE_kwargs,
+
+ InvalidArguments,
+)
+
+
+if T.TYPE_CHECKING:
+ # Object holders need the actual interpreter
+ from ...interpreter import Interpreter
+
+class StringHolder(ObjectHolder[str]):
+ def __init__(self, obj: str, interpreter: 'Interpreter') -> None:
+ super().__init__(obj, interpreter)
+ self.methods.update({
+ 'contains': self.contains_method,
+ 'startswith': self.startswith_method,
+ 'endswith': self.endswith_method,
+ 'format': self.format_method,
+ 'join': self.join_method,
+ 'replace': self.replace_method,
+ 'split': self.split_method,
+ 'strip': self.strip_method,
+ 'substring': self.substring_method,
+ 'to_int': self.to_int_method,
+ 'to_lower': self.to_lower_method,
+ 'to_upper': self.to_upper_method,
+ 'underscorify': self.underscorify_method,
+ 'version_compare': self.version_compare_method,
+ })
+
+
+ self.trivial_operators.update({
+ # Arithmetic
+ MesonOperator.PLUS: (str, lambda x: self.held_object + x),
+
+ # Comparison
+ MesonOperator.EQUALS: (str, lambda x: self.held_object == x),
+ MesonOperator.NOT_EQUALS: (str, lambda x: self.held_object != x),
+ MesonOperator.GREATER: (str, lambda x: self.held_object > x),
+ MesonOperator.LESS: (str, lambda x: self.held_object < x),
+ MesonOperator.GREATER_EQUALS: (str, lambda x: self.held_object >= x),
+ MesonOperator.LESS_EQUALS: (str, lambda x: self.held_object <= x),
+ })
+
+ # Use actual methods for functions that require additional checks
+ self.operators.update({
+ MesonOperator.DIV: self.op_div,
+ })
+
+ def display_name(self) -> str:
+ return 'str'
+
+ @noKwargs
+ @typed_pos_args('str.contains', str)
+ def contains_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> bool:
+ return self.held_object.find(args[0]) >= 0
+
+ @noKwargs
+ @typed_pos_args('str.startswith', str)
+ def startswith_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> bool:
+ return self.held_object.startswith(args[0])
+
+ @noKwargs
+ @typed_pos_args('str.endswith', str)
+ def endswith_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> bool:
+ return self.held_object.endswith(args[0])
+
+ @noKwargs
+ @typed_pos_args('str.format', varargs=object)
+ def format_method(self, args: T.Tuple[T.List[object]], kwargs: TYPE_kwargs) -> str:
+ arg_strings: T.List[str] = []
+ for arg in args[0]:
+ if isinstance(arg, bool): # Python boolean is upper case.
+ arg = str(arg).lower()
+ arg_strings.append(str(arg))
+
+ def arg_replace(match: T.Match[str]) -> str:
+ idx = int(match.group(1))
+ if idx >= len(arg_strings):
+ raise InvalidArguments(f'Format placeholder @{idx}@ out of range.')
+ return arg_strings[idx]
+
+ return re.sub(r'@(\d+)@', arg_replace, self.held_object)
+
+ @noKwargs
+ @typed_pos_args('str.join', varargs=str)
+ def join_method(self, args: T.Tuple[T.List[str]], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.join(args[0])
+
+ @noKwargs
+ @typed_pos_args('str.replace', str, str)
+ def replace_method(self, args: T.Tuple[str, str], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.replace(args[0], args[1])
+
+ @noKwargs
+ @typed_pos_args('str.split', optargs=[str])
+ def split_method(self, args: T.Tuple[T.Optional[str]], kwargs: TYPE_kwargs) -> T.List[str]:
+ return self.held_object.split(args[0])
+
+ @noKwargs
+ @typed_pos_args('str.strip', optargs=[str])
+ def strip_method(self, args: T.Tuple[T.Optional[str]], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.strip(args[0])
+
+ @noKwargs
+ @typed_pos_args('str.substring', optargs=[int, int])
+ def substring_method(self, args: T.Tuple[T.Optional[int], T.Optional[int]], kwargs: TYPE_kwargs) -> str:
+ start = args[0] if args[0] is not None else 0
+ end = args[1] if args[1] is not None else len(self.held_object)
+ return self.held_object[start:end]
+
+ @noKwargs
+ @noPosargs
+ def to_int_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> int:
+ try:
+ return int(self.held_object)
+ except ValueError:
+ raise InvalidArguments(f'String {self.held_object!r} cannot be converted to int')
+
+ @noKwargs
+ @noPosargs
+ def to_lower_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.lower()
+
+ @noKwargs
+ @noPosargs
+ def to_upper_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.held_object.upper()
+
+ @noKwargs
+ @noPosargs
+ def underscorify_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return re.sub(r'[^a-zA-Z0-9]', '_', self.held_object)
+
+ @noKwargs
+ @typed_pos_args('str.version_compare', str)
+ def version_compare_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> bool:
+ return version_compare(self.held_object, args[0])
+
+
+ @FeatureNew('/ with string arguments', '0.49.0')
+ @typed_operator(MesonOperator.DIV, str)
+ def op_div(self, other: str) -> str:
+ return (PurePath(self.held_object) / other).as_posix()
+
+
+class MesonVersionString(str):
+ pass
+
+class MesonVersionStringHolder(StringHolder):
+ @noKwargs
+ @typed_pos_args('str.version_compare', str)
+ def version_compare_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> bool:
+ self.interpreter.tmp_meson_version = args[0]
+ return version_compare(self.held_object, args[0])
diff --git a/mesonbuild/interpreterbase/__init__.py b/mesonbuild/interpreterbase/__init__.py
index f237c2f..d4c4a36 100644
--- a/mesonbuild/interpreterbase/__init__.py
+++ b/mesonbuild/interpreterbase/__init__.py
@@ -17,7 +17,6 @@ __all__ = [
'MesonInterpreterObject',
'ObjectHolder',
'RangeHolder',
- 'MesonVersionString',
'MutableInterpreterObject',
'MesonOperator',
@@ -129,5 +128,5 @@ from .exceptions import (
from .disabler import Disabler, is_disabled
from .helpers import check_stringlist, default_resolve_key, flatten, resolve_second_level_holders
-from .interpreterbase import MesonVersionString, InterpreterBase
+from .interpreterbase import InterpreterBase
from .operator import MesonOperator
diff --git a/mesonbuild/interpreterbase/_unholder.py b/mesonbuild/interpreterbase/_unholder.py
index 202f53b..221c52c 100644
--- a/mesonbuild/interpreterbase/_unholder.py
+++ b/mesonbuild/interpreterbase/_unholder.py
@@ -19,9 +19,7 @@ from ..mesonlib import HoldableObject, MesonBugException
import typing as T
def _unholder(obj: T.Union[TYPE_var, InterpreterObject]) -> TYPE_var:
- if isinstance(obj, str):
- return obj
- elif isinstance(obj, list):
+ if isinstance(obj, list):
return [_unholder(x) for x in obj]
elif isinstance(obj, dict):
return {k: _unholder(v) for k, v in obj.items()}
diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py
index 80cf0b5..c979f7d 100644
--- a/mesonbuild/interpreterbase/baseobjects.py
+++ b/mesonbuild/interpreterbase/baseobjects.py
@@ -129,8 +129,8 @@ class MesonInterpreterObject(InterpreterObject):
class MutableInterpreterObject:
''' Dummy class to mark the object type as mutable '''
-HoldableTypes = (HoldableObject, int, bool)
-TYPE_HoldableTypes = T.Union[HoldableObject, int, bool]
+HoldableTypes = (HoldableObject, int, bool, str)
+TYPE_HoldableTypes = T.Union[HoldableObject, int, bool, str]
InterpreterObjectTypeVar = T.TypeVar('InterpreterObjectTypeVar', bound=TYPE_HoldableTypes)
class ObjectHolder(InterpreterObject, T.Generic[InterpreterObjectTypeVar]):
diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py
index 6e942b7..9ae50c8 100644
--- a/mesonbuild/interpreterbase/interpreterbase.py
+++ b/mesonbuild/interpreterbase/interpreterbase.py
@@ -62,6 +62,8 @@ HolderMapType = T.Dict[
T.Union[
T.Type[mesonlib.HoldableObject],
T.Type[int],
+ T.Type[bool],
+ T.Type[str],
],
# For some reason, this has to be a callable and can't just be ObjectHolder[InterpreterObjectTypeVar]
T.Callable[[InterpreterObjectTypeVar, 'Interpreter'], ObjectHolder[InterpreterObjectTypeVar]]
@@ -84,11 +86,8 @@ def _holderify_result(types: T.Union[None, T.Type, T.Tuple[T.Type, ...]] = None)
return T.cast(__FN, wrapper)
return inner
-class MesonVersionString(str):
- pass
-
class InterpreterBase:
- elementary_types = (str, list)
+ elementary_types = (list, )
def __init__(self, source_root: str, subdir: str, subproject: str):
self.source_root = source_root
@@ -128,9 +127,6 @@ class InterpreterBase:
me.file = mesonfile
raise me
- def join_path_strings(self, args: T.Sequence[str]) -> str:
- return os.path.join(*args).replace('\\', '/')
-
def parse_project(self) -> None:
"""
Parses project() and initializes languages, compilers etc. Do this
@@ -203,7 +199,7 @@ class InterpreterBase:
elif isinstance(cur, mparser.MethodNode):
return self.method_call(cur)
elif isinstance(cur, mparser.StringNode):
- return cur.value
+ return self._holderify(cur.value)
elif isinstance(cur, mparser.BooleanNode):
return self._holderify(cur.value)
elif isinstance(cur, mparser.IfClauseNode):
@@ -259,7 +255,7 @@ class InterpreterBase:
def resolve_key(key: mparser.BaseNode) -> str:
if not isinstance(key, mparser.StringNode):
FeatureNew.single_use('Dictionary entry using non literal key', '0.53.0', self.subproject)
- str_key = self.evaluate_statement(key)
+ str_key = _unholder(self.evaluate_statement(key))
if not isinstance(str_key, str):
raise InvalidArguments('Key must be a string')
return str_key
@@ -382,13 +378,13 @@ class InterpreterBase:
# Use type: ignore because mypy will complain that we are comparing two Unions,
# but we actually guarantee earlier that both types are the same
elif node.ctype == '<':
- return val1 < val2 # type: ignore
+ return val1 < val2
elif node.ctype == '<=':
- return val1 <= val2 # type: ignore
+ return val1 <= val2
elif node.ctype == '>':
- return val1 > val2 # type: ignore
+ return val1 > val2
elif node.ctype == '>=':
- return val1 >= val2 # type: ignore
+ return val1 >= val2
else:
raise InvalidCode('You broke my compare eval.')
@@ -436,14 +432,6 @@ class InterpreterBase:
raise InterpreterException(f'Argument to negation ({v}) is not an InterpreterObject but {type(v).__name__}.')
return v.operator_call(MesonOperator.UMINUS, None)
- @FeatureNew('/ with string arguments', '0.49.0')
- def evaluate_path_join(self, l: str, r: str) -> str:
- if not isinstance(l, str):
- raise InvalidCode('The division operator can only append to a string.')
- if not isinstance(r, str):
- raise InvalidCode('The division operator can only append a string.')
- return self.join_path_strings((l, r))
-
def evaluate_arithmeticstatement(self, cur: mparser.ArithmeticNode) -> T.Union[TYPE_var, InterpreterObject]:
l = self.evaluate_statement(cur.left)
if isinstance(l, Disabler):
@@ -484,9 +472,7 @@ class InterpreterBase:
raise InvalidCode('Multiplication works only with integers.')
raise mesonlib.MesonBugException('The integer was not held by an ObjectHolder!')
elif cur.operation == 'div':
- if isinstance(l, str) and isinstance(r, str):
- return self.evaluate_path_join(l, r)
- raise InvalidCode('Division works only with strings or integers.')
+ raise mesonlib.MesonBugException('The integer or string was not held by an ObjectHolder!')
elif cur.operation == 'mod':
if not isinstance(l, int) or not isinstance(r, int):
raise InvalidCode('Modulo works only with integers.')
@@ -508,7 +494,8 @@ class InterpreterBase:
return self.evaluate_statement(node.falseblock)
@FeatureNew('format strings', '0.58.0')
- def evaluate_fstring(self, node: mparser.FormatStringNode) -> TYPE_var:
+ @_holderify_result(str)
+ def evaluate_fstring(self, node: mparser.FormatStringNode) -> str:
assert isinstance(node, mparser.FormatStringNode)
def replace(match: T.Match[str]) -> str:
@@ -545,8 +532,8 @@ class InterpreterBase:
if len(node.varnames) != 2:
raise InvalidArguments('Foreach on dict unpacks key and value')
for key, value in sorted(items.items()):
- self.set_variable(node.varnames[0], key)
- self.set_variable(node.varnames[1], value)
+ self.set_variable(node.varnames[0], self._holderify(key))
+ self.set_variable(node.varnames[1], self._holderify(value, permissive=True))
try:
self.evaluate_codeblock(node.block)
except ContinueRequest:
@@ -656,7 +643,7 @@ class InterpreterBase:
if is_disabled(args, kwargs):
return Disabler()
if isinstance(obj, str):
- return self._holderify(self.string_method_call(obj, method_name, args, kwargs))
+ raise mesonlib.MesonBugException('Strings are now wrapped in object holders!')
if isinstance(obj, bool):
raise mesonlib.MesonBugException('Booleans are now wrapped in object holders!')
if isinstance(obj, int):
@@ -680,8 +667,6 @@ class InterpreterBase:
# TODO: remove `permissive` once all primitives are ObjectHolders
if res is None:
return None
- if isinstance(res, str):
- return res
elif isinstance(res, list):
return [self._holderify(x, permissive=permissive) for x in res]
elif isinstance(res, dict):
@@ -722,96 +707,6 @@ class InterpreterBase:
return s
return None
- @noKwargs
- def string_method_call(self, obj: str, method_name: str, posargs: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[str, int, bool, T.List[str]]:
- if method_name == 'strip':
- s1 = self._get_one_string_posarg(posargs, 'strip')
- if s1 is not None:
- return obj.strip(s1)
- return obj.strip()
- elif method_name == 'format':
- return self.format_string(obj, posargs)
- elif method_name == 'to_upper':
- return obj.upper()
- elif method_name == 'to_lower':
- return obj.lower()
- elif method_name == 'underscorify':
- return re.sub(r'[^a-zA-Z0-9]', '_', obj)
- elif method_name == 'split':
- s2 = self._get_one_string_posarg(posargs, 'split')
- if s2 is not None:
- return obj.split(s2)
- return obj.split()
- elif method_name == 'startswith' or method_name == 'contains' or method_name == 'endswith':
- s3 = posargs[0]
- if not isinstance(s3, str):
- raise InterpreterException('Argument must be a string.')
- if method_name == 'startswith':
- return obj.startswith(s3)
- elif method_name == 'contains':
- return obj.find(s3) >= 0
- return obj.endswith(s3)
- elif method_name == 'to_int':
- try:
- return int(obj)
- except Exception:
- raise InterpreterException(f'String {obj!r} cannot be converted to int')
- elif method_name == 'join':
- if len(posargs) != 1:
- raise InterpreterException('Join() takes exactly one argument.')
- strlist = posargs[0]
- check_stringlist(strlist)
- assert isinstance(strlist, list) # Required for mypy
- return obj.join(strlist)
- elif method_name == 'version_compare':
- if len(posargs) != 1:
- raise InterpreterException('Version_compare() takes exactly one argument.')
- cmpr = posargs[0]
- if not isinstance(cmpr, str):
- raise InterpreterException('Version_compare() argument must be a string.')
- if isinstance(obj, MesonVersionString):
- self.tmp_meson_version = cmpr
- return mesonlib.version_compare(obj, cmpr)
- elif method_name == 'substring':
- if len(posargs) > 2:
- raise InterpreterException('substring() takes maximum two arguments.')
- start = 0
- end = len(obj)
- if len (posargs) > 0:
- if not isinstance(posargs[0], int):
- raise InterpreterException('substring() argument must be an int')
- start = posargs[0]
- if len (posargs) > 1:
- if not isinstance(posargs[1], int):
- raise InterpreterException('substring() argument must be an int')
- end = posargs[1]
- return obj[start:end]
- elif method_name == 'replace':
- FeatureNew.single_use('str.replace', '0.58.0', self.subproject)
- if len(posargs) != 2:
- raise InterpreterException('replace() takes exactly two arguments.')
- if not isinstance(posargs[0], str) or not isinstance(posargs[1], str):
- raise InterpreterException('replace() requires that both arguments be strings')
- return obj.replace(posargs[0], posargs[1])
- raise InterpreterException('Unknown method "%s" for a string.' % method_name)
-
- def format_string(self, templ: str, args: T.List[TYPE_var]) -> str:
- arg_strings = []
- for arg in args:
- if isinstance(arg, mparser.BaseNode):
- arg = self.evaluate_statement(arg)
- if isinstance(arg, bool): # Python boolean is upper case.
- arg = str(arg).lower()
- arg_strings.append(str(arg))
-
- def arg_replace(match: T.Match[str]) -> str:
- idx = int(match.group(1))
- if idx >= len(arg_strings):
- raise InterpreterException(f'Format placeholder @{idx}@ out of range.')
- return arg_strings[idx]
-
- return re.sub(r'@(\d+)@', arg_replace, templ)
-
def unknown_function_called(self, func_name: str) -> None:
raise InvalidCode('Unknown function "%s".' % func_name)
diff --git a/test cases/common/35 string operations/meson.build b/test cases/common/35 string operations/meson.build
index ca0342d..3405e95 100644
--- a/test cases/common/35 string operations/meson.build
+++ b/test cases/common/35 string operations/meson.build
@@ -61,6 +61,7 @@ assert('@0@'.format(true) == 'true', 'bool string formatting failed')
assert(' '.join(['a', 'b', 'c']) == 'a b c', 'join() array broken')
assert(''.join(['a', 'b', 'c']) == 'abc', 'empty join() broken')
assert(' '.join(['a']) == 'a', 'single join broken')
+assert(' '.join(['a'], ['b', ['c']], 'd') == 'a b c d', 'varargs join broken')
version_number = '1.2.8'
diff --git a/test cases/failing/11 object arithmetic/test.json b/test cases/failing/11 object arithmetic/test.json
index 5339fac..84f5c46 100644
--- a/test cases/failing/11 object arithmetic/test.json
+++ b/test cases/failing/11 object arithmetic/test.json
@@ -2,7 +2,7 @@
"stdout": [
{
"match": "re",
- "line": "test cases/failing/11 object arithmetic/meson\\.build:3:0: ERROR: Invalid use of addition: .*"
+ "line": "test cases/failing/11 object arithmetic/meson\\.build:3:0: ERROR: The `\\+` of str does not accept objects of type MesonMain .*"
}
]
}
diff --git a/test cases/failing/12 string arithmetic/test.json b/test cases/failing/12 string arithmetic/test.json
index 476f9bb..c762229 100644
--- a/test cases/failing/12 string arithmetic/test.json
+++ b/test cases/failing/12 string arithmetic/test.json
@@ -1,8 +1,7 @@
{
"stdout": [
{
- "match": "re",
- "line": "test cases/failing/12 string arithmetic/meson\\.build:3:0: ERROR: Invalid use of addition: .*"
+ "line": "test cases/failing/12 string arithmetic/meson.build:3:0: ERROR: The `+` of str does not accept objects of type int (3)"
}
]
}