diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2016-11-19 22:11:20 +0200 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2016-11-19 22:11:20 +0200 |
commit | c41805f012fe7305dfba978f6cb70741d782c57f (patch) | |
tree | 1eb0ed9af19cd6f4eed82a0637a6330da2034a71 | |
parent | 7e8872236d1c8b8baca910a3c03b0399ed94dbea (diff) | |
download | meson-c41805f012fe7305dfba978f6cb70741d782c57f.zip meson-c41805f012fe7305dfba978f6cb70741d782c57f.tar.gz meson-c41805f012fe7305dfba978f6cb70741d782c57f.tar.bz2 |
Moved more stuff, can now parse all of common tests.
-rwxr-xr-x | mesonast.py | 18 | ||||
-rw-r--r-- | mesonbuild/astinterpreter.py | 54 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 283 | ||||
-rw-r--r-- | mesonbuild/interpreterbase.py | 287 |
4 files changed, 352 insertions, 290 deletions
diff --git a/mesonast.py b/mesonast.py index 7f5fd90..e24c31e 100755 --- a/mesonast.py +++ b/mesonast.py @@ -24,8 +24,9 @@ # - reindent? import mesonbuild.astinterpreter - -import sys +from mesonbuild.mesonlib import MesonException +from mesonbuild import mlog +import sys, traceback if __name__ == '__main__': if len(sys.argv) == 1: @@ -33,4 +34,15 @@ if __name__ == '__main__': else: source_root = sys.argv[1] ast = mesonbuild.astinterpreter.AstInterpreter(source_root, '') - ast.dump() + try: + ast.dump() + except Exception as e: + if isinstance(e, MesonException): + if hasattr(e, 'file') and hasattr(e, 'lineno') and hasattr(e, 'colno'): + mlog.log(mlog.red('\nMeson encountered an error in file %s, line %d, column %d:' % (e.file, e.lineno, e.colno))) + else: + mlog.log(mlog.red('\nMeson encountered an error:')) + mlog.log(e) + else: + traceback.print_exc() + sys.exit(1) diff --git a/mesonbuild/astinterpreter.py b/mesonbuild/astinterpreter.py index 571ecfd..247f7d9 100644 --- a/mesonbuild/astinterpreter.py +++ b/mesonbuild/astinterpreter.py @@ -34,22 +34,52 @@ class MockStaticLibrary(interpreterbase.InterpreterObject): class MockSharedLibrary(interpreterbase.InterpreterObject): pass +class MockCustomTarget(interpreterbase.InterpreterObject): + pass + +class MockRunTarget(interpreterbase.InterpreterObject): + pass + class AstInterpreter(interpreterbase.InterpreterBase): def __init__(self, source_root, subdir): super().__init__(source_root, subdir) self.funcs.update({'project' : self.func_do_nothing, 'test' : self.func_do_nothing, + 'benchmark' : self.func_do_nothing, 'install_headers' : self.func_do_nothing, 'install_man' : self.func_do_nothing, 'install_data' : self.func_do_nothing, + 'install_subdir' : self.func_do_nothing, 'configuration_data' : self.func_do_nothing, 'configure_file' : self.func_do_nothing, 'find_program' : self.func_do_nothing, + 'include_directories' : self.func_do_nothing, + 'add_global_arguments' : self.func_do_nothing, + 'add_global_link_arguments' : self.func_do_nothing, + 'add_project_arguments' : self.func_do_nothing, + 'add_project_link_arguments' : self.func_do_nothing, + 'message' : self.func_do_nothing, + 'generator' : self.func_do_nothing, + 'error' : self.func_do_nothing, + 'run_command' : self.func_do_nothing, + 'assert' : self.func_do_nothing, + 'subproject' : self.func_do_nothing, + 'dependency' : self.func_do_nothing, + 'get_option' : self.func_do_nothing, + 'join_paths' : self.func_do_nothing, + 'environment' : self.func_do_nothing, + 'import' : self.func_do_nothing, + 'vcs_tag' : self.func_do_nothing, + 'add_languages' : self.func_do_nothing, + 'declare_dependency' : self.func_do_nothing, 'files' : self.func_files, 'executable': self.func_executable, 'static_library' : self.func_static_lib, 'shared_library' : self.func_shared_lib, + 'library' : self.func_library, 'build_target' : self.func_build_target, + 'custom_target' : self.func_custom_target, + 'run_target' : self.func_run_target, 'subdir' : self.func_subdir, 'set_variable' : self.func_set_variable, 'get_variable' : self.func_get_variable, @@ -57,7 +87,10 @@ class AstInterpreter(interpreterbase.InterpreterBase): }) def func_do_nothing(self, *args, **kwargs): - return DontCareObject() + return True + + def method_call(self, node): + return True def func_executable(self, *args, **kwargs): return MockExecutable() @@ -68,6 +101,15 @@ class AstInterpreter(interpreterbase.InterpreterBase): def func_shared_lib(self, *args, **kwargs): return MockSharedLibrary() + def func_library(self, *args, **kwargs): + return self.func_shared_lib(*args, **kwargs) + + def func_custom_target(self, *args, **kwargs): + return MockCustomTarget() + + def func_run_target(self, *args, **kwargs): + return MockRunTarget() + def func_subdir(self, node, args, kwargs): prev_subdir = self.subdir subdir = os.path.join(prev_subdir, args[0]) @@ -93,8 +135,14 @@ class AstInterpreter(interpreterbase.InterpreterBase): return [args] return args - def method_call(self, node): - return DontCareObject() + def evaluate_arithmeticstatement(self, cur): + return 0 + + def evaluate_plusassign(self, node): + return 0 + + def evaluate_indexing(self, node): + return 0 def dump(self): self.load_root_meson_file() diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index cc75848..cd10c07 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -2277,97 +2277,6 @@ requirements use the version keyword argument instead.''') if not os.path.isfile(fname): raise InterpreterException('Tried to add non-existing source file %s.' % s) - def bool_method_call(self, obj, method_name, args): - obj = self.to_native(obj) - (posargs, _) = self.reduce_arguments(args) - if method_name == 'to_string': - if len(posargs) == 0: - if obj == True: - return 'true' - else: - return 'false' - elif len(posargs) == 2 and isinstance(posargs[0], str) and isinstance(posargs[1], str): - if obj == True: - return posargs[0] - else: - return posargs[1] - else: - raise InterpreterException('bool.to_string() must have either no arguments or exactly two string arguments that signify what values to return for true and false.') - elif method_name == 'to_int': - if obj == True: - return 1 - else: - return 0 - else: - raise InterpreterException('Unknown method "%s" for a boolean.' % method_name) - - def int_method_call(self, obj, method_name, args): - obj = self.to_native(obj) - (posargs, _) = self.reduce_arguments(args) - if method_name == 'is_even': - if len(posargs) == 0: - return obj % 2 == 0 - else: - raise InterpreterException('int.is_even() must have no arguments.') - elif method_name == 'is_odd': - if len(posargs) == 0: - return obj % 2 != 0 - else: - raise InterpreterException('int.is_odd() must have no arguments.') - else: - raise InterpreterException('Unknown method "%s" for an integer.' % method_name) - - def string_method_call(self, obj, method_name, args): - obj = self.to_native(obj) - (posargs, _) = self.reduce_arguments(args) - if method_name == 'strip': - return obj.strip() - elif method_name == 'format': - return self.format_string(obj, args) - 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': - if len(posargs) > 1: - raise InterpreterException('Split() must have at most one argument.') - elif len(posargs) == 1: - s = posargs[0] - if not isinstance(s, str): - raise InterpreterException('Split() argument must be a string') - return obj.split(s) - else: - return obj.split() - elif method_name == 'startswith' or method_name == 'contains' or method_name == 'endswith': - s = posargs[0] - if not isinstance(s, str): - raise InterpreterException('Argument must be a string.') - if method_name == 'startswith': - return obj.startswith(s) - elif method_name == 'contains': - return obj.find(s) >= 0 - return obj.endswith(s) - elif method_name == 'to_int': - try: - return int(obj) - except Exception: - raise InterpreterException('String {!r} cannot be converted to int'.format(obj)) - elif method_name == 'join': - if len(posargs) != 1: - raise InterpreterException('Join() takes exactly one argument.') - strlist = posargs[0] - check_stringlist(strlist) - 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.') - return mesonlib.version_compare(obj, cmpr) - raise InterpreterException('Unknown method "%s" for a string.' % method_name) def format_string(self, templ, args): templ = self.to_native(templ) @@ -2380,32 +2289,6 @@ requirements use the version keyword argument instead.''') templ = templ.replace('@{}@'.format(i), str(arg)) return templ - def method_call(self, node): - invokable = node.source_object - if isinstance(invokable, mparser.IdNode): - object_name = invokable.value - obj = self.get_variable(object_name) - else: - obj = self.evaluate_statement(invokable) - method_name = node.name - args = node.args - if isinstance(obj, mparser.StringNode): - obj = obj.get_value() - if isinstance(obj, str): - return self.string_method_call(obj, method_name, args) - if isinstance(obj, bool): - return self.bool_method_call(obj, method_name, args) - if isinstance(obj, int): - return self.int_method_call(obj, method_name, args) - if isinstance(obj, list): - return self.array_method_call(obj, method_name, self.reduce_arguments(args)[0]) - if not isinstance(obj, InterpreterObject): - raise InvalidArguments('Variable "%s" is not callable.' % object_name) - (args, kwargs) = self.reduce_arguments(args) - if method_name == 'extract_objects': - self.validate_extraction(obj.held_object) - return obj.method_call(method_name, self.flatten(args), kwargs) - # Only permit object extraction from the same subproject def validate_extraction(self, buildtarget): if not self.subdir.startswith(self.subproject_dir): @@ -2417,20 +2300,6 @@ requirements use the version keyword argument instead.''') if self.subdir.split('/')[1] != buildtarget.subdir.split('/')[1]: raise InterpreterException('Tried to extract objects from a different subproject.') - def array_method_call(self, obj, method_name, args): - if method_name == 'contains': - return self.check_contains(obj, args) - elif method_name == 'length': - return len(obj) - elif method_name == 'get': - index = args[0] - if not isinstance(index, int): - raise InvalidArguments('Array index must be a number.') - if index < -len(obj) or index >= len(obj): - raise InvalidArguments('Array index %s is out of bounds for array of size %d.' % (index, len(obj))) - return obj[index] - raise InterpreterException('Arrays do not have a method called "%s".' % method_name) - def check_contains(self, obj, args): if len(args) != 1: raise InterpreterException('Contains method takes exactly one argument.') @@ -2447,157 +2316,5 @@ requirements use the version keyword argument instead.''') pass return False - def evaluate_ternary(self, node): - assert(isinstance(node, mparser.TernaryNode)) - result = self.evaluate_statement(node.condition) - if not isinstance(result, bool): - raise InterpreterException('Ternary condition is not boolean.') - if result: - return self.evaluate_statement(node.trueblock) - else: - return self.evaluate_statement(node.falseblock) - - def evaluate_foreach(self, node): - assert(isinstance(node, mparser.ForeachClauseNode)) - varname = node.varname.value - items = self.evaluate_statement(node.items) - if not isinstance(items, list): - raise InvalidArguments('Items of foreach loop is not an array') - for item in items: - self.set_variable(varname, item) - self.evaluate_codeblock(node.block) - - def evaluate_plusassign(self, node): - assert(isinstance(node, mparser.PlusAssignmentNode)) - varname = node.var_name - addition = self.evaluate_statement(node.value) - # Remember that all variables are immutable. We must always create a - # full new variable and then assign it. - old_variable = self.get_variable(varname) - 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 not isinstance(old_variable, list): - raise InvalidArguments('The += operator currently only works with arrays, strings or ints ') - # Add other data types here. - else: - if isinstance(addition, list): - new_value = old_variable + addition - else: - new_value = old_variable + [addition] - self.set_variable(varname, new_value) - - def evaluate_indexing(self, node): - assert(isinstance(node, mparser.IndexNode)) - iobject = self.evaluate_statement(node.iobject) - if not isinstance(iobject, list): - raise InterpreterException('Tried to index a non-array object.') - index = self.evaluate_statement(node.index) - if not isinstance(index, int): - raise InterpreterException('Index value is not an integer.') - if index < -len(iobject) or index >= len(iobject): - raise InterpreterException('Index %d out of bounds of array of size %d.' % (index, len(iobject))) - return iobject[index] - - def is_elementary_type(self, v): - return isinstance(v, (int, float, str, bool, list)) - - def evaluate_comparison(self, node): - v1 = self.evaluate_statement(node.left) - v2 = self.evaluate_statement(node.right) - if self.is_elementary_type(v1): - val1 = v1 - else: - val1 = v1.value - if self.is_elementary_type(v2): - val2 = v2 - else: - val2 = v2.value - if node.ctype == '==': - return val1 == val2 - elif node.ctype == '!=': - return val1 != val2 - elif node.ctype == '<': - return val1 < val2 - elif node.ctype == '<=': - return val1 <= val2 - elif node.ctype == '>': - return val1 > val2 - elif node.ctype == '>=': - return val1 >= val2 - else: - raise InvalidCode('You broke my compare eval.') - - def evaluate_andstatement(self, cur): - l = self.evaluate_statement(cur.left) - if isinstance(l, mparser.BooleanNode): - l = l.value - if not isinstance(l, bool): - raise InterpreterException('First argument to "and" is not a boolean.') - if not l: - return False - r = self.evaluate_statement(cur.right) - if isinstance(r, mparser.BooleanNode): - r = r.value - if not isinstance(r, bool): - raise InterpreterException('Second argument to "and" is not a boolean.') - return r - - def evaluate_orstatement(self, cur): - l = self.evaluate_statement(cur.left) - if isinstance(l, mparser.BooleanNode): - l = l.get_value() - if not isinstance(l, bool): - raise InterpreterException('First argument to "or" is not a boolean.') - if l: - return True - r = self.evaluate_statement(cur.right) - if isinstance(r, mparser.BooleanNode): - r = r.get_value() - if not isinstance(r, bool): - raise InterpreterException('Second argument to "or" is not a boolean.') - return r - - def evaluate_uminusstatement(self, cur): - v = self.evaluate_statement(cur.value) - if isinstance(v, mparser.NumberNode): - v = v.value - if not isinstance(v, int): - raise InterpreterException('Argument to negation is not an integer.') - return -v - - def evaluate_arithmeticstatement(self, cur): - l = self.to_native(self.evaluate_statement(cur.left)) - r = self.to_native(self.evaluate_statement(cur.right)) - - if cur.operation == 'add': - try: - return l + r - except Exception as e: - raise InvalidCode('Invalid use of addition: ' + str(e)) - elif cur.operation == 'sub': - if not isinstance(l, int) or not isinstance(r, int): - raise InvalidCode('Subtraction works only with integers.') - return l - r - elif cur.operation == 'mul': - if not isinstance(l, int) or not isinstance(r, int): - raise InvalidCode('Multiplication works only with integers.') - return l * r - elif cur.operation == 'div': - if not isinstance(l, int) or not isinstance(r, int): - raise InvalidCode('Division works only with integers.') - return l // r - elif cur.operation == 'mod': - if not isinstance(l, int) or not isinstance(r, int): - raise InvalidCode('Modulo works only with integers.') - return l % r - else: - raise InvalidCode('You broke me.') - def is_subproject(self): return self.subproject != '' diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index 4268597..66ffe3a 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -203,7 +203,6 @@ class InterpreterBase: raise InterpreterException('Argument to "not" is not a boolean.') return not v - def evaluate_if(self, node): assert(isinstance(node, mparser.IfClauseNode)) for i in node.ifs: @@ -216,6 +215,155 @@ class InterpreterBase: if not isinstance(node.elseblock, mparser.EmptyNode): self.evaluate_codeblock(node.elseblock) + def evaluate_comparison(self, node): + v1 = self.evaluate_statement(node.left) + v2 = self.evaluate_statement(node.right) + if self.is_elementary_type(v1): + val1 = v1 + else: + val1 = v1.value + if self.is_elementary_type(v2): + val2 = v2 + else: + val2 = v2.value + if node.ctype == '==': + return val1 == val2 + elif node.ctype == '!=': + return val1 != val2 + elif node.ctype == '<': + return val1 < val2 + elif node.ctype == '<=': + return val1 <= val2 + elif node.ctype == '>': + return val1 > val2 + elif node.ctype == '>=': + return val1 >= val2 + else: + raise InvalidCode('You broke my compare eval.') + + def evaluate_andstatement(self, cur): + l = self.evaluate_statement(cur.left) + if isinstance(l, mparser.BooleanNode): + l = l.value + if not isinstance(l, bool): + raise InterpreterException('First argument to "and" is not a boolean.') + if not l: + return False + r = self.evaluate_statement(cur.right) + if isinstance(r, mparser.BooleanNode): + r = r.value + if not isinstance(r, bool): + raise InterpreterException('Second argument to "and" is not a boolean.') + return r + + def evaluate_orstatement(self, cur): + l = self.evaluate_statement(cur.left) + if isinstance(l, mparser.BooleanNode): + l = l.get_value() + if not isinstance(l, bool): + raise InterpreterException('First argument to "or" is not a boolean.') + if l: + return True + r = self.evaluate_statement(cur.right) + if isinstance(r, mparser.BooleanNode): + r = r.get_value() + if not isinstance(r, bool): + raise InterpreterException('Second argument to "or" is not a boolean.') + return r + + def evaluate_uminusstatement(self, cur): + v = self.evaluate_statement(cur.value) + if isinstance(v, mparser.NumberNode): + v = v.value + if not isinstance(v, int): + raise InterpreterException('Argument to negation is not an integer.') + return -v + + def evaluate_arithmeticstatement(self, cur): + l = self.to_native(self.evaluate_statement(cur.left)) + r = self.to_native(self.evaluate_statement(cur.right)) + + if cur.operation == 'add': + try: + return l + r + except Exception as e: + raise InvalidCode('Invalid use of addition: ' + str(e)) + elif cur.operation == 'sub': + if not isinstance(l, int) or not isinstance(r, int): + raise InvalidCode('Subtraction works only with integers.') + return l - r + elif cur.operation == 'mul': + if not isinstance(l, int) or not isinstance(r, int): + raise InvalidCode('Multiplication works only with integers.') + return l * r + elif cur.operation == 'div': + if not isinstance(l, int) or not isinstance(r, int): + raise InvalidCode('Division works only with integers.') + return l // r + elif cur.operation == 'mod': + if not isinstance(l, int) or not isinstance(r, int): + raise InvalidCode('Modulo works only with integers.') + return l % r + else: + raise InvalidCode('You broke me.') + + def evaluate_ternary(self, node): + assert(isinstance(node, mparser.TernaryNode)) + result = self.evaluate_statement(node.condition) + if not isinstance(result, bool): + raise InterpreterException('Ternary condition is not boolean.') + if result: + return self.evaluate_statement(node.trueblock) + else: + return self.evaluate_statement(node.falseblock) + + def evaluate_foreach(self, node): + assert(isinstance(node, mparser.ForeachClauseNode)) + varname = node.varname.value + items = self.evaluate_statement(node.items) + if not isinstance(items, list): + raise InvalidArguments('Items of foreach loop is not an array') + for item in items: + self.set_variable(varname, item) + self.evaluate_codeblock(node.block) + + def evaluate_plusassign(self, node): + assert(isinstance(node, mparser.PlusAssignmentNode)) + varname = node.var_name + addition = self.evaluate_statement(node.value) + # Remember that all variables are immutable. We must always create a + # full new variable and then assign it. + old_variable = self.get_variable(varname) + 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 not isinstance(old_variable, list): + raise InvalidArguments('The += operator currently only works with arrays, strings or ints ') + # Add other data types here. + else: + if isinstance(addition, list): + new_value = old_variable + addition + else: + new_value = old_variable + [addition] + self.set_variable(varname, new_value) + + def evaluate_indexing(self, node): + assert(isinstance(node, mparser.IndexNode)) + iobject = self.evaluate_statement(node.iobject) + if not isinstance(iobject, list): + raise InterpreterException('Tried to index a non-array object.') + index = self.evaluate_statement(node.index) + if not isinstance(index, int): + raise InterpreterException('Index value is not an integer.') + if index < -len(iobject) or index >= len(iobject): + raise InterpreterException('Index %d out of bounds of array of size %d.' % (index, len(iobject))) + return iobject[index] + def function_call(self, node): func_name = node.func_name (posargs, kwargs) = self.reduce_arguments(node.args) @@ -224,9 +372,142 @@ class InterpreterBase: else: self.unknown_function_called(func_name) + def method_call(self, node): + invokable = node.source_object + if isinstance(invokable, mparser.IdNode): + object_name = invokable.value + obj = self.get_variable(object_name) + else: + obj = self.evaluate_statement(invokable) + method_name = node.name + args = node.args + if isinstance(obj, mparser.StringNode): + obj = obj.get_value() + if isinstance(obj, str): + return self.string_method_call(obj, method_name, args) + if isinstance(obj, bool): + return self.bool_method_call(obj, method_name, args) + if isinstance(obj, int): + return self.int_method_call(obj, method_name, args) + if isinstance(obj, list): + return self.array_method_call(obj, method_name, self.reduce_arguments(args)[0]) + if not isinstance(obj, InterpreterObject): + raise InvalidArguments('Variable "%s" is not callable.' % object_name) + (args, kwargs) = self.reduce_arguments(args) + if method_name == 'extract_objects': + self.validate_extraction(obj.held_object) + return obj.method_call(method_name, self.flatten(args), kwargs) + + def bool_method_call(self, obj, method_name, args): + obj = self.to_native(obj) + (posargs, _) = self.reduce_arguments(args) + if method_name == 'to_string': + if len(posargs) == 0: + if obj == True: + return 'true' + else: + return 'false' + elif len(posargs) == 2 and isinstance(posargs[0], str) and isinstance(posargs[1], str): + if obj == True: + return posargs[0] + else: + return posargs[1] + else: + raise InterpreterException('bool.to_string() must have either no arguments or exactly two string arguments that signify what values to return for true and false.') + elif method_name == 'to_int': + if obj == True: + return 1 + else: + return 0 + else: + raise InterpreterException('Unknown method "%s" for a boolean.' % method_name) + + def int_method_call(self, obj, method_name, args): + obj = self.to_native(obj) + (posargs, _) = self.reduce_arguments(args) + if method_name == 'is_even': + if len(posargs) == 0: + return obj % 2 == 0 + else: + raise InterpreterException('int.is_even() must have no arguments.') + elif method_name == 'is_odd': + if len(posargs) == 0: + return obj % 2 != 0 + else: + raise InterpreterException('int.is_odd() must have no arguments.') + else: + raise InterpreterException('Unknown method "%s" for an integer.' % method_name) + + def string_method_call(self, obj, method_name, args): + obj = self.to_native(obj) + (posargs, _) = self.reduce_arguments(args) + if method_name == 'strip': + return obj.strip() + elif method_name == 'format': + return self.format_string(obj, args) + 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': + if len(posargs) > 1: + raise InterpreterException('Split() must have at most one argument.') + elif len(posargs) == 1: + s = posargs[0] + if not isinstance(s, str): + raise InterpreterException('Split() argument must be a string') + return obj.split(s) + else: + return obj.split() + elif method_name == 'startswith' or method_name == 'contains' or method_name == 'endswith': + s = posargs[0] + if not isinstance(s, str): + raise InterpreterException('Argument must be a string.') + if method_name == 'startswith': + return obj.startswith(s) + elif method_name == 'contains': + return obj.find(s) >= 0 + return obj.endswith(s) + elif method_name == 'to_int': + try: + return int(obj) + except Exception: + raise InterpreterException('String {!r} cannot be converted to int'.format(obj)) + elif method_name == 'join': + if len(posargs) != 1: + raise InterpreterException('Join() takes exactly one argument.') + strlist = posargs[0] + check_stringlist(strlist) + 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.') + return mesonlib.version_compare(obj, cmpr) + raise InterpreterException('Unknown method "%s" for a string.' % method_name) + def unknown_function_called(self, func_name): raise InvalidCode('Unknown function "%s".' % func_name) + def array_method_call(self, obj, method_name, args): + if method_name == 'contains': + return self.check_contains(obj, args) + elif method_name == 'length': + return len(obj) + elif method_name == 'get': + index = args[0] + if not isinstance(index, int): + raise InvalidArguments('Array index must be a number.') + if index < -len(obj) or index >= len(obj): + raise InvalidArguments('Array index %s is out of bounds for array of size %d.' % (index, len(obj))) + return obj[index] + raise InterpreterException('Arrays do not have a method called "%s".' % method_name) + + def reduce_arguments(self, args): assert(isinstance(args, mparser.ArgumentNode)) if args.incorrect_order(): @@ -349,3 +630,7 @@ class InterpreterBase: raise InvalidCode('Is_variable takes two arguments.') varname = args[0] return varname in self.variables + + def is_elementary_type(self, v): + return isinstance(v, (int, float, str, bool, list)) + |