fixed arrays

This commit is contained in:
Masashi 2025-12-19 21:16:38 +05:30
commit f8abae63b3
12 changed files with 183 additions and 26 deletions

View file

@ -11,6 +11,7 @@ pub enum TypeAnnot {
Function(Vec<TypeAnnot>, Box<TypeAnnot>),
Tuple(Vec<TypeAnnot>),
Array(Box<TypeAnnot>),
FixedArray(Box<TypeAnnot>, i64),
Ptr(Box<TypeAnnot>),
}
@ -208,6 +209,7 @@ pub enum ExprKind {
Bool(bool),
String(String),
Array(Vec<Expr>),
FixedArray(Box<Expr>, i64),
Tuple(Vec<Expr>),
StructLit(String, Vec<(String, Expr)>), // Name { a: expr, b: expr }
@ -402,6 +404,7 @@ pub enum TypedExprKind {
Bool(bool),
String(String),
Array(Vec<TypedExpr>),
FixedArray(Box<TypedExpr>, usize),
Tuple(Vec<TypedExpr>),
StructLit(String, Vec<(String, TypedExpr)>),
EnumLit(String, String, Vec<TypedExpr>),

View file

@ -9,6 +9,7 @@ pub enum CType {
Struct(String),
UnnamedStruct(Vec<CVarDecl>),
Array(Box<CType>), // heap-allocated array wrapper with data pointer, len, capacity
FixedArray(Box<CType>, usize), // fixed-size array like int[10]
Func(Vec<CType>, Box<CType>), // 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<String> = args.iter().map(|t| t.to_string()).collect();
format!("{} (*)({})", ret.to_string(), arg_strs.join(", "))

View file

@ -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() {

View file

@ -117,6 +117,11 @@ impl StatementsTranspiler {
.collect::<Result<Vec<_>, _>>()?;
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() {

View file

@ -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<String> = elements
.iter()
.map(|expr| self.generate_expr(expr))
.collect();
output.push_str(&format!(" = {{{}}}", vec.join(", ")));
}
_ => {
output.push_str(&format!(" = {}", self.generate_expr(init)));
}
}
}
output
}

View file

@ -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::<Result<Vec<_>, _>>()?;
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()

View file

@ -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),
}
}

View file

@ -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();

View file

@ -12,6 +12,7 @@ pub enum Type {
Unit,
Never,
Array(Box<Type>),
FixedArray(Box<Type>, usize),
Ptr(Box<Type>),
Tuple(Vec<Type>),
Function(Vec<Type>, Box<Type>),
@ -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<String> = 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<Type> = 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()

47
tests/fixed_arrays.c Normal file
View file

@ -0,0 +1,47 @@
#include "libsuicmez/libsuicmez.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
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;
}

BIN
tests/fixed_arrays.o Executable file

Binary file not shown.

6
tests/fixed_arrays.sui Normal file
View file

@ -0,0 +1,6 @@
# Fixed array test
fn main() -> int do
let arr: [int; 5] = [42; 5];
let x = arr[2];
x
end