def visit_BoolOp(self, bool_op: ast.BoolOp) -> Optional[IType]: """ Verifies if the types of the operands are valid to the boolean operations If the operations are valid, changes de Python operator by the Boa operator in the syntax tree :param bool_op: the python ast boolean operation node :return: the type of the result of the operation if the operation is valid. Otherwise, returns None :rtype: IType or None """ lineno: int = bool_op.lineno col_offset: int = bool_op.col_offset try: return_type: IType = None bool_operation: IOperation = None operator: Operator = self.get_operator(bool_op.op) if not isinstance(operator, Operator): # the operator is invalid or it was not implemented yet self._log_error( CompilerError.UnresolvedReference(lineno, col_offset, type(operator).__name__) ) l_operand = self.visit(bool_op.values[0]) for index, operand in enumerate(bool_op.values[1:]): r_operand = self.visit(operand) operation: IOperation = self.get_bin_op(operator, r_operand, l_operand) if operation is None: self._log_error( CompilerError.NotSupportedOperation(lineno, col_offset, operator) ) elif bool_operation is None: return_type = operation.result bool_operation = operation lineno = operand.lineno col_offset = operand.col_offset l_operand = r_operand bool_op.op = bool_operation return return_type except CompilerError.MismatchedTypes as raised_error: raised_error.line = lineno raised_error.col = col_offset # raises the exception with the line/col info self._log_error(raised_error)
def visit_Assign(self, assign: ast.Assign): """ Verifies if it is a multiple assignments statement :param assign: the python ast variable assignment node """ # multiple assignments if isinstance(assign.targets[0], ast.Tuple): self._log_error( CompilerError.NotSupportedOperation(assign.lineno, assign.col_offset, 'Multiple variable assignments') ) else: for target in assign.targets: self.validate_type_variable_assign(target, assign.value) # continue to walk through the tree self.generic_visit(assign)
def visit_Call(self, call: ast.Call): """ Verifies if the number of arguments is correct :param call: the python ast function call node :return: the result type of the called function """ if isinstance(call.func, ast.Name): callable_id: str = call.func.id callable_target = self.get_symbol(callable_id) else: callable_id, callable_target = self.get_callable_and_update_args(call) # type: str, ISymbol callable_target = self.validate_builtin_callable(callable_id, callable_target) if not isinstance(callable_target, Callable): # the symbol doesn't exists or is not a function self._log_error( CompilerError.UnresolvedReference(call.func.lineno, call.func.col_offset, callable_id) ) else: if callable_target is Builtin.NewEvent: return callable_target.return_type # TODO: change when kwargs is implemented if len(call.keywords) > 0: raise NotImplementedError if self.validate_callable_arguments(call, callable_target): args = [self.get_type(param) for param in call.args] if isinstance(callable_target, IBuiltinMethod): # if the arguments are not generic, build the specified method callable_target: IBuiltinMethod = callable_target.build(args) if not callable_target.is_supported: self._log_error( CompilerError.NotSupportedOperation(call.lineno, call.col_offset, callable_id) ) return callable_target.return_type self.validate_passed_arguments(call, args, callable_id, callable_target) self.update_callable_after_validation(call, callable_id, callable_target) return self.get_type(callable_target)
def visit_ClassDef(self, node: ast.ClassDef): # TODO: refactor when classes defined by the user are implemented self._log_error( CompilerError.NotSupportedOperation(node.lineno, node.col_offset, symbol_id='class'))