diff --git a/src/ast.rs b/src/ast.rs index c4008c5..6e694f2 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -11,6 +11,7 @@ pub enum TypeAnnot { Function(Vec, Box), Tuple(Vec), Array(Box), + FixedArray(Box, i64), Ptr(Box), } @@ -208,6 +209,7 @@ pub enum ExprKind { Bool(bool), String(String), Array(Vec), + FixedArray(Box, i64), Tuple(Vec), StructLit(String, Vec<(String, Expr)>), // Name { a: expr, b: expr } @@ -402,6 +404,7 @@ pub enum TypedExprKind { Bool(bool), String(String), Array(Vec), + FixedArray(Box, usize), Tuple(Vec), StructLit(String, Vec<(String, TypedExpr)>), EnumLit(String, String, Vec), diff --git a/src/c_ir.rs b/src/c_ir.rs index 15e5ea0..44c0638 100644 --- a/src/c_ir.rs +++ b/src/c_ir.rs @@ -9,6 +9,7 @@ pub enum CType { Struct(String), UnnamedStruct(Vec), Array(Box), // heap-allocated array wrapper with data pointer, len, capacity + FixedArray(Box, usize), // fixed-size array like int[10] Func(Vec, Box), // args and return } @@ -48,6 +49,7 @@ impl CType { "struct sui_array_{}", inner.to_string().replace(" ", "_").replace("*", "ptr") ), + CType::FixedArray(inner, size) => format!("{}*", inner.to_string()), // For now, treat as pointer in type strings CType::Func(args, ret) => { let arg_strs: Vec = args.iter().map(|t| t.to_string()).collect(); format!("{} (*)({})", ret.to_string(), arg_strs.join(", ")) diff --git a/src/c_lowerer/declaration_transpiler.rs b/src/c_lowerer/declaration_transpiler.rs index 5cb7dae..7d8a0bb 100644 --- a/src/c_lowerer/declaration_transpiler.rs +++ b/src/c_lowerer/declaration_transpiler.rs @@ -148,6 +148,10 @@ impl DeclarationTranspiler { // Arrays are pointers to the element type Ok(CType::Ptr(Box::new(inner_type))) } + Some(TypeAnnot::FixedArray(inner, size)) => { + let inner_type = self.type_annot_to_c_type(&Some(*inner.clone()))?; + Ok(CType::FixedArray(Box::new(inner_type), *size as usize)) + } Some(TypeAnnot::Tuple(fields)) => { let mut c_fields = Vec::new(); for (i, field_annot) in fields.iter().enumerate() { diff --git a/src/c_lowerer/statements_transpiler.rs b/src/c_lowerer/statements_transpiler.rs index fdf35c5..6d27754 100644 --- a/src/c_lowerer/statements_transpiler.rs +++ b/src/c_lowerer/statements_transpiler.rs @@ -117,6 +117,11 @@ impl StatementsTranspiler { .collect::, _>>()?; Ok(CExpr::ArrayLit(c_exprs)) } + TypedExprKind::FixedArray(expr, size) => { + let c_expr = self.transpile_expr(expr)?; + let c_exprs = vec![c_expr; *size]; + Ok(CExpr::ArrayLit(c_exprs)) + } TypedExprKind::Cast(expr, type_annot) => { let c_expr = self.transpile_expr(expr)?; // Simplified: assuming we can map type annotations to C types @@ -482,6 +487,10 @@ impl StatementsTranspiler { // Arrays are pointers to the element type Ok(CType::Ptr(Box::new(inner_type))) } + Type::FixedArray(inner, size) => { + let inner_type = self.type_to_ctype(inner)?; + Ok(CType::FixedArray(Box::new(inner_type), *size)) + } Type::Struct(name, _) => Ok(CType::Ptr(Box::new(CType::Struct(name.clone())))), Type::Enum(name, _) => Ok(CType::Ptr(Box::new(CType::Struct(name.clone())))), Type::Tuple(types) => { @@ -536,6 +545,10 @@ impl StatementsTranspiler { // Arrays are pointers to the element type Ok(CType::Ptr(Box::new(inner_type))) } + TypeAnnot::FixedArray(inner, size) => { + let inner_type = self.type_annot_to_ctype(inner)?; + Ok(CType::FixedArray(Box::new(inner_type), *size as usize)) + } TypeAnnot::Tuple(fields) => { let mut c_fields = Vec::new(); for (i, field_annot) in fields.iter().enumerate() { diff --git a/src/codegen/transpiler.rs b/src/codegen/transpiler.rs index d3ab12b..e830f36 100644 --- a/src/codegen/transpiler.rs +++ b/src/codegen/transpiler.rs @@ -404,9 +404,23 @@ impl Transpiler { } fn generate_var_decl(&self, var: &CVarDecl) -> String { - let mut output = format!("{} {}", var.ty.to_string(), var.name); + let mut output = match &var.ty { + CType::FixedArray(elem_ty, size) => format!("{} {}[{}]", elem_ty.to_string(), var.name, size), + _ => format!("{} {}", var.ty.to_string(), var.name), + }; if let Some(init) = &var.initializer { - output.push_str(&format!(" = {}", self.generate_expr(init))); + match (&var.ty, init) { + (CType::FixedArray(_, _), CExpr::ArrayLit(elements)) => { + let vec: Vec = elements + .iter() + .map(|expr| self.generate_expr(expr)) + .collect(); + output.push_str(&format!(" = {{{}}}", vec.join(", "))); + } + _ => { + output.push_str(&format!(" = {}", self.generate_expr(init))); + } + } } output } diff --git a/src/lambda_lower.rs b/src/lambda_lower.rs index b9996b1..97e25c7 100644 --- a/src/lambda_lower.rs +++ b/src/lambda_lower.rs @@ -126,6 +126,9 @@ impl LambdaLowerer { self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope); } } + ExprKind::FixedArray(expr, _) => { + self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope); + } ExprKind::Tuple(exprs) => { for expr in exprs { self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope); @@ -340,6 +343,10 @@ impl LambdaLowerer { .collect::, _>>()?; ExprKind::Array(lowered_exprs) } + ExprKind::FixedArray(expr, size) => { + let lowered_expr = self.lower_expr(expr)?; + ExprKind::FixedArray(Box::new(lowered_expr), *size) + } ExprKind::Tuple(exprs) => { let lowered_exprs = exprs .iter() diff --git a/src/monomorphize.rs b/src/monomorphize.rs index fb6c6ac..dd47187 100644 --- a/src/monomorphize.rs +++ b/src/monomorphize.rs @@ -342,6 +342,11 @@ impl Monomorphizer { } TypedExprKind::Array(new_elems) } + TypedExprKind::FixedArray(expr, size) => { + let (new_expr, expr_needs) = self.monomorphize_expr(expr)?; + needs.extend(expr_needs); + TypedExprKind::FixedArray(Box::new(new_expr), *size) + } TypedExprKind::Tuple(elems) => { let mut new_elems = Vec::new(); @@ -767,6 +772,10 @@ impl Monomorphizer { let new_inner = self.substitute_in_type_annot(inner, subst_map)?; Ok(TypeAnnot::Array(Box::new(new_inner))) } + TypeAnnot::FixedArray(inner, size) => { + let new_inner = self.substitute_in_type_annot(inner, subst_map)?; + Ok(TypeAnnot::FixedArray(Box::new(new_inner), *size)) + } TypeAnnot::Ptr(inner) => { let new_inner = self.substitute_in_type_annot(inner, subst_map)?; Ok(TypeAnnot::Ptr(Box::new(new_inner))) @@ -782,6 +791,7 @@ impl Monomorphizer { Type::String => TypeAnnot::Var("string".to_string()), Type::Unit => TypeAnnot::Tuple(Vec::new()), Type::Array(inner) => TypeAnnot::Array(Box::new(self.type_to_type_annot(inner))), + Type::FixedArray(inner, size) => TypeAnnot::FixedArray(Box::new(self.type_to_type_annot(inner)), *size as i64), Type::Ptr(inner) => TypeAnnot::Ptr(Box::new(self.type_to_type_annot(inner))), Type::Tuple(types) => { let annots = types.iter().map(|t| self.type_to_type_annot(t)).collect(); @@ -901,6 +911,9 @@ impl Monomorphizer { TypeAnnot::Array(inner) => { self.collect_needs_from_type(inner, needs); } + TypeAnnot::FixedArray(inner, _) => { + self.collect_needs_from_type(inner, needs); + } TypeAnnot::Ptr(inner) => { self.collect_needs_from_type(inner, needs); } @@ -932,10 +945,8 @@ impl Monomorphizer { let tys = types.iter().map(|t| self.type_annot_to_type(t)).collect(); Type::Tuple(tys) } - TypeAnnot::Array(inner) => { - let inner_type = self.type_annot_to_type(inner); - Type::Array(Box::new(inner_type)) - } + TypeAnnot::Array(inner) => Type::Array(Box::new(self.type_annot_to_type(inner))), + TypeAnnot::FixedArray(inner, size) => Type::FixedArray(Box::new(self.type_annot_to_type(inner)), *size as usize), TypeAnnot::Ptr(inner) => { let inner_type = self.type_annot_to_type(inner); Type::Ptr(Box::new(inner_type)) @@ -1216,6 +1227,7 @@ fn has_typevars_in_type_annot(ty: &TypeAnnot) -> bool { } TypeAnnot::Tuple(types) => types.iter().any(has_typevars_in_type_annot), TypeAnnot::Array(inner) => has_typevars_in_type_annot(inner), + TypeAnnot::FixedArray(inner, _) => has_typevars_in_type_annot(inner), TypeAnnot::Ptr(inner) => has_typevars_in_type_annot(inner), } } diff --git a/src/parser.rs b/src/parser.rs index 0f87e82..9d9e9c2 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -912,8 +912,23 @@ impl Parser { } Some((Token::LBracket, _)) => { let inner = self.parse_type_annot()?; - self.expect(Token::RBracket)?; - TypeAnnot::Array(Box::new(inner)) + if matches!(self.peek(), Some(Token::Semicolon)) { + self.next(); + let size = match self.next() { + Some((Token::Int(n), _)) => n, + Some((_, span)) => { + return self.error("Expected integer size for fixed array".to_string(), span); + } + None => { + return self.error("Expected integer size for fixed array".to_string(), start..start); + } + }; + self.expect(Token::RBracket)?; + TypeAnnot::FixedArray(Box::new(inner), size) + } else { + self.expect(Token::RBracket)?; + TypeAnnot::Array(Box::new(inner)) + } } Some((Token::Bang, _)) => TypeAnnot::Cons("never".to_string(), vec![]), Some((_, span)) => { @@ -1607,25 +1622,44 @@ impl Parser { } Some(Token::LBracket) => { self.next(); - let mut elements = Vec::new(); - loop { - if matches!(self.peek(), Some(Token::RBracket)) { - self.next(); - break; - } - elements.push(self.parse_expr()?); - - if matches!(self.peek(), Some(Token::Comma)) { - self.next(); + let first_expr = self.parse_expr()?; + if matches!(self.peek(), Some(Token::Semicolon)) { + self.next(); + let size = match self.next() { + Some((Token::Int(n), _)) => n, + Some((_, span)) => { + return self.error("Expected integer size for fixed array".to_string(), span); + } + None => { + return self.error("Expected integer size for fixed array".to_string(), start..start); + } + }; + self.expect(Token::RBracket)?; + let end = self.peek_span().unwrap_or(start..start).end; + Ok(Expr { + kind: ExprKind::FixedArray(Box::new(first_expr), size), + span: Span::new(&(start..end), self.file.clone()), + attributes: Vec::new(), + }) + } else { + let mut elements = vec![first_expr]; + loop { + if matches!(self.peek(), Some(Token::RBracket)) { + self.next(); + break; + } + if matches!(self.peek(), Some(Token::Comma)) { + self.next(); + } + elements.push(self.parse_expr()?); } + let end = self.peek_span().unwrap_or(start..start).end; + Ok(Expr { + kind: ExprKind::Array(elements), + span: Span::new(&(start..end), self.file.clone()), + attributes: Vec::new(), + }) } - - let end = self.peek_span().unwrap_or(start..start).end; - Ok(Expr { - kind: ExprKind::Array(elements), - span: Span::new(&(start..end), self.file.clone()), - attributes: Vec::new(), - }) } Some(Token::KeywordLet) => { self.next(); diff --git a/src/typechecker.rs b/src/typechecker.rs index e1c21fe..9b07ae4 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -12,6 +12,7 @@ pub enum Type { Unit, Never, Array(Box), + FixedArray(Box, usize), Ptr(Box), Tuple(Vec), Function(Vec, Box), @@ -32,6 +33,7 @@ impl Type { Type::Unit => "()".to_string(), Type::Never => "!".to_string(), Type::Array(inner) => format!("[{}]", inner.to_string()), + Type::FixedArray(inner, size) => format!("[{}; {}]", inner.to_string(), size), Type::Ptr(inner) => format!("*{}", inner.to_string()), Type::Tuple(types) => { let type_strs: Vec = types.iter().map(|t| t.to_string()).collect(); @@ -1814,6 +1816,14 @@ impl TypeChecker { ) } + ExprKind::FixedArray(expr, size) => { + let typed_expr = self.typecheck_expr(expr)?; + ( + TypedExprKind::FixedArray(Box::new(typed_expr.clone()), *size as usize), + Type::FixedArray(Box::new(typed_expr.ty), *size as usize), + ) + } + ExprKind::Tuple(elements) => { let mut typed_elements = Vec::new(); let mut types = Vec::new(); @@ -2171,7 +2181,7 @@ impl TypeChecker { } let element_type = match &typed_array.ty { - Type::Array(elem_ty) => (**elem_ty).clone(), + Type::Array(elem_ty) | Type::FixedArray(elem_ty, _) => (**elem_ty).clone(), ty => { return Err(TypeError { kind: TypeErrorKind::NotAnArray(ty.clone()), @@ -2850,6 +2860,9 @@ impl TypeChecker { TypeAnnot::Array(inner) => { Type::Array(Box::new(self.substitute_type(inner, subst_map))) } + TypeAnnot::FixedArray(inner, size) => { + Type::FixedArray(Box::new(self.substitute_type(inner, subst_map)), *size as usize) + } TypeAnnot::Ptr(inner) => Type::Ptr(Box::new(self.substitute_type(inner, subst_map))), TypeAnnot::Tuple(types) => { let substituted_types: Vec = types @@ -2923,6 +2936,7 @@ impl TypeChecker { Type::Tuple(tuple_types) } TypeAnnot::Array(inner) => Type::Array(Box::new(self.type_annot_to_type(inner))), + TypeAnnot::FixedArray(inner, size) => Type::FixedArray(Box::new(self.type_annot_to_type(inner)), *size as usize), TypeAnnot::Ptr(inner) => Type::Ptr(Box::new(self.type_annot_to_type(inner))), } } @@ -2937,6 +2951,7 @@ impl TypeChecker { (Type::Unit, Type::Unit) => true, (Type::Never, _) | (_, Type::Never) => true, (Type::Array(a), Type::Array(b)) => self.types_compatible(a, b), + (Type::FixedArray(a, s1), Type::FixedArray(b, s2)) => s1 == s2 && self.types_compatible(a, b), (Type::Ptr(a), Type::Ptr(b)) => self.types_compatible(a, b), (Type::Tuple(a), Type::Tuple(b)) => { a.len() == b.len() diff --git a/tests/fixed_arrays.c b/tests/fixed_arrays.c new file mode 100644 index 0000000..9d22d90 --- /dev/null +++ b/tests/fixed_arrays.c @@ -0,0 +1,47 @@ +#include "libsuicmez/libsuicmez.h" +#include +#include +#include +#include + +void* gc_alloc(const TypeInfo* type, size_t size); +void gc_init(void); +void gc_shutdown(void); + +// Helper for allocating arrays +static void* suic_alloc_array(const TypeInfo* type, size_t elem_size, size_t len, void* init_data) { + void* ptr = gc_alloc(type, elem_size * len); + if (init_data) memcpy(ptr, init_data, elem_size * len); + return ptr; +} + +// Helper for allocating structs +static void* suic_alloc_struct(const TypeInfo* type, size_t size, void* init_data) { + void* ptr = gc_alloc(type, size); + if (init_data) memcpy(ptr, init_data, size); + return ptr; +} + + + + +int suic_main(void); + + +int suic_main(void) { + int arr[5] = {42, 42, 42, 42, 42}; + int x = arr[2]; + return x; +} + +int main(int argc, char* argv[]) { + // Initialize GC + gc_init(); + // init globals + // init event loop + int result = suic_main(); + // Shutdown GC + gc_shutdown(); + return result; +} + diff --git a/tests/fixed_arrays.o b/tests/fixed_arrays.o new file mode 100755 index 0000000..61598b3 Binary files /dev/null and b/tests/fixed_arrays.o differ diff --git a/tests/fixed_arrays.sui b/tests/fixed_arrays.sui new file mode 100644 index 0000000..c233d33 --- /dev/null +++ b/tests/fixed_arrays.sui @@ -0,0 +1,6 @@ +# Fixed array test +fn main() -> int do + let arr: [int; 5] = [42; 5]; + let x = arr[2]; + x +end \ No newline at end of file