diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index 158dfe96..0118ca43 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -34,7 +34,8 @@ use super::{ }, types::{ndarray::NDArrayType, ListType}, values::{ - ndarray::NDArrayValue, ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, RangeValue, + ndarray::{NDArrayValue, RustNDIndex}, + ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, RangeValue, TypedArrayLikeAccessor, UntypedArrayLikeAccessor, }, CodeGenContext, CodeGenTask, CodeGenerator, @@ -3486,19 +3487,21 @@ pub fn gen_expr<'ctx, G: CodeGenerator>( } } TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => { - let (ty, ndims) = - unpack_ndarray_var_tys(&mut ctx.unifier, value.custom.unwrap()); - - let v = if let Some(v) = generator.gen_expr(ctx, value)? { - v.to_basic_value_enum(ctx, generator, value.custom.unwrap())? - .into_pointer_value() - } else { + let Some(ndarray) = generator.gen_expr(ctx, value)? else { return Ok(None); }; - let v = NDArrayType::from_unifier_type(generator, ctx, value.custom.unwrap()) - .map_value(v, None); - return gen_ndarray_subscript_expr(generator, ctx, ty, ndims, v, slice); + let ndarray_ty = value.custom.unwrap(); + let ndarray = ndarray.to_basic_value_enum(ctx, generator, ndarray_ty)?; + let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty) + .map_value(ndarray.into_pointer_value(), None); + + let indices = RustNDIndex::from_subscript_expr(generator, ctx, slice)?; + let result = ndarray + .index(generator, ctx, &indices) + .split_unsized(generator, ctx) + .to_basic_value_enum(); + return Ok(Some(ValueEnum::Dynamic(result))); } TypeEnum::TTuple { .. } => { let index: u32 = diff --git a/nac3core/src/codegen/values/ndarray/mod.rs b/nac3core/src/codegen/values/ndarray/mod.rs index fdf11dd2..b6d86de5 100644 --- a/nac3core/src/codegen/values/ndarray/mod.rs +++ b/nac3core/src/codegen/values/ndarray/mod.rs @@ -403,6 +403,33 @@ impl<'ctx> NDArrayValue<'ctx> { assert_eq!(self.dtype, src.dtype, "self and src dtype should match"); irrt::ndarray::call_nac3_ndarray_copy_data(generator, ctx, src, *self); } + + /// Returns true if this ndarray is unsized - `ndims == 0` and only contains a scalar. + #[must_use] + pub fn is_unsized(&self) -> Option<bool> { + self.ndims.map(|ndims| ndims == 0) + } + + /// If this ndarray is unsized, return its sole value as an [`AnyObject`]. + /// Otherwise, do nothing and return the ndarray itself. + // TODO: Rename to get_unsized_element + pub fn split_unsized<G: CodeGenerator + ?Sized>( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ) -> ScalarOrNDArray<'ctx> { + let Some(is_unsized) = self.is_unsized() else { todo!() }; + + if is_unsized { + // NOTE: `np.size(self) == 0` here is never possible. + let zero = generator.get_size_type(ctx.ctx).const_zero(); + let value = unsafe { self.data().get_unchecked(ctx, generator, &zero, None) }; + + ScalarOrNDArray::Scalar(value) + } else { + ScalarOrNDArray::NDArray(*self) + } + } } impl<'ctx> ProxyValue<'ctx> for NDArrayValue<'ctx> { @@ -884,3 +911,21 @@ pub fn make_contiguous_strides(itemsize: u64, ndims: u64, shape: &[u64]) -> Vec< } strides } + +/// A convenience enum for implementing functions that acts on scalars or ndarrays or both. +#[derive(Clone, Copy)] +pub enum ScalarOrNDArray<'ctx> { + Scalar(BasicValueEnum<'ctx>), + NDArray(NDArrayValue<'ctx>), +} + +impl<'ctx> ScalarOrNDArray<'ctx> { + /// Get the underlying [`BasicValueEnum<'ctx>`] of this [`ScalarOrNDArray`]. + #[must_use] + pub fn to_basic_value_enum(self) -> BasicValueEnum<'ctx> { + match self { + ScalarOrNDArray::Scalar(scalar) => scalar, + ScalarOrNDArray::NDArray(ndarray) => ndarray.as_base_value().into(), + } + } +}