aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/interpreterbase/interpreterbase.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/interpreterbase/interpreterbase.py')
-rw-r--r--mesonbuild/interpreterbase/interpreterbase.py100
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