diff options
Diffstat (limited to 'mesonbuild/interpreterbase/interpreterbase.py')
-rw-r--r-- | mesonbuild/interpreterbase/interpreterbase.py | 100 |
1 files changed, 41 insertions, 59 deletions
diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index b6e7d0d..d990e61 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -15,7 +15,7 @@ # This class contains the basic functionality needed to run any interpreter # or an interpreter-based tool. -from .. import mparser, mesonlib, mlog +from .. import mparser, mesonlib from .. import environment from .baseobjects import ( @@ -29,6 +29,8 @@ from .baseobjects import ( TYPE_elementary, TYPE_var, TYPE_kwargs, + + HoldableTypes, ) from .exceptions import ( @@ -54,7 +56,10 @@ if T.TYPE_CHECKING: from ..interpreter import Interpreter HolderMapType = T.Dict[ - T.Type[mesonlib.HoldableObject], + T.Union[ + T.Type[mesonlib.HoldableObject], + T.Type[int], + ], # For some reason, this has to be a callable and can't just be ObjectHolder[InterpreterObjectTypeVar] T.Callable[[InterpreterObjectTypeVar, 'Interpreter'], ObjectHolder[InterpreterObjectTypeVar]] ] @@ -68,7 +73,7 @@ class MesonVersionString(str): pass class InterpreterBase: - elementary_types = (int, str, bool, list) + elementary_types = (str, bool, list) def __init__(self, source_root: str, subdir: str, subproject: str): self.source_root = source_root @@ -197,7 +202,7 @@ class InterpreterBase: elif isinstance(cur, mparser.DictNode): return self.evaluate_dictstatement(cur) elif isinstance(cur, mparser.NumberNode): - return cur.value + return self._holderify(cur.value) elif isinstance(cur, mparser.AndNode): return self.evaluate_andstatement(cur) elif isinstance(cur, mparser.OrNode): @@ -396,13 +401,14 @@ class InterpreterBase: raise InterpreterException('Second argument to "or" is not a boolean.') return r - def evaluate_uminusstatement(self, cur: mparser.UMinusNode) -> T.Union[int, Disabler]: + def evaluate_uminusstatement(self, cur: mparser.UMinusNode) -> T.Union[TYPE_var, InterpreterObject]: v = self.evaluate_statement(cur.value) if isinstance(v, Disabler): return v - if not isinstance(v, int): - raise InterpreterException('Argument to negation is not an integer.') - return -v + # TYPING TODO: Remove this check once `evaluate_statement` only returns InterpreterObjects + if not isinstance(v, InterpreterObject): + raise InterpreterException(f'Argument to negation ({v}) is not an InterpreterObject but {type(v).__name__}.') + return self._holderify(v.operator_call(MesonOperator.UMINUS, None)) @FeatureNew('/ with string arguments', '0.49.0') def evaluate_path_join(self, l: str, r: str) -> str: @@ -412,16 +418,7 @@ class InterpreterBase: raise InvalidCode('The division operator can only append a string.') return self.join_path_strings((l, r)) - def evaluate_division(self, l: T.Any, r: T.Any) -> T.Union[int, str]: - if isinstance(l, str) or isinstance(r, str): - return self.evaluate_path_join(l, r) - if isinstance(l, int) and isinstance(r, int): - if r == 0: - raise InvalidCode('Division by zero.') - return l // r - raise InvalidCode('Division works only with strings or integers.') - - def evaluate_arithmeticstatement(self, cur: mparser.ArithmeticNode) -> T.Union[int, str, dict, list, Disabler]: + def evaluate_arithmeticstatement(self, cur: mparser.ArithmeticNode) -> T.Union[TYPE_var, InterpreterObject]: l = self.evaluate_statement(cur.left) if isinstance(l, Disabler): return l @@ -455,17 +452,19 @@ class InterpreterBase: elif cur.operation == 'sub': if not isinstance(l, int) or not isinstance(r, int): raise InvalidCode('Subtraction works only with integers.') - return l - r + raise mesonlib.MesonBugException('The integer was not held by an ObjectHolder!') elif cur.operation == 'mul': if not isinstance(l, int) or not isinstance(r, int): raise InvalidCode('Multiplication works only with integers.') - return l * r + raise mesonlib.MesonBugException('The integer was not held by an ObjectHolder!') elif cur.operation == 'div': - return self.evaluate_division(l, r) + if isinstance(l, str) and isinstance(r, str): + return self.evaluate_path_join(l, r) + raise InvalidCode('Division works only with strings or integers.') elif cur.operation == 'mod': if not isinstance(l, int) or not isinstance(r, int): raise InvalidCode('Modulo works only with integers.') - return l % r + raise mesonlib.MesonBugException('The integer was not held by an ObjectHolder!') else: raise InvalidCode('You broke me.') @@ -488,7 +487,7 @@ class InterpreterBase: def replace(match: T.Match[str]) -> str: var = str(match.group(1)) try: - val = self.variables[var] + val = _unholder(self.variables[var]) if not isinstance(val, (str, int, float, bool)): raise InvalidCode(f'Identifier "{var}" does not name a formattable variable ' + '(has to be an integer, a string, a floating point number or a boolean).') @@ -508,7 +507,7 @@ class InterpreterBase: raise InvalidArguments('Foreach on array does not unpack') varname = node.varnames[0] for item in items: - self.set_variable(varname, item) + self.set_variable(varname, self._holderify(item, permissive=True)) try: self.evaluate_codeblock(node.block) except ContinueRequest: @@ -538,15 +537,12 @@ class InterpreterBase: # Remember that all variables are immutable. We must always create a # full new variable and then assign it. old_variable = self.get_variable(varname) - new_value = None # type: T.Union[str, int, float, bool, dict, list] + # TYPING TODO: This should only be InterpreterObject in the future + new_value: T.Union[None, TYPE_var, InterpreterObject] = None if isinstance(old_variable, str): if not isinstance(addition, str): raise InvalidArguments('The += operator requires a string on the right hand side if the variable on the left is a string') new_value = old_variable + addition - elif isinstance(old_variable, int): - if not isinstance(addition, int): - raise InvalidArguments('The += operator requires an int on the right hand side if the variable on the left is an int') - new_value = old_variable + addition elif isinstance(old_variable, list): if isinstance(addition, list): new_value = old_variable + addition @@ -556,6 +552,9 @@ class InterpreterBase: if not isinstance(addition, dict): raise InvalidArguments('The += operator requires a dict on the right hand side if the variable on the left is a dict') new_value = {**old_variable, **addition} + elif isinstance(old_variable, InterpreterObject): + # TODO: don't make _unholder permissive + new_value = self._holderify(old_variable.operator_call(MesonOperator.PLUS, _unholder(addition, permissive=True))) # Add other data types here. else: raise InvalidArguments('The += operator currently only works with arrays, dicts, strings or ints') @@ -569,7 +568,7 @@ class InterpreterBase: if not hasattr(iobject, '__getitem__'): raise InterpreterException( 'Tried to index an object that doesn\'t support indexing.') - index = self.evaluate_statement(node.index) + index = _unholder(self.evaluate_statement(node.index)) if isinstance(iobject, dict): if not isinstance(index, str): @@ -630,11 +629,11 @@ class InterpreterBase: if is_disabled(args, kwargs): return Disabler() if isinstance(obj, str): - return self.string_method_call(obj, method_name, args, kwargs) + return self._holderify(self.string_method_call(obj, method_name, args, kwargs)) if isinstance(obj, bool): - return self.bool_method_call(obj, method_name, args, kwargs) + return self._holderify(self.bool_method_call(obj, method_name, args, kwargs)) if isinstance(obj, int): - return self.int_method_call(obj, method_name, args, kwargs) + raise mesonlib.MesonBugException('Integers are now wrapped in object holders!') if isinstance(obj, list): return self.array_method_call(obj, method_name, args, kwargs) if isinstance(obj, dict): @@ -656,16 +655,17 @@ class InterpreterBase: obj.current_node = node return self._holderify(obj.method_call(method_name, args, kwargs)) - def _holderify(self, res: T.Union[TYPE_var, InterpreterObject, None]) -> T.Union[TYPE_elementary, InterpreterObject]: + def _holderify(self, res: T.Union[TYPE_var, InterpreterObject, None], *, permissive: bool = False) -> T.Union[TYPE_elementary, InterpreterObject]: + # TODO: remove `permissive` once all primitives are ObjectHolders if res is None: return None - if isinstance(res, (int, bool, str)): + if isinstance(res, (bool, str)): return res elif isinstance(res, list): - return [self._holderify(x) for x in res] + return [self._holderify(x, permissive=permissive) for x in res] elif isinstance(res, dict): - return {k: self._holderify(v) for k, v in res.items()} - elif isinstance(res, mesonlib.HoldableObject): + return {k: self._holderify(v, permissive=permissive) for k, v in res.items()} + elif isinstance(res, HoldableTypes): # Always check for an exact match first. cls = self.holder_map.get(type(res), None) if cls is not None: @@ -678,6 +678,8 @@ class InterpreterBase: return cls(res, T.cast('Interpreter', self)) raise mesonlib.MesonBugException(f'Object {res} of type {type(res).__name__} is neither in self.holder_map nor self.bound_holder_map.') elif isinstance(res, ObjectHolder): + if permissive: + return res raise mesonlib.MesonBugException(f'Returned object {res} of type {type(res).__name__} is an object holder.') elif isinstance(res, MesonInterpreterObject): return res @@ -711,26 +713,6 @@ class InterpreterBase: else: raise InterpreterException('Unknown method "%s" for a boolean.' % method_name) - @noKwargs - def int_method_call(self, obj: int, method_name: str, posargs: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[str, bool]: - if method_name == 'is_even': - if not posargs: - return obj % 2 == 0 - else: - raise InterpreterException('int.is_even() must have no arguments.') - elif method_name == 'is_odd': - if not posargs: - return obj % 2 != 0 - else: - raise InterpreterException('int.is_odd() must have no arguments.') - elif method_name == 'to_string': - if not posargs: - return str(obj) - else: - raise InterpreterException('int.to_string() must have no arguments.') - else: - raise InterpreterException('Unknown method "%s" for an integer.' % method_name) - @staticmethod def _get_one_string_posarg(posargs: T.List[TYPE_var], method_name: str) -> str: if len(posargs) > 1: @@ -856,7 +838,7 @@ class InterpreterBase: return False return check_contains([_unholder(x) for x in obj]) elif method_name == 'length': - return len(obj) + return self._holderify(len(obj)) elif method_name == 'get': index = posargs[0] fallback = None |