From 0a74262ceedd1a344d21c9b35c9753395f41b7c4 Mon Sep 17 00:00:00 2001 From: Jooris Hadeler Date: Thu, 23 Apr 2026 00:19:11 +0200 Subject: [PATCH] feat: add support for arrays --- examples/helper.c | 5 + examples/raytracer.src | 204 +++++++++++++++++++++++++++++++++++++++ src/backend/cranelift.rs | 44 +++++++-- src/frontend/ast.rs | 8 ++ src/frontend/parser.rs | 63 +++++++++++- src/frontend/sema.rs | 81 ++++++++++++++++ src/middle/builder.rs | 76 ++++++++++++++- src/middle/fold.rs | 6 ++ src/middle/mir.rs | 5 + tests/array_basic.test | 16 +++ tests/array_swap.test | 15 +++ 11 files changed, 512 insertions(+), 11 deletions(-) create mode 100644 examples/helper.c create mode 100644 examples/raytracer.src create mode 100644 tests/array_basic.test create mode 100644 tests/array_swap.test diff --git a/examples/helper.c b/examples/helper.c new file mode 100644 index 0000000..d8cf29f --- /dev/null +++ b/examples/helper.c @@ -0,0 +1,5 @@ +#include + +void print_number(int number) { + printf("%d", number); +} \ No newline at end of file diff --git a/examples/raytracer.src b/examples/raytracer.src new file mode 100644 index 0000000..42e7bf2 --- /dev/null +++ b/examples/raytracer.src @@ -0,0 +1,204 @@ +foreign fn putchar(c: i32) -> i32; +foreign fn print_number(n: i32); +foreign fn sqrtf(num: f32) -> f32; + +struct Vec3 { + x: f32, + y: f32, + z: f32 +} + +struct Ray { + origin: Vec3, + dir: Vec3 +} + +struct Sphere { + center: Vec3, + radius: f32, + color: Vec3 +} + +struct Hit { + hit: bool, + t: f32, + normal: Vec3, + color: Vec3 +} + +fn vec3_add(a: Vec3, b: Vec3) -> Vec3 { + return Vec3 { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; +} + +fn vec3_sub(a: Vec3, b: Vec3) -> Vec3 { + return Vec3 { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }; +} + +fn vec3_mul(a: Vec3, t: f32) -> Vec3 { + return Vec3 { x: a.x * t, y: a.y * t, z: a.z * t }; +} + +fn vec3_dot(a: Vec3, b: Vec3) -> f32 { + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +fn vec3_normalize(v: Vec3) -> Vec3 { + let len = sqrtf(vec3_dot(v, v)); + if len == 0.0 { + return Vec3 { x: 0.0, y: 0.0, z: 0.0 }; + } + return vec3_mul(v, 1.0 / len); +} + +fn intersect_sphere(ray: *Ray, sphere: *Sphere, t_min: f32, t_max: f32) -> Hit { + let oc = vec3_sub((*ray).origin, (*sphere).center); + let a = vec3_dot((*ray).dir, (*ray).dir); + let half_b = vec3_dot(oc, (*ray).dir); + let c = vec3_dot(oc, oc) - (*sphere).radius * (*sphere).radius; + let discriminant = half_b * half_b - a * c; + + let miss = Hit { + hit: false, + t: 0.0, + normal: Vec3 { x: 0.0, y: 0.0, z: 0.0 }, + color: Vec3 { x: 0.0, y: 0.0, z: 0.0 } + }; + + if discriminant < 0.0 { + return miss; + } + + let sqrtd = sqrtf(discriminant); + let root = (-half_b - sqrtd) / a; + + if root < t_min { + root = (-half_b + sqrtd) / a; + if root < t_min { + return miss; + } + } + + if root > t_max { + return miss; + } + + let hit_point = vec3_add((*ray).origin, vec3_mul((*ray).dir, root)); + let normal = vec3_mul(vec3_sub(hit_point, (*sphere).center), 1.0 / (*sphere).radius); + + return Hit { + hit: true, + t: root, + normal: normal, + color: (*sphere).color + }; +} + +fn ray_color(ray: *Ray, spheres: *[Sphere; 2]) -> Vec3 { + let closest_so_far = 1000.0; + let hit_anything = false; + let hit_color = Vec3 { x: 0.0, y: 0.0, z: 0.0 }; + let hit_normal = Vec3 { x: 0.0, y: 0.0, z: 0.0 }; + + let i = 0; + while i < 2 { + // Pointer arithmetic to array structs via the &[] index syntax + let hit = intersect_sphere(ray, &(*spheres)[i], 0.001, closest_so_far); + + if hit.hit { + hit_anything = true; + closest_so_far = hit.t; + hit_color = hit.color; + hit_normal = hit.normal; + } + i = i + 1; + } + + if hit_anything { + // We remap the surface normal from [-1..1] to the [0..1] color space + let mapped_normal = vec3_mul(vec3_add(hit_normal, Vec3 { x: 1.0, y: 1.0, z: 1.0 }), 0.5); + + // Multiply the resulting surface map shading by the intrinsic color of the object + return Vec3 { + x: hit_color.x * mapped_normal.x, + y: hit_color.y * mapped_normal.y, + z: hit_color.z * mapped_normal.z + }; + } + + // Beautiful skybox blue to white linear gradient + let unit_dir = vec3_normalize((*ray).dir); + let t = 0.5 * (unit_dir.y + 1.0); + return vec3_add( + vec3_mul(Vec3 { x: 1.0, y: 1.0, z: 1.0 }, 1.0 - t), + vec3_mul(Vec3 { x: 0.5, y: 0.7, z: 1.0 }, t) + ); +} + +fn main() -> i32 { + let image_width = 1024; + let image_height = 1024; + + let spheres: [Sphere; 2] = [ + Sphere { + center: Vec3 { x: 0.0, y: 0.0, z: -1.0 }, + radius: 0.5, + color: Vec3 { x: 1.0, y: 0.2, z: 0.2 } // Red sphere + }, + Sphere { + center: Vec3 { x: 0.0, y: -100.5, z: -1.0 }, + radius: 100.0, + color: Vec3 { x: 0.2, y: 0.8, z: 0.2 } // Green ground + } + ]; + + // Print standard PPM Image File Header + putchar(80); putchar(51); putchar(10); // "P3\n" + + print_number(image_width); + putchar(32); // " " + print_number(image_height); + putchar(10); // "\n" + + print_number(255); + putchar(10); // "255\n" + + let j = image_height - 1; + while j >= 0 { + let i = 0; + while i < image_width { + let u = (i as f32) / (image_width as f32 - 1.0); + let v = (j as f32) / (image_height as f32 - 1.0); + + // Set up a classic look-at viewport layout + let lower_left_corner = Vec3 { x: -2.0, y: -2.0, z: -1.0 }; + let horizontal = Vec3 { x: 4.0, y: 0.0, z: 0.0 }; + let vertical = Vec3 { x: 0.0, y: 4.0, z: 0.0 }; + let origin = Vec3 { x: 0.0, y: 0.0, z: 0.0 }; + + let dir = vec3_sub( + vec3_add(lower_left_corner, vec3_add(vec3_mul(horizontal, u), vec3_mul(vertical, v))), + origin + ); + + let ray = Ray { origin: origin, dir: dir }; + let col = ray_color(&ray, &spheres); + + // Precedence parser respects parenthesis grouping over multiplication + let ir = (255.999 * col.x) as i32; + let ig = (255.999 * col.y) as i32; + let ib = (255.999 * col.z) as i32; + + print_number(ir); + putchar(32); + print_number(ig); + putchar(32); + print_number(ib); + putchar(10); + + i = i + 1; + } + j = j - 1; + } + + return 0; +} \ No newline at end of file diff --git a/src/backend/cranelift.rs b/src/backend/cranelift.rs index d0bd2cd..701e237 100644 --- a/src/backend/cranelift.rs +++ b/src/backend/cranelift.rs @@ -143,7 +143,7 @@ impl CraneliftBackend { let mut var_map = HashMap::new(); let mut stack_slot_map = HashMap::new(); for local in &func.locals { - if local.address_taken || matches!(local.ty, Ty::Struct(_)) { + if local.address_taken || matches!(local.ty, Ty::Struct(_) | Ty::Array(_, _)) { let bytes = Self::type_size(&local.ty, structs); let slot = builder.create_sized_stack_slot(ir::StackSlotData::new( ir::StackSlotKind::ExplicitSlot, @@ -184,7 +184,7 @@ impl CraneliftBackend { for (j, param_id) in func.params.iter().enumerate() { let val = trans.builder.block_params(cl_block)[j]; - if matches!(func.locals[param_id.0].ty, Ty::Struct(_)) { + if matches!(func.locals[param_id.0].ty, Ty::Struct(_) | Ty::Array(_, _)) { let slot = trans.stack_slot_map[param_id]; let dest_addr = trans.builder.ins().stack_addr(types::I64, slot, 0); let size = @@ -223,7 +223,7 @@ impl CraneliftBackend { Ty::F32 => types::F32, Ty::F64 => types::F64, Ty::Bool => types::I8, // Booleans are represented as 8-bit integers - Ty::Pointer(_) | Ty::Struct(_) => types::I64, // Structs are passed by reference implicitly + Ty::Pointer(_) | Ty::Struct(_) | Ty::Array(_, _) => types::I64, // Aggregates and pointers are passed by reference _ => unimplemented!("Unsupported type for Cranelift lowering: {:?}", ty), } } @@ -247,6 +247,10 @@ impl CraneliftBackend { } size } + Ty::Array(inner, len) => { + let elem_size = Self::type_size(inner, structs); + elem_size * (*len as u32) + } Ty::Unit => 0, Ty::Var(_) | Ty::Function(_, _) => unimplemented!(), } @@ -293,11 +297,11 @@ impl<'a> FunctionTranslator<'a> { match &stmt.kind { StatementKind::Assign(local_id, rvalue) => { if let Some(&slot) = self.stack_slot_map.get(local_id) { - if let Ty::Struct(name) = &self.locals[local_id.0].ty { + if matches!(self.locals[local_id.0].ty, Ty::Struct(_) | Ty::Array(_, _)) { let dest_addr = self.builder.ins().stack_addr(types::I64, slot, 0); if let Some(src_addr) = self.translate_rvalue(rvalue) { let size = CraneliftBackend::type_size( - &Ty::Struct(name.clone()), + &self.locals[local_id.0].ty, self.structs, ); self.emit_memcpy(dest_addr, src_addr, size); @@ -318,7 +322,7 @@ impl<'a> FunctionTranslator<'a> { StatementKind::Store { ptr, val } => { let ptr_val = self.translate_operand(ptr); let rval_ty = self.get_rvalue_type(val); - if matches!(rval_ty, Ty::Struct(_)) { + if matches!(rval_ty, Ty::Struct(_) | Ty::Array(_, _)) { if let Some(src_addr) = self.translate_rvalue(val) { let size = CraneliftBackend::type_size(&rval_ty, self.structs); self.emit_memcpy(ptr_val, src_addr, size); @@ -379,7 +383,7 @@ impl<'a> FunctionTranslator<'a> { fn translate_operand(&mut self, op: &Operand) -> ir::Value { match op { Operand::Copy(local_id) => { - if matches!(self.locals[local_id.0].ty, Ty::Struct(_)) { + if matches!(self.locals[local_id.0].ty, Ty::Struct(_) | Ty::Array(_, _)) { let slot = self.stack_slot_map[local_id]; self.builder.ins().stack_addr(types::I64, slot, 0) } else if let Some(&slot) = self.stack_slot_map.get(local_id) { @@ -656,7 +660,7 @@ impl<'a> FunctionTranslator<'a> { Ty::Pointer(inner) => *inner, _ => unreachable!(), }; - if matches!(inner_ty, Ty::Struct(_)) { + if matches!(inner_ty, Ty::Struct(_) | Ty::Array(_, _)) { Some(ptr_val) } else { let cl_ty = CraneliftBackend::lower_type(&inner_ty); @@ -676,6 +680,29 @@ impl<'a> FunctionTranslator<'a> { let offset = CraneliftBackend::field_offset(struct_name, field_name, self.structs); Some(self.builder.ins().iadd_imm(base, offset as i64)) } + Rvalue::GetElementPtr { + base_ptr, + index, + element_ty, + } => { + let base = self.translate_operand(base_ptr); + let idx = self.translate_operand(index); + + let idx_ty = self.get_operand_type(index); + let idx_64 = if idx_ty.bit_width() < 64 { + if idx_ty.is_signed() { + self.builder.ins().sextend(types::I64, idx) + } else { + self.builder.ins().uextend(types::I64, idx) + } + } else { + idx + }; + + let size = CraneliftBackend::type_size(element_ty, self.structs); + let offset = self.builder.ins().imul_imm(idx_64, size as i64); + Some(self.builder.ins().iadd(base, offset)) + } } } @@ -718,6 +745,7 @@ impl<'a> FunctionTranslator<'a> { .clone(); Ty::Pointer(Box::new(ty)) } + Rvalue::GetElementPtr { element_ty, .. } => Ty::Pointer(Box::new(element_ty.clone())), } } diff --git a/src/frontend/ast.rs b/src/frontend/ast.rs index 766cab7..f70c6a3 100644 --- a/src/frontend/ast.rs +++ b/src/frontend/ast.rs @@ -105,6 +105,7 @@ pub enum TypeKind { Bool, Pointer(Box), Struct(String), + Array(Box, usize), } #[derive(Debug, PartialEq)] @@ -195,6 +196,13 @@ pub enum ExprKind { field: String, field_span: Span, }, + Array { + elements: Vec>, + }, + Index { + array: Box>, + index: Box>, + }, } #[derive(Debug, PartialEq)] diff --git a/src/frontend/parser.rs b/src/frontend/parser.rs index 983d401..7c9bb38 100644 --- a/src/frontend/parser.rs +++ b/src/frontend/parser.rs @@ -377,6 +377,19 @@ impl<'src> Parser<'src> { span, }); } + TokenKind::LBracket => { + let lbracket = self.advance().unwrap(); + let inner = self.parse_type()?; + self.expect(TokenKind::Semicolon)?; + let len_token = self.expect(TokenKind::IntegerLit)?; + let len = len_token.text.parse().unwrap(); + let rbracket = self.expect(TokenKind::RBracket)?; + let span = lbracket.span.join(rbracket.span); + return Ok(Type { + kind: TypeKind::Array(Box::new(inner), len), + span, + }); + } _ => { return Err(ParseError::new( @@ -642,7 +655,9 @@ impl<'src> Parser<'src> { let mut lhs = self.parse_leading_expr(allow_struct)?; loop { - let peek_token = self.peek_no_eof()?; + let Some(peek_token) = self.peek() else { + break; + }; if peek_token.kind == TokenKind::Assign { let left_bp = 2; @@ -743,6 +758,28 @@ impl<'src> Parser<'src> { continue; } + if peek_token.kind == TokenKind::LBracket { + let left_bp = 30; // Array indexing has high precedence + if left_bp < min_bp { + break; + } + self.advance(); // consume `[` + + let index = self.parse_expr()?; + let rbracket_token = self.expect(TokenKind::RBracket)?; + let span = lhs.span.join(rbracket_token.span); + + lhs = Expr { + kind: ExprKind::Index { + array: Box::new(lhs), + index: Box::new(index), + }, + ty: (), + span, + }; + continue; + } + let Some((op, left_bp, right_bp)) = self.infix_operator(peek_token.kind) else { break; // Not an infix operator }; @@ -881,6 +918,30 @@ impl<'src> Parser<'src> { }) } + TokenKind::LBracket => { + let lbracket = self.advance().unwrap(); + let mut elements = Vec::new(); + + if !self.is_peek(TokenKind::RBracket) { + loop { + elements.push(self.parse_expr()?); + if !self.is_peek(TokenKind::Comma) { + break; + } + self.advance(); // consume `,` + } + } + + let rbracket = self.expect(TokenKind::RBracket)?; + let span = lbracket.span.join(rbracket.span); + + Ok(Expr { + kind: ExprKind::Array { elements }, + ty: (), + span, + }) + } + kind if let Some((op, r_bp)) = self.prefix_operator(kind) => { let op_token = self.advance().unwrap(); let rhs = self.parse_expr_bp(r_bp, allow_struct)?; diff --git a/src/frontend/sema.rs b/src/frontend/sema.rs index 42e548e..9d18621 100644 --- a/src/frontend/sema.rs +++ b/src/frontend/sema.rs @@ -43,6 +43,7 @@ pub enum Ty { Function(Vec, Box), Pointer(Box), Struct(String), + Array(Box, usize), } impl Ty { @@ -110,6 +111,7 @@ impl From<&TypeKind> for Ty { TypeKind::Bool => Ty::Bool, TypeKind::Pointer(inner) => Ty::Pointer(Box::new(Ty::from(&inner.kind))), TypeKind::Struct(name) => Ty::Struct(name.clone()), + TypeKind::Array(inner, len) => Ty::Array(Box::new(Ty::from(&inner.kind)), *len), } } } @@ -123,6 +125,7 @@ impl TypedExpr { op: UnaryOp::Deref, .. } => true, TypedExprKind::FieldAccess { expr, .. } => expr.is_lvalue(), + TypedExprKind::Index { .. } => true, _ => false, } } @@ -213,6 +216,7 @@ impl Sema { } Ty::Pointer(inner) => Ty::Pointer(Box::new(self.apply_subst(inner))), + Ty::Array(inner, len) => Ty::Array(Box::new(self.apply_subst(inner)), *len), t => t.clone(), } @@ -228,6 +232,7 @@ impl Sema { params.iter().any(|p| self.occurs_check(v, p)) || self.occurs_check(v, &ret) } Ty::Pointer(inner) => self.occurs_check(v, &inner), + Ty::Array(inner, _) => self.occurs_check(v, &inner), _ => false, } } @@ -264,6 +269,7 @@ impl Sema { self.unify(&r1, &r2) } (Ty::Pointer(p1), Ty::Pointer(p2)) => self.unify(&p1, &p2), + (Ty::Array(a1, l1), Ty::Array(a2, l2)) if l1 == l2 => self.unify(&a1, &a2), (a, b) => Err(format!("type mismatch: expected {:?}, found {:?}", a, b)), } } @@ -895,6 +901,71 @@ impl Sema { span: expr.span, } } + ExprKind::Array { elements } => { + let mut typed_elements = Vec::new(); + let element_ty = self.new_var(); + + for el in elements { + let typed_el = self.analyze_expr(el); + if let Err(e) = self.unify(&element_ty, &typed_el.ty) { + self.errors.push(SemanticError::new(e, el.span)); + } + typed_elements.push(typed_el); + } + + let len = elements.len(); + TypedExpr { + kind: TypedExprKind::Array { + elements: typed_elements, + }, + ty: Ty::Array(Box::new(element_ty), len), + span: expr.span, + } + } + ExprKind::Index { array, index } => { + let typed_array = self.analyze_expr(array); + let typed_index = self.analyze_expr(index); + + // Enforce that the array index evaluates to an integer + let default_index_ty = self.new_var(); + self.deferred_int_literals + .push((index.span, default_index_ty.clone())); + if let Err(e) = self.unify(&typed_index.ty, &default_index_ty) { + self.errors.push(SemanticError::new(e, index.span)); + } + + let result_ty = self.new_var(); + let array_ty_resolved = self.apply_subst(&typed_array.ty); + match array_ty_resolved { + Ty::Array(inner_ty, _) => { + if let Err(e) = self.unify(&result_ty, &inner_ty) { + self.errors.push(SemanticError::new(e, expr.span)); + } + } + Ty::Pointer(inner_ty) => { + if let Err(e) = self.unify(&result_ty, &inner_ty) { + self.errors.push(SemanticError::new(e, expr.span)); + } + } + Ty::Var(_) => self.errors.push(SemanticError::new( + "type of expression must be known to index", + array.span, + )), + _ => self.errors.push(SemanticError::new( + "cannot index into a non-array, non-pointer type", + array.span, + )), + } + + TypedExpr { + kind: TypedExprKind::Index { + array: Box::new(typed_array), + index: Box::new(typed_index), + }, + ty: result_ty, + span: expr.span, + } + } ExprKind::FieldAccess { expr: inner_expr, field, @@ -1108,6 +1179,16 @@ impl Sema { field, field_span, }, + TypedExprKind::Array { elements } => TypedExprKind::Array { + elements: elements + .into_iter() + .map(|e| self.apply_subst_expr(e)) + .collect(), + }, + TypedExprKind::Index { array, index } => TypedExprKind::Index { + array: Box::new(self.apply_subst_expr(*array)), + index: Box::new(self.apply_subst_expr(*index)), + }, }; TypedExpr { kind, ty, span } diff --git a/src/middle/builder.rs b/src/middle/builder.rs index 6dcb447..581c4d3 100644 --- a/src/middle/builder.rs +++ b/src/middle/builder.rs @@ -36,7 +36,7 @@ impl MirBuilder { return_type, body, } => { - let is_sret = matches!(return_type, Ty::Struct(_)); + let is_sret = matches!(return_type, Ty::Struct(_) | Ty::Array(_, _)); let mir_return_type = if is_sret { Ty::Unit } else { @@ -494,7 +494,7 @@ impl FuncBuilder { }; let mut arg_ops = Vec::new(); - let is_sret = matches!(expr.ty, Ty::Struct(_)); + let is_sret = matches!(expr.ty, Ty::Struct(_) | Ty::Array(_, _)); let mut sret_temp = None; // Implement implicit sret struct passing to the callee @@ -589,6 +589,57 @@ impl FuncBuilder { }); Operand::Copy(temp) } + TypedExprKind::Array { elements } => { + let local_id = self.new_temp(expr.ty.clone()); + self.locals[local_id.0].address_taken = true; + + let base_ptr_temp = self.new_temp(Ty::Pointer(Box::new(expr.ty.clone()))); + self.emit_stmt(Statement { + kind: StatementKind::Assign(base_ptr_temp, Rvalue::AddressOf(local_id)), + span: expr.span, + }); + + let element_ty = match &expr.ty { + Ty::Array(inner, _) => *inner.clone(), + _ => unreachable!(), + }; + + for (i, el) in elements.iter().enumerate() { + let val_op = self.lower_expr(el); + let index_op = Operand::Constant(ConstantValue::Integer(i as u64, Ty::I32)); + let element_ptr_temp = self.new_temp(Ty::Pointer(Box::new(element_ty.clone()))); + + self.emit_stmt(Statement { + kind: StatementKind::Assign( + element_ptr_temp, + Rvalue::GetElementPtr { + base_ptr: Operand::Copy(base_ptr_temp), + index: index_op, + element_ty: element_ty.clone(), + }, + ), + span: el.span, + }); + + self.emit_stmt(Statement { + kind: StatementKind::Store { + ptr: Operand::Copy(element_ptr_temp), + val: Rvalue::Use(val_op), + }, + span: el.span, + }); + } + Operand::Copy(local_id) + } + TypedExprKind::Index { .. } => { + let ptr_op = self.lower_address_of(expr); + let temp = self.new_temp(expr.ty.clone()); + self.emit_stmt(Statement { + kind: StatementKind::Assign(temp, Rvalue::ReadPointer(ptr_op)), + span: expr.span, + }); + Operand::Copy(temp) + } } } @@ -634,6 +685,27 @@ impl FuncBuilder { }); Operand::Copy(temp) } + TypedExprKind::Index { array, index } => { + let base_ptr = match &array.ty { + Ty::Array(_, _) => self.lower_address_of(array), + Ty::Pointer(_) => self.lower_expr(array), + _ => unreachable!(), + }; + let index_op = self.lower_expr(index); + let temp = self.new_temp(Ty::Pointer(Box::new(expr.ty.clone()))); + self.emit_stmt(Statement { + kind: StatementKind::Assign( + temp, + Rvalue::GetElementPtr { + base_ptr, + index: index_op, + element_ty: expr.ty.clone(), + }, + ), + span: expr.span, + }); + Operand::Copy(temp) + } _ => { let val_op = self.lower_expr(expr); if let Operand::Copy(id) = val_op { diff --git a/src/middle/fold.rs b/src/middle/fold.rs index e2b32ec..fcacbd1 100644 --- a/src/middle/fold.rs +++ b/src/middle/fold.rs @@ -95,6 +95,12 @@ fn propagate_rvalue(rvalue: &mut Rvalue, known_constants: &HashMap {} Rvalue::ReadPointer(op) => propagate_operand(op, known_constants), Rvalue::GetFieldPtr { base_ptr, .. } => propagate_operand(base_ptr, known_constants), + Rvalue::GetElementPtr { + base_ptr, index, .. + } => { + propagate_operand(base_ptr, known_constants); + propagate_operand(index, known_constants); + } } } diff --git a/src/middle/mir.rs b/src/middle/mir.rs index bda93a5..f3fca91 100644 --- a/src/middle/mir.rs +++ b/src/middle/mir.rs @@ -86,6 +86,11 @@ pub enum Rvalue { struct_name: String, field_name: String, }, + GetElementPtr { + base_ptr: Operand, + index: Operand, + element_ty: Ty, + }, } /// An atomic value used as inputs to instructions. diff --git a/tests/array_basic.test b/tests/array_basic.test new file mode 100644 index 0000000..577cf74 --- /dev/null +++ b/tests/array_basic.test @@ -0,0 +1,16 @@ +[code] +foreign fn putchar(c: i32) -> i32; + +fn main() -> i32 { + let arr: [i32; 3] = [65, 66, 67]; + putchar(arr[0]); + putchar(arr[1]); + putchar(arr[2]); + return 0; +} + +[expected_return_code] +0 + +[expected_output] +ABC \ No newline at end of file diff --git a/tests/array_swap.test b/tests/array_swap.test new file mode 100644 index 0000000..131b53f --- /dev/null +++ b/tests/array_swap.test @@ -0,0 +1,15 @@ +[code] +fn swap(a: *i32, b: *i32) { + let temp = *a; + *a = *b; + *b = temp; +} + +fn main() -> i32 { + let a = [10, 20]; + swap(&a[0], &a[1]); + return a[0] - a[1]; // 20 - 10 = 10 +} + +[expected_return_code] +10 \ No newline at end of file