diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2016-12-06 20:22:04 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-12-06 20:22:04 +0200 |
commit | 228a9035af26515481a686b8940968ea924f85fd (patch) | |
tree | f95e7d228a97fb344f74ed4b0f650403f08bfe41 /mesonbuild/interpreter.py | |
parent | f3bd0d149174b2bbb4983b48e603e060a3e18c22 (diff) | |
parent | 14ca7d602c92b6f97fe32230cf694d4a51d8a524 (diff) | |
download | meson-228a9035af26515481a686b8940968ea924f85fd.zip meson-228a9035af26515481a686b8940968ea924f85fd.tar.gz meson-228a9035af26515481a686b8940968ea924f85fd.tar.bz2 |
Merge pull request #1103 from mesonbuild/rewriter
Beginnings of a rewriter
Diffstat (limited to 'mesonbuild/interpreter.py')
-rw-r--r-- | mesonbuild/interpreter.py | 603 |
1 files changed, 14 insertions, 589 deletions
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index e9273e4..2167b81 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -22,58 +22,17 @@ from . import optinterpreter from . import compilers from .wrap import wrap from . import mesonlib +from mesonbuild.interpreterbase import InterpreterBase +from mesonbuild.interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs +from mesonbuild.interpreterbase import InterpreterException, InvalidArguments, InvalidCode +from mesonbuild.interpreterbase import InterpreterObject, MutableInterpreterObject import os, sys, subprocess, shutil, uuid, re -from functools import wraps import importlib -import copy run_depr_printed = False -class InterpreterException(mesonlib.MesonException): - pass - -class InvalidCode(InterpreterException): - pass - -class InvalidArguments(InterpreterException): - pass - -# Decorators for method calls. - -def check_stringlist(a, msg='Arguments must be strings.'): - if not isinstance(a, list): - mlog.debug('Not a list:', str(a)) - raise InvalidArguments('Argument not a list.') - if not all(isinstance(s, str) for s in a): - mlog.debug('Element not a string:', str(a)) - raise InvalidArguments(msg) - -def noPosargs(f): - @wraps(f) - def wrapped(self, node, args, kwargs): - if len(args) != 0: - raise InvalidArguments('Function does not take positional arguments.') - return f(self, node, args, kwargs) - return wrapped - -def noKwargs(f): - @wraps(f) - def wrapped(self, node, args, kwargs): - if len(kwargs) != 0: - raise InvalidArguments('Function does not take keyword arguments.') - return f(self, node, args, kwargs) - return wrapped - -def stringArgs(f): - @wraps(f) - def wrapped(self, node, args, kwargs): - assert(isinstance(args, list)) - check_stringlist(args) - return f(self, node, args, kwargs) - return wrapped - def stringifyUserArguments(args): if isinstance(args, list): return '[%s]' % ', '.join([stringifyUserArguments(x) for x in args]) @@ -83,18 +42,6 @@ def stringifyUserArguments(args): return "'%s'" % args raise InvalidArguments('Function accepts only strings, integers, lists and lists thereof.') -class InterpreterObject(): - def __init__(self): - self.methods = {} - - def method_call(self, method_name, args, kwargs): - if method_name in self.methods: - return self.methods[method_name](args, kwargs) - raise InvalidCode('Unknown method "%s" in object.' % method_name) - -class MutableInterpreterObject(InterpreterObject): - def __init__(self): - super().__init__() class TryRunResultHolder(InterpreterObject): def __init__(self, res): @@ -1139,16 +1086,15 @@ class MesonMain(InterpreterObject): return args[1] raise InterpreterException('Unknown cross property: %s.' % propname) -class Interpreter(): +class Interpreter(InterpreterBase): def __init__(self, build, backend, subproject='', subdir='', subproject_dir='subprojects'): + super().__init__(build.environment.get_source_dir(), subdir) self.build = build self.environment = build.environment self.coredata = self.environment.get_coredata() self.backend = backend self.subproject = subproject - self.subdir = subdir - self.source_root = build.environment.get_source_dir() self.subproject_dir = subproject_dir option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt') if os.path.exists(option_file): @@ -1156,22 +1102,9 @@ class Interpreter(): self.build.environment.cmd_line_options.projectoptions) oi.process(option_file) self.build.environment.merge_options(oi.options) - mesonfile = os.path.join(self.source_root, self.subdir, environment.build_filename) - if not os.path.isfile(mesonfile): - raise InvalidArguments('Missing Meson file in %s' % mesonfile) - with open(mesonfile, encoding='utf8') as mf: - code = mf.read() - if len(code.strip()) == 0: - raise InvalidCode('Builder file is empty.') - assert(isinstance(code, str)) - try: - self.ast = mparser.Parser(code).parse() - except mesonlib.MesonException as me: - me.file = environment.build_filename - raise me + self.load_root_meson_file() self.sanity_check_ast() - self.variables = {} - self.builtin = {'meson': MesonMain(build, self)} + self.builtin.update({'meson': MesonMain(build, self)}) self.generators = [] self.visited_subdirs = {} self.args_frozen = False @@ -1196,7 +1129,7 @@ class Interpreter(): self.build_def_files = [os.path.join(self.subdir, environment.build_filename)] def build_func_dict(self): - self.funcs = {'project' : self.func_project, + self.funcs.update({'project' : self.func_project, 'message' : self.func_message, 'error' : self.func_error, 'executable': self.func_executable, @@ -1241,14 +1174,7 @@ class Interpreter(): 'assert': self.func_assert, 'environment' : self.func_environment, 'join_paths' : self.func_join_paths, - } - - def parse_project(self): - """ - Parses project() and initializes languages, compilers etc. Do this - early because we need this before we parse the rest of the AST. - """ - self.evaluate_codeblock(self.ast, end=1) + }) def module_method_callback(self, invalues): unwrap_single = False @@ -1295,16 +1221,6 @@ class Interpreter(): def get_variables(self): return self.variables - def sanity_check_ast(self): - if not isinstance(self.ast, mparser.CodeBlockNode): - raise InvalidCode('AST is of invalid type. Possibly a bug in the parser.') - if len(self.ast.lines) == 0: - raise InvalidCode('No statements in code.') - first = self.ast.lines[0] - if not isinstance(first, mparser.FunctionNode) or first.func_name != 'project': - raise InvalidCode('First statement must be a call to project') - - def check_cross_stdlibs(self): if self.build.environment.is_cross_build(): cross_info = self.build.environment.cross_info @@ -1321,71 +1237,6 @@ class Interpreter(): except KeyError as e: pass - def run(self): - # Evaluate everything after the first line, which is project() because - # we already parsed that in self.parse_project() - self.evaluate_codeblock(self.ast, start=1) - mlog.log('Build targets in project:', mlog.bold(str(len(self.build.targets)))) - - def evaluate_codeblock(self, node, start=0, end=None): - if node is None: - return - if not isinstance(node, mparser.CodeBlockNode): - e = InvalidCode('Tried to execute a non-codeblock. Possibly a bug in the parser.') - e.lineno = node.lineno - e.colno = node.colno - raise e - statements = node.lines[start:end] - i = 0 - while i < len(statements): - cur = statements[i] - try: - self.evaluate_statement(cur) - except Exception as e: - if not(hasattr(e, 'lineno')): - e.lineno = cur.lineno - e.colno = cur.colno - e.file = os.path.join(self.subdir, 'meson.build') - raise e - i += 1 # In THE FUTURE jump over blocks and stuff. - - def get_variable(self, varname): - if varname in self.builtin: - return self.builtin[varname] - if varname in self.variables: - return self.variables[varname] - raise InvalidCode('Unknown variable "%s".' % varname) - - def func_set_variable(self, node, args, kwargs): - if len(args) != 2: - raise InvalidCode('Set_variable takes two arguments.') - varname = args[0] - value = self.to_native(args[1]) - self.set_variable(varname, value) - - @noKwargs - def func_get_variable(self, node, args, kwargs): - if len(args)<1 or len(args)>2: - raise InvalidCode('Get_variable takes one or two arguments.') - varname = args[0] - if not isinstance(varname, str): - raise InterpreterException('First argument must be a string.') - try: - return self.variables[varname] - except KeyError: - pass - if len(args) == 2: - return args[1] - raise InterpreterException('Tried to get unknown variable "%s".' % varname) - - @stringArgs - @noKwargs - def func_is_variable(self, node, args, kwargs): - if len(args) != 1: - raise InvalidCode('Is_variable takes two arguments.') - varname = args[0] - return varname in self.variables - @stringArgs @noKwargs def func_import(self, node, args, kwargs): @@ -1446,63 +1297,6 @@ class Interpreter(): if not value: raise InterpreterException('Assert failed: ' + message) - def set_variable(self, varname, variable): - if variable is None: - raise InvalidCode('Can not assign None to variable.') - if not isinstance(varname, str): - raise InvalidCode('First argument to set_variable must be a string.') - if not self.is_assignable(variable): - raise InvalidCode('Assigned value not of assignable type.') - if re.match('[_a-zA-Z][_0-9a-zA-Z]*$', varname) is None: - raise InvalidCode('Invalid variable name: ' + varname) - if varname in self.builtin: - raise InvalidCode('Tried to overwrite internal variable "%s"' % varname) - self.variables[varname] = variable - - def evaluate_statement(self, cur): - if isinstance(cur, mparser.FunctionNode): - return self.function_call(cur) - elif isinstance(cur, mparser.AssignmentNode): - return self.assignment(cur) - elif isinstance(cur, mparser.MethodNode): - return self.method_call(cur) - elif isinstance(cur, mparser.StringNode): - return cur.value - elif isinstance(cur, mparser.BooleanNode): - return cur.value - elif isinstance(cur, mparser.IfClauseNode): - return self.evaluate_if(cur) - elif isinstance(cur, mparser.IdNode): - return self.get_variable(cur.value) - elif isinstance(cur, mparser.ComparisonNode): - return self.evaluate_comparison(cur) - elif isinstance(cur, mparser.ArrayNode): - return self.evaluate_arraystatement(cur) - elif isinstance(cur, mparser.NumberNode): - return cur.value - elif isinstance(cur, mparser.AndNode): - return self.evaluate_andstatement(cur) - elif isinstance(cur, mparser.OrNode): - return self.evaluate_orstatement(cur) - elif isinstance(cur, mparser.NotNode): - return self.evaluate_notstatement(cur) - elif isinstance(cur, mparser.UMinusNode): - return self.evaluate_uminusstatement(cur) - elif isinstance(cur, mparser.ArithmeticNode): - return self.evaluate_arithmeticstatement(cur) - elif isinstance(cur, mparser.ForeachClauseNode): - return self.evaluate_foreach(cur) - elif isinstance(cur, mparser.PlusAssignmentNode): - return self.evaluate_plusassign(cur) - elif isinstance(cur, mparser.IndexNode): - return self.evaluate_indexing(cur) - elif isinstance(cur, mparser.TernaryNode): - return self.evaluate_ternary(cur) - elif self.is_elementary_type(cur): - return cur - else: - raise InvalidCode("Unknown statement.") - def validate_arguments(self, args, argcount, arg_types): if argcount is not None: if argcount != len(args): @@ -2193,7 +1987,7 @@ requirements use the version keyword argument instead.''') code = f.read() assert(isinstance(code, str)) try: - codeblock = mparser.Parser(code).parse() + codeblock = mparser.Parser(code, self.subdir).parse() except mesonlib.MesonException as me: me.file = buildfilename raise me @@ -2376,21 +2170,9 @@ requirements use the version keyword argument instead.''') def func_join_paths(self, node, args, kwargs): return os.path.join(*args).replace('\\', '/') - def flatten(self, args): - if isinstance(args, mparser.StringNode): - return args.value - if isinstance(args, (int, str, InterpreterObject)): - return args - result = [] - for a in args: - if isinstance(a, list): - rest = self.flatten(a) - result = result + rest - elif isinstance(a, mparser.StringNode): - result.append(a.value) - else: - result.append(a) - return result + def run(self): + super().run() + mlog.log('Build targets in project:', mlog.bold(str(len(self.build.targets)))) def source_strings_to_files(self, sources): results = [] @@ -2487,145 +2269,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 function_call(self, node): - func_name = node.func_name - (posargs, kwargs) = self.reduce_arguments(node.args) - if func_name in self.funcs: - return self.funcs[func_name](node, self.flatten(posargs), kwargs) - else: - raise InvalidCode('Unknown function "%s".' % func_name) - - def is_assignable(self, value): - return isinstance(value, (InterpreterObject, dependencies.Dependency, - str, int, list, mesonlib.File)) - - def assignment(self, node): - assert(isinstance(node, mparser.AssignmentNode)) - var_name = node.var_name - if not isinstance(var_name, str): - raise InvalidArguments('Tried to assign value to a non-variable.') - value = self.evaluate_statement(node.value) - value = self.to_native(value) - if not self.is_assignable(value): - raise InvalidCode('Tried to assign an invalid value to variable.') - # For mutable objects we need to make a copy on assignment - if isinstance(value, MutableInterpreterObject): - value = copy.deepcopy(value) - self.set_variable(var_name, value) - return value - - def reduce_arguments(self, args): - assert(isinstance(args, mparser.ArgumentNode)) - if args.incorrect_order(): - raise InvalidArguments('All keyword arguments must be after positional arguments.') - reduced_pos = [self.evaluate_statement(arg) for arg in args.arguments] - reduced_kw = {} - for key in args.kwargs.keys(): - if not isinstance(key, str): - raise InvalidArguments('Keyword argument name is not a string.') - a = args.kwargs[key] - reduced_kw[key] = self.evaluate_statement(a) - if not isinstance(reduced_pos, list): - reduced_pos = [reduced_pos] - return (reduced_pos, reduced_kw) - - 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 to_native(self, arg): - if isinstance(arg, (mparser.StringNode, mparser.NumberNode, - mparser.BooleanNode)): - return arg.value - return arg def format_string(self, templ, args): templ = self.to_native(templ) @@ -2638,32 +2281,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): @@ -2675,20 +2292,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.') @@ -2705,183 +2308,5 @@ requirements use the version keyword argument instead.''') pass return False - def evaluate_if(self, node): - assert(isinstance(node, mparser.IfClauseNode)) - for i in node.ifs: - result = self.evaluate_statement(i.condition) - if not(isinstance(result, bool)): - raise InvalidCode('If clause {!r} does not evaluate to true or false.'.format(result)) - if result: - self.evaluate_codeblock(i.block) - return - if not isinstance(node.elseblock, mparser.EmptyNode): - self.evaluate_codeblock(node.elseblock) - - 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_notstatement(self, cur): - v = self.evaluate_statement(cur.value) - if isinstance(v, mparser.BooleanNode): - v = v.value - if not isinstance(v, bool): - raise InterpreterException('Argument to "not" is not a boolean.') - return not v - - 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_arraystatement(self, cur): - (arguments, kwargs) = self.reduce_arguments(cur.args) - if len(kwargs) > 0: - raise InvalidCode('Keyword arguments are invalid in array construction.') - return arguments - def is_subproject(self): return self.subproject != '' |