feat: add support for arrays
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void print_number(int number) {
|
||||||
|
printf("%d", number);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -143,7 +143,7 @@ impl CraneliftBackend {
|
|||||||
let mut var_map = HashMap::new();
|
let mut var_map = HashMap::new();
|
||||||
let mut stack_slot_map = HashMap::new();
|
let mut stack_slot_map = HashMap::new();
|
||||||
for local in &func.locals {
|
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 bytes = Self::type_size(&local.ty, structs);
|
||||||
let slot = builder.create_sized_stack_slot(ir::StackSlotData::new(
|
let slot = builder.create_sized_stack_slot(ir::StackSlotData::new(
|
||||||
ir::StackSlotKind::ExplicitSlot,
|
ir::StackSlotKind::ExplicitSlot,
|
||||||
@@ -184,7 +184,7 @@ impl CraneliftBackend {
|
|||||||
for (j, param_id) in func.params.iter().enumerate() {
|
for (j, param_id) in func.params.iter().enumerate() {
|
||||||
let val = trans.builder.block_params(cl_block)[j];
|
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 slot = trans.stack_slot_map[param_id];
|
||||||
let dest_addr = trans.builder.ins().stack_addr(types::I64, slot, 0);
|
let dest_addr = trans.builder.ins().stack_addr(types::I64, slot, 0);
|
||||||
let size =
|
let size =
|
||||||
@@ -223,7 +223,7 @@ impl CraneliftBackend {
|
|||||||
Ty::F32 => types::F32,
|
Ty::F32 => types::F32,
|
||||||
Ty::F64 => types::F64,
|
Ty::F64 => types::F64,
|
||||||
Ty::Bool => types::I8, // Booleans are represented as 8-bit integers
|
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),
|
_ => unimplemented!("Unsupported type for Cranelift lowering: {:?}", ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,6 +247,10 @@ impl CraneliftBackend {
|
|||||||
}
|
}
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
|
Ty::Array(inner, len) => {
|
||||||
|
let elem_size = Self::type_size(inner, structs);
|
||||||
|
elem_size * (*len as u32)
|
||||||
|
}
|
||||||
Ty::Unit => 0,
|
Ty::Unit => 0,
|
||||||
Ty::Var(_) | Ty::Function(_, _) => unimplemented!(),
|
Ty::Var(_) | Ty::Function(_, _) => unimplemented!(),
|
||||||
}
|
}
|
||||||
@@ -293,11 +297,11 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
match &stmt.kind {
|
match &stmt.kind {
|
||||||
StatementKind::Assign(local_id, rvalue) => {
|
StatementKind::Assign(local_id, rvalue) => {
|
||||||
if let Some(&slot) = self.stack_slot_map.get(local_id) {
|
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);
|
let dest_addr = self.builder.ins().stack_addr(types::I64, slot, 0);
|
||||||
if let Some(src_addr) = self.translate_rvalue(rvalue) {
|
if let Some(src_addr) = self.translate_rvalue(rvalue) {
|
||||||
let size = CraneliftBackend::type_size(
|
let size = CraneliftBackend::type_size(
|
||||||
&Ty::Struct(name.clone()),
|
&self.locals[local_id.0].ty,
|
||||||
self.structs,
|
self.structs,
|
||||||
);
|
);
|
||||||
self.emit_memcpy(dest_addr, src_addr, size);
|
self.emit_memcpy(dest_addr, src_addr, size);
|
||||||
@@ -318,7 +322,7 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
StatementKind::Store { ptr, val } => {
|
StatementKind::Store { ptr, val } => {
|
||||||
let ptr_val = self.translate_operand(ptr);
|
let ptr_val = self.translate_operand(ptr);
|
||||||
let rval_ty = self.get_rvalue_type(val);
|
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) {
|
if let Some(src_addr) = self.translate_rvalue(val) {
|
||||||
let size = CraneliftBackend::type_size(&rval_ty, self.structs);
|
let size = CraneliftBackend::type_size(&rval_ty, self.structs);
|
||||||
self.emit_memcpy(ptr_val, src_addr, size);
|
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 {
|
fn translate_operand(&mut self, op: &Operand) -> ir::Value {
|
||||||
match op {
|
match op {
|
||||||
Operand::Copy(local_id) => {
|
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];
|
let slot = self.stack_slot_map[local_id];
|
||||||
self.builder.ins().stack_addr(types::I64, slot, 0)
|
self.builder.ins().stack_addr(types::I64, slot, 0)
|
||||||
} else if let Some(&slot) = self.stack_slot_map.get(local_id) {
|
} else if let Some(&slot) = self.stack_slot_map.get(local_id) {
|
||||||
@@ -656,7 +660,7 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
Ty::Pointer(inner) => *inner,
|
Ty::Pointer(inner) => *inner,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
if matches!(inner_ty, Ty::Struct(_)) {
|
if matches!(inner_ty, Ty::Struct(_) | Ty::Array(_, _)) {
|
||||||
Some(ptr_val)
|
Some(ptr_val)
|
||||||
} else {
|
} else {
|
||||||
let cl_ty = CraneliftBackend::lower_type(&inner_ty);
|
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);
|
let offset = CraneliftBackend::field_offset(struct_name, field_name, self.structs);
|
||||||
Some(self.builder.ins().iadd_imm(base, offset as i64))
|
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();
|
.clone();
|
||||||
Ty::Pointer(Box::new(ty))
|
Ty::Pointer(Box::new(ty))
|
||||||
}
|
}
|
||||||
|
Rvalue::GetElementPtr { element_ty, .. } => Ty::Pointer(Box::new(element_ty.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ pub enum TypeKind {
|
|||||||
Bool,
|
Bool,
|
||||||
Pointer(Box<Type>),
|
Pointer(Box<Type>),
|
||||||
Struct(String),
|
Struct(String),
|
||||||
|
Array(Box<Type>, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@@ -195,6 +196,13 @@ pub enum ExprKind<P: Phase = Untyped> {
|
|||||||
field: String,
|
field: String,
|
||||||
field_span: Span,
|
field_span: Span,
|
||||||
},
|
},
|
||||||
|
Array {
|
||||||
|
elements: Vec<Expr<P>>,
|
||||||
|
},
|
||||||
|
Index {
|
||||||
|
array: Box<Expr<P>>,
|
||||||
|
index: Box<Expr<P>>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
|||||||
+62
-1
@@ -377,6 +377,19 @@ impl<'src> Parser<'src> {
|
|||||||
span,
|
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(
|
return Err(ParseError::new(
|
||||||
@@ -642,7 +655,9 @@ impl<'src> Parser<'src> {
|
|||||||
let mut lhs = self.parse_leading_expr(allow_struct)?;
|
let mut lhs = self.parse_leading_expr(allow_struct)?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let peek_token = self.peek_no_eof()?;
|
let Some(peek_token) = self.peek() else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
if peek_token.kind == TokenKind::Assign {
|
if peek_token.kind == TokenKind::Assign {
|
||||||
let left_bp = 2;
|
let left_bp = 2;
|
||||||
@@ -743,6 +758,28 @@ impl<'src> Parser<'src> {
|
|||||||
continue;
|
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 {
|
let Some((op, left_bp, right_bp)) = self.infix_operator(peek_token.kind) else {
|
||||||
break; // Not an infix operator
|
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) => {
|
kind if let Some((op, r_bp)) = self.prefix_operator(kind) => {
|
||||||
let op_token = self.advance().unwrap();
|
let op_token = self.advance().unwrap();
|
||||||
let rhs = self.parse_expr_bp(r_bp, allow_struct)?;
|
let rhs = self.parse_expr_bp(r_bp, allow_struct)?;
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ pub enum Ty {
|
|||||||
Function(Vec<Ty>, Box<Ty>),
|
Function(Vec<Ty>, Box<Ty>),
|
||||||
Pointer(Box<Ty>),
|
Pointer(Box<Ty>),
|
||||||
Struct(String),
|
Struct(String),
|
||||||
|
Array(Box<Ty>, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ty {
|
impl Ty {
|
||||||
@@ -110,6 +111,7 @@ impl From<&TypeKind> for Ty {
|
|||||||
TypeKind::Bool => Ty::Bool,
|
TypeKind::Bool => Ty::Bool,
|
||||||
TypeKind::Pointer(inner) => Ty::Pointer(Box::new(Ty::from(&inner.kind))),
|
TypeKind::Pointer(inner) => Ty::Pointer(Box::new(Ty::from(&inner.kind))),
|
||||||
TypeKind::Struct(name) => Ty::Struct(name.clone()),
|
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, ..
|
op: UnaryOp::Deref, ..
|
||||||
} => true,
|
} => true,
|
||||||
TypedExprKind::FieldAccess { expr, .. } => expr.is_lvalue(),
|
TypedExprKind::FieldAccess { expr, .. } => expr.is_lvalue(),
|
||||||
|
TypedExprKind::Index { .. } => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,6 +216,7 @@ impl Sema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ty::Pointer(inner) => Ty::Pointer(Box::new(self.apply_subst(inner))),
|
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(),
|
t => t.clone(),
|
||||||
}
|
}
|
||||||
@@ -228,6 +232,7 @@ impl Sema {
|
|||||||
params.iter().any(|p| self.occurs_check(v, p)) || self.occurs_check(v, &ret)
|
params.iter().any(|p| self.occurs_check(v, p)) || self.occurs_check(v, &ret)
|
||||||
}
|
}
|
||||||
Ty::Pointer(inner) => self.occurs_check(v, &inner),
|
Ty::Pointer(inner) => self.occurs_check(v, &inner),
|
||||||
|
Ty::Array(inner, _) => self.occurs_check(v, &inner),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,6 +269,7 @@ impl Sema {
|
|||||||
self.unify(&r1, &r2)
|
self.unify(&r1, &r2)
|
||||||
}
|
}
|
||||||
(Ty::Pointer(p1), Ty::Pointer(p2)) => self.unify(&p1, &p2),
|
(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)),
|
(a, b) => Err(format!("type mismatch: expected {:?}, found {:?}", a, b)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -895,6 +901,71 @@ impl Sema {
|
|||||||
span: expr.span,
|
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 {
|
ExprKind::FieldAccess {
|
||||||
expr: inner_expr,
|
expr: inner_expr,
|
||||||
field,
|
field,
|
||||||
@@ -1108,6 +1179,16 @@ impl Sema {
|
|||||||
field,
|
field,
|
||||||
field_span,
|
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 }
|
TypedExpr { kind, ty, span }
|
||||||
|
|||||||
+74
-2
@@ -36,7 +36,7 @@ impl MirBuilder {
|
|||||||
return_type,
|
return_type,
|
||||||
body,
|
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 {
|
let mir_return_type = if is_sret {
|
||||||
Ty::Unit
|
Ty::Unit
|
||||||
} else {
|
} else {
|
||||||
@@ -494,7 +494,7 @@ impl FuncBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut arg_ops = Vec::new();
|
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;
|
let mut sret_temp = None;
|
||||||
|
|
||||||
// Implement implicit sret struct passing to the callee
|
// Implement implicit sret struct passing to the callee
|
||||||
@@ -589,6 +589,57 @@ impl FuncBuilder {
|
|||||||
});
|
});
|
||||||
Operand::Copy(temp)
|
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)
|
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);
|
let val_op = self.lower_expr(expr);
|
||||||
if let Operand::Copy(id) = val_op {
|
if let Operand::Copy(id) = val_op {
|
||||||
|
|||||||
@@ -95,6 +95,12 @@ fn propagate_rvalue(rvalue: &mut Rvalue, known_constants: &HashMap<LocalId, Cons
|
|||||||
Rvalue::AddressOf(_) => {}
|
Rvalue::AddressOf(_) => {}
|
||||||
Rvalue::ReadPointer(op) => propagate_operand(op, known_constants),
|
Rvalue::ReadPointer(op) => propagate_operand(op, known_constants),
|
||||||
Rvalue::GetFieldPtr { base_ptr, .. } => propagate_operand(base_ptr, 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,11 @@ pub enum Rvalue {
|
|||||||
struct_name: String,
|
struct_name: String,
|
||||||
field_name: String,
|
field_name: String,
|
||||||
},
|
},
|
||||||
|
GetElementPtr {
|
||||||
|
base_ptr: Operand,
|
||||||
|
index: Operand,
|
||||||
|
element_ty: Ty,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An atomic value used as inputs to instructions.
|
/// An atomic value used as inputs to instructions.
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user