Compare commits

...

3 commits

Author SHA1 Message Date
22afdb20a4 const kw 2025-12-20 01:56:02 +05:30
dca10507c0 const kw 2025-12-20 01:55:12 +05:30
f8abae63b3 fixed arrays 2025-12-20 01:54:24 +05:30
18 changed files with 2298 additions and 1439 deletions

File diff suppressed because it is too large Load diff

1174
game/client.c.bak Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,218 +0,0 @@
Wildered
wildered.da.programmer
Online
Masashi
— 5:27 PM
ill add both
Wildered — 5:27 PM
and allow for using variables obv
duh
ii love you
not in the homo way
xd
Masashi
— 5:27 PM
okok
love u too bro (no homo)
Wildered — 5:28 PM
lol
Masashi
— 5:32 PM
fuck
nishi's new version of libfishsoup
broke our compiler
:despair:
bitwise added
now arrays
Masashi
— 5:39 PM
@Wildered
Wildered — 5:40 PM
yes
Masashi
— 5:40 PM
because of the way our typesystem works
we DONT have null
ull need to do [Option::None; size]
is that okay?
and whatever ur using just make it Option::Some
oh wait
enums dont work
................
@Wildered what do we do
wait we can do this
say u want an array of type T
u make struct TWrapper
with val: T and is_used: bool
how's the idea
Wildered — 5:45 PM
hi
yes
that's fine
Wildered — 5:46 PM
this is ok
no worries
Masashi
— 5:46 PM
homemade Option 😭
Wildered — 5:46 PM
lmao\
Masashi
— 5:46 PM
doesnt work
Masashi
— 5:46 PM
.
Wildered — 5:46 PM
im fine w it
what's the syntax for it
Masashi
— 5:46 PM
w
fn main() -> int do
let arr: [int; 5] = [42; 5];
let x = arr[2];
x
end
Wildered — 5:47 PM
so 42 is the default
Masashi
— 5:47 PM
correct
Wildered — 5:47 PM
that's brilliant yay
:D
Masashi
— 5:48 PM
:3
Masashi
— 6:47 PM
progress?
Wildered — 6:55 PM
almost done
sneakpeak
Image
Starting import resolution...
[parsing] game/client.sui
Error: Parse error in game/client.sui: Unexpected token in pattern: KeywordElse. Expected variable names, struct patterns like Struct { field }, or enum patterns like Enum::Variant
34 | else
| ~~~~
| ^
fn main() -> int do
suic_init_window(1280, 720, "Soup - Voxel Battle Royale");
suic_enable_msaa_4x();
let current_screen = 0;
let player_name = "Player";
let server_host = "127.0.0.1";
Expand
message.txt
5 KB

fn main() -> int do
suic_init_window(1280, 720, "Soup - Voxel Battle Royale");
suic_enable_msaa_4x();
let current_screen = 0;
let player_name = "Player";
let server_host = "127.0.0.1";
let server_port = 8888;
suic_show_fps_meter(suic_vec2 { x: 10.0, y: 10.0 });
while suic_window_should_close() == false do
suic_begin_drawing();
suic_clear_background(30, 30, 40, 255);
if current_screen == 0 do
suic_draw_text("SOUP", 450, 80, 120, 100, 200, 255, 255);
suic_draw_text("Voxel Battle Royale", 400, 220, 40, 200, 200, 200, 255);
suic_draw_rectangle(450, 320, 380, 80, 50, 150, 255, 255);
suic_draw_rectangle_lines(450, 320, 380, 80, 100, 200, 255, 255);
suic_draw_text("PLAY", 600, 350, 40, 255, 255, 255, 255);
suic_draw_rectangle(450, 430, 380, 80, 100, 100, 100, 255);
suic_draw_rectangle_lines(450, 430, 380, 80, 150, 150, 150, 255);
suic_draw_text("SETTINGS", 540, 460, 40, 255, 255, 255, 255);
suic_draw_rectangle(450, 540, 380, 80, 150, 50, 50, 255);
suic_draw_rectangle_lines(450, 540, 380, 80, 200, 100, 100, 255);
suic_draw_text("QUIT", 600, 570, 40, 255, 255, 255, 255);
suic_draw_text("v0.1.0 - Early Access", 500, 680, 20, 150, 150, 150, 255);
let mouse_x = suic_get_mouse_x() as int;
let mouse_y = suic_get_mouse_y() as int;
let is_mouse_clicked = suic_is_mouse_button_pressed(MOUSE_BUTTON_LEFT);
if mouse_x >= 450 && mouse_x <= 830 && mouse_y >= 320 && mouse_y <= 400 && is_mouse_clicked != 0 do
current_screen = 1;
end
if mouse_x >= 450 && mouse_x <= 830 && mouse_y >= 430 && mouse_y <= 510 && is_mouse_clicked != 0 do
current_screen = 2;
end
if mouse_x >= 450 && mouse_x <= 830 && mouse_y >= 540 && mouse_y <= 620 && is_mouse_clicked != 0 do
break;
end
end
if current_screen == 1 do
suic_draw_text("MATCHMAKING", 450, 100, 60, 100, 200, 255, 255);
suic_draw_text("Connecting to server...", 400, 250, 30, 200, 200, 200, 255);
suic_draw_text("Host: 127.0.0.1:8888", 400, 310, 20, 150, 150, 150, 255);
suic_draw_text("Your Name: Player", 400, 380, 20, 200, 200, 200, 255);
suic_draw_text("Players in Queue: 0/100", 400, 420, 20, 200, 200, 200, 255);
suic_draw_text("Loading...", 550, 500, 30, 100, 200, 255, 255);
suic_draw_rectangle(100, 630, 150, 60, 150, 50, 50, 255);
suic_draw_rectangle_lines(100, 630, 150, 60, 200, 100, 100, 255);
suic_draw_text("BACK", 120, 650, 30, 255, 255, 255, 255);
let mouse_x = suic_get_mouse_x() as int;
let mouse_y = suic_get_mouse_y() as int;
let is_mouse_clicked = suic_is_mouse_button_pressed(MOUSE_BUTTON_LEFT);
if mouse_x >= 100 && mouse_x <= 250 && mouse_y >= 630 && mouse_y <= 690 && is_mouse_clicked != 0 do
current_screen = 0;
end
end
if current_screen == 2 do
suic_draw_text("SETTINGS", 450, 100, 60, 100, 200, 255, 255);
suic_draw_text("Player Name:", 250, 200, 24, 200, 200, 200, 255);
suic_draw_rectangle(450, 190, 300, 50, 50, 50, 50, 255);
suic_draw_rectangle_lines(450, 190, 300, 50, 100, 100, 100, 255);
suic_draw_text("Player", 460, 205, 20, 255, 255, 255, 255);
suic_draw_text("Server Host:", 250, 300, 24, 200, 200, 200, 255);
suic_draw_rectangle(450, 290, 300, 50, 50, 50, 50, 255);
suic_draw_rectangle_lines(450, 290, 300, 50, 100, 100, 100, 255);
suic_draw_text("127.0.0.1", 460, 305, 20, 255, 255, 255, 255);
suic_draw_text("Server Port:", 250, 400, 24, 200, 200, 200, 255);
suic_draw_rectangle(450, 390, 300, 50, 50, 50, 50, 255);
suic_draw_rectangle_lines(450, 390, 300, 50, 100, 100, 100, 255);
suic_draw_text("8888", 460, 405, 20, 255, 255, 255, 255);
suic_draw_rectangle(450, 550, 300, 70, 100, 100, 100, 255);
suic_draw_rectangle_lines(450, 550, 300, 70, 150, 150, 150, 255);
suic_draw_text("BACK TO HOME", 495, 577, 30, 255, 255, 255, 255);
let mouse_x = suic_get_mouse_x() as int;
let mouse_y = suic_get_mouse_y() as int;
let is_mouse_clicked = suic_is_mouse_button_pressed(MOUSE_BUTTON_LEFT);
if mouse_x >= 450 && mouse_x <= 750 && mouse_y >= 550 && mouse_y <= 620 && is_mouse_clicked != 0 do
current_screen = 0;
end
end
suic_end_drawing();
end
suic_close_window();
0
end

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>),
}
@ -66,6 +67,7 @@ pub enum ASTNodeKind {
Function(Function),
Extern(Extern),
Load(Load),
Const(Const),
Struct(Struct),
Enum(Enum),
Impl(Impl),
@ -79,6 +81,13 @@ pub struct Use {
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct Const {
pub name: String,
pub typ: Option<TypeAnnot>,
pub value: Expr,
}
// ? implies OPTIONAL here
// \( implies the presence of (. same for /)
@ -204,10 +213,12 @@ pub struct Expr {
#[derive(Debug, Clone)]
pub enum ExprKind {
Int(i64),
TypedInt(i64, String),
Float(f64),
Bool(bool),
String(String),
Array(Vec<Expr>),
FixedArray(Box<Expr>, i64),
Tuple(Vec<Expr>),
StructLit(String, Vec<(String, Expr)>), // Name { a: expr, b: expr }
@ -262,6 +273,8 @@ pub enum BinOp {
Or,
BitwiseAnd,
BitwiseOr,
LShift,
RShift,
Eq,
Neq,
Lt,
@ -311,6 +324,7 @@ pub enum TypedASTNodeKind {
Function(TypedFunction),
Extern(TypedExtern),
Load(TypedLoad),
Const(TypedConst),
Struct(TypedStruct),
Enum(TypedEnum),
Impl(TypedImpl),
@ -344,6 +358,13 @@ pub struct TypedLoad {
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct TypedConst {
pub name: String,
pub typ: Option<TypeAnnot>,
pub value: TypedExpr,
}
#[derive(Debug, Clone)]
pub struct TypedStruct {
pub name: String,
@ -398,10 +419,12 @@ pub struct TypedExpr {
#[derive(Debug, Clone)]
pub enum TypedExprKind {
Int(i64),
TypedInt(i64, String),
Float(f64),
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

@ -2,6 +2,7 @@
pub enum CType {
Void,
Int,
U8,
Float,
Bool,
Char,
@ -9,6 +10,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
}
@ -32,6 +34,7 @@ impl CType {
match self {
CType::Void => "void".to_string(),
CType::Int => "int".to_string(),
CType::U8 => "uint8_t".to_string(),
CType::Float => "float".to_string(),
CType::Bool => "bool".to_string(),
CType::Char => "char".to_string(),
@ -48,6 +51,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(", "))
@ -61,6 +65,7 @@ pub struct CVarDecl {
pub name: String,
pub ty: CType,
pub initializer: Option<CExpr>,
pub is_const: bool,
}
#[derive(Debug, Clone, PartialEq)]
@ -116,6 +121,8 @@ pub enum CBinaryOp {
Or,
BitwiseAnd,
BitwiseOr,
LShift,
RShift,
}
impl CBinaryOp {
@ -136,6 +143,8 @@ impl CBinaryOp {
CBinaryOp::Or => "||",
CBinaryOp::BitwiseAnd => "&",
CBinaryOp::BitwiseOr => "|",
CBinaryOp::LShift => "<<",
CBinaryOp::RShift => ">>",
}
}
}

View file

@ -23,6 +23,7 @@ impl DeclarationTranspiler {
name: field.name.clone(),
ty: field_type,
initializer: None,
is_const: false,
});
}
@ -53,6 +54,7 @@ impl DeclarationTranspiler {
name: name.clone(),
ty: param_type,
initializer: None,
is_const: false,
});
}
@ -81,6 +83,7 @@ impl DeclarationTranspiler {
name: field_name,
ty: c_type,
initializer: None,
is_const: false,
});
}
@ -100,6 +103,7 @@ impl DeclarationTranspiler {
name: field_name,
ty: CType::Struct(struct_name),
initializer: None,
is_const: false,
});
}
@ -115,11 +119,13 @@ impl DeclarationTranspiler {
name: "discriminant".to_string(),
ty: CType::Int,
initializer: None,
is_const: false,
},
CVarDecl {
name: "data".to_string(),
ty: CType::Struct(union_name_clone),
initializer: None,
is_const: false,
},
];
@ -148,6 +154,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() {
@ -156,6 +166,7 @@ impl DeclarationTranspiler {
name: format!("field{}", i),
ty: field_type,
initializer: None,
is_const: false,
});
}
Ok(CType::UnnamedStruct(c_fields))
@ -176,6 +187,7 @@ impl DeclarationTranspiler {
pub fn convert_to_c_type(name: &String) -> Result<CType, String> {
match String::as_str(name) {
"int" => Ok(CType::Int),
"u8" => Ok(CType::U8),
"float" => Ok(CType::Float),
"bool" => Ok(CType::Bool),
"string" => Ok(CType::Ptr(Box::new(CType::Char))),

View file

@ -30,6 +30,7 @@ impl StatementsTranspiler {
name: name.clone(),
ty,
initializer,
is_const: false,
};
(name, decl)
@ -48,6 +49,13 @@ impl StatementsTranspiler {
pub fn transpile_expr(&self, expr: &TypedExpr) -> Result<CExpr, String> {
match &expr.kind {
TypedExprKind::Int(i) => Ok(CExpr::IntLit(*i)),
TypedExprKind::TypedInt(i, t) => {
let inner = CExpr::IntLit(*i);
match t.as_str() {
"u8" => Ok(CExpr::Cast(Box::new(inner), CType::U8)),
_ => Err(format!("Unsupported typed integer type: {}", t)),
}
}
TypedExprKind::Float(f) => Ok(CExpr::FloatLit(*f)),
TypedExprKind::Bool(b) => Ok(CExpr::BoolLit(*b)),
TypedExprKind::String(s) => Ok(CExpr::StringLit(s.clone())),
@ -117,6 +125,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
@ -170,6 +183,7 @@ impl StatementsTranspiler {
name: name.clone(),
ty: c_type,
initializer,
is_const: false,
};
Ok(CStmt::VarDecl(var_decl))
}
@ -221,6 +235,7 @@ impl StatementsTranspiler {
name: var_name.clone(),
ty: CType::Int,
initializer: Some(start_expr),
is_const: false,
};
let cond = CExpr::BinOp(
@ -272,6 +287,7 @@ impl StatementsTranspiler {
)),
Box::new(CExpr::Var(idx_name.clone())),
)),
is_const: false,
});
let mut body_stmts = vec![bind];
@ -321,6 +337,7 @@ impl StatementsTranspiler {
Box::new(CExpr::Var(arr_name.clone())),
Box::new(CExpr::Var(idx_name.clone())),
)),
is_const: false,
});
let mut body_stmts = vec![bind];
@ -456,6 +473,8 @@ impl StatementsTranspiler {
BinOp::Or => Ok(CBinaryOp::Or),
BinOp::BitwiseAnd => Ok(CBinaryOp::BitwiseAnd),
BinOp::BitwiseOr => Ok(CBinaryOp::BitwiseOr),
BinOp::LShift => Ok(CBinaryOp::LShift),
BinOp::RShift => Ok(CBinaryOp::RShift),
}
}
@ -469,9 +488,10 @@ impl StatementsTranspiler {
}
}
fn type_to_ctype(&self, ty: &Type) -> Result<CType, String> {
pub fn type_to_ctype(&self, ty: &Type) -> Result<CType, String> {
match ty {
Type::Int => Ok(CType::Int),
Type::U8 => Ok(CType::U8),
Type::Float => Ok(CType::Float),
Type::Bool => Ok(CType::Bool),
Type::String => Ok(CType::Ptr(Box::new(CType::Char))),
@ -482,6 +502,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) => {
@ -492,6 +516,7 @@ impl StatementsTranspiler {
name: format!("field{}", i),
ty: c_type,
initializer: None,
is_const: false,
});
}
Ok(CType::UnnamedStruct(fields))
@ -536,6 +561,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() {
@ -544,6 +573,7 @@ impl StatementsTranspiler {
name: format!("field{}", i),
ty: field_type,
initializer: None,
is_const: false,
});
}
Ok(CType::UnnamedStruct(c_fields))

View file

@ -90,6 +90,39 @@ impl Transpiler {
self.lower_declarations_to_c_ir(node)?;
}
// Add builtin structs
if !self.structs.iter().any(|s| s.name == "Color") {
self.structs.push(CStructDecl {
name: "Color".to_string(),
fields: vec![
CVarDecl { name: "r".to_string(), ty: CType::U8, initializer: None, is_const: false },
CVarDecl { name: "g".to_string(), ty: CType::U8, initializer: None, is_const: false },
CVarDecl { name: "b".to_string(), ty: CType::U8, initializer: None, is_const: false },
CVarDecl { name: "a".to_string(), ty: CType::U8, initializer: None, is_const: false },
],
});
// Generate TypeInfo
let bitmap = Self::generate_pointer_bitmap(&vec![
CVarDecl { name: "r".to_string(), ty: CType::U8, initializer: None, is_const: false },
CVarDecl { name: "g".to_string(), ty: CType::U8, initializer: None, is_const: false },
CVarDecl { name: "b".to_string(), ty: CType::U8, initializer: None, is_const: false },
CVarDecl { name: "a".to_string(), ty: CType::U8, initializer: None, is_const: false },
]);
self.typeinfo_map.insert("Color".to_string(), bitmap);
}
if !self.structs.iter().any(|s| s.name == "Shader") {
self.structs.push(CStructDecl {
name: "Shader".to_string(),
fields: vec![
CVarDecl { name: "id".to_string(), ty: CType::Int, initializer: None, is_const: false },
],
});
let bitmap = Self::generate_pointer_bitmap(&vec![
CVarDecl { name: "id".to_string(), ty: CType::Int, initializer: None, is_const: false },
]);
self.typeinfo_map.insert("Shader".to_string(), bitmap);
}
self.lower_function_bodies_to_c_ir(nodes)?;
// Rename main to suic_main and track that we have a main
@ -238,6 +271,16 @@ impl Transpiler {
self.functions.push(func_decl);
}
}
TypedASTNodeKind::Const(c) => {
let value_code = self.stmt_transpiler.transpile_expr(&c.value)?;
let var_decl = CVarDecl {
name: c.name.clone(),
ty: self.stmt_transpiler.type_to_ctype(&c.value.ty)?,
initializer: Some(value_code),
is_const: true,
};
self.globals.push(var_decl);
}
TypedASTNodeKind::Extern(e) => {
// For externs, we might need to add function prototypes
// But for now, skip as they're handled differently
@ -404,9 +447,26 @@ 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 var.is_const {
output = format!("const {}", output);
}
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
}
@ -638,13 +698,14 @@ impl Transpiler {
"rl_pop_matrix" => "suic_rl_pop_matrix",
"rl_translate_f" => "suic_rl_translate_f",
"rl_rotate_f" => "suic_rl_rotate_f",
// UI Framework Utils
"draw_text" => "suic_draw_text",
"draw_rectangle" => "suic_draw_rectangle",
"draw_rectangle_lines" => "suic_draw_rectangle_lines",
"measure_text" => "suic_measure_text",
"draw_circle" => "suic_draw_circle",
"draw_line" => "suic_draw_line",
// UI Framework Utils
"draw_text" => "DrawText",
"draw_rectangle" => "DrawRectangle",
"draw_rectangle_lines" => "DrawRectangleLines",
"measure_text" => "MeasureText",
"draw_circle" => "DrawCircle",
"draw_line" => "DrawLine",
"draw_fps" => "DrawFPS",
// Player controller functions
"player_controller_create" => "suic_player_controller_create",
"player_controller_destroy" => "suic_player_controller_destroy",
@ -735,18 +796,7 @@ impl Transpiler {
"Vec3" => "suic_vec3".to_string(),
_ => format!("struct {}", struct_name),
};
// For built-in simple structs, generate compound literal directly
// For user-defined structs, use heap allocation
match struct_name.as_str() {
"Vec2" | "Vec3" => format!("({}){{ {} }}", c_type_name, field_inits.join(", ")),
_ => format!(
"suic_alloc_struct(&sui_typeinfo_{}, sizeof({}), &({}){{ {} }})",
struct_name,
c_type_name,
c_type_name,
field_inits.join(", ")
),
}
format!("({}){{ {} }}", c_type_name, field_inits.join(", "))
}
CExpr::EnumLit(enum_name, variant_name, args) => {
// Find the variant index - for simplicity, assume variants are in order

View file

@ -370,14 +370,22 @@ impl ImportResolver {
ASTNodeKind::Load(_) => {
result.push(node);
}
ASTNodeKind::Trait(t) => {
self.symbol_registry.register(
t.name.clone(),
filename.to_string(),
node.span.clone(),
)?;
result.push(node);
}
ASTNodeKind::Trait(t) => {
self.symbol_registry.register(
t.name.clone(),
filename.to_string(),
node.span.clone(),
)?;
result.push(node);
}
ASTNodeKind::Const(c) => {
self.symbol_registry.register(
c.name.clone(),
filename.to_string(),
node.span.clone(),
)?;
result.push(node);
}
}
}

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);
@ -150,6 +153,7 @@ impl LambdaLowerer {
}
// Terminal expressions don't contain variables
ExprKind::Int(_)
| ExprKind::TypedInt(_, _)
| ExprKind::Float(_)
| ExprKind::Bool(_)
| ExprKind::String(_)
@ -340,6 +344,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()
@ -369,6 +377,7 @@ impl LambdaLowerer {
}
// Terminal expressions that don't contain other expressions
ExprKind::Int(_)
| ExprKind::TypedInt(_, _)
| ExprKind::Float(_)
| ExprKind::Bool(_)
| ExprKind::String(_)

View file

@ -20,6 +20,14 @@ pub enum Token {
}, priority = 4)]
Int(i64),
#[regex(r"(0|[1-9][0-9_]*)u8", |lex| {
let s = lex.slice();
let num_part = &s[..s.len()-2];
let num = num_part.replace("_", "").parse::<i64>().unwrap();
(num, "u8".to_string())
}, priority = 5)]
TypedInt((i64, String)),
#[regex(r"(([0-9][0-9_]*\.[0-9_]+|[0-9]*\.[0-9_]+)([eE][+-]?[0-9_]+)?)", |lex| {
let s = lex.slice().replace("_", "");
s.parse::<f64>().unwrap()
@ -61,6 +69,9 @@ pub enum Token {
#[token("string")]
KeywordString,
#[token("u8")]
KeywordU8,
#[token("let")]
KeywordLet,
@ -135,6 +146,9 @@ pub enum Token {
#[token("trait")]
KeywordTrait,
#[token("const")]
KeywordConst,
// #[token("type")]
// KeywordType,
//
@ -171,6 +185,12 @@ pub enum Token {
#[token("**", priority = 3)]
Power,
#[token("<<")]
LShift,
#[token(">>")]
RShift,
#[token("$")]
Dollar,

View file

@ -244,6 +244,9 @@ fn run_file(filename: &str, debug: bool) -> Result<(), String> {
suicmez::ast::TypedASTNodeKind::Function(f) => {
format!("Function({})", f.name)
}
suicmez::ast::TypedASTNodeKind::Const(c) => {
format!("Const({})", c.name)
}
suicmez::ast::TypedASTNodeKind::Struct(s) => {
format!("Struct({}) with {} params", s.name, s.parameters.len())
}
@ -297,6 +300,9 @@ fn run_file(filename: &str, debug: bool) -> Result<(), String> {
suicmez::ast::TypedASTNodeKind::Function(f) => {
format!("Function({})", f.name)
}
suicmez::ast::TypedASTNodeKind::Const(c) => {
format!("Const({})", c.name)
}
suicmez::ast::TypedASTNodeKind::Struct(s) => {
format!("Struct({}) with {} params", s.name, s.parameters.len())
}

View file

@ -104,6 +104,9 @@ impl Monomorphizer {
for (_idx, node) in nodes.iter().enumerate() {
match &node.kind {
TypedASTNodeKind::Const(c) => {
result_nodes.push(node.clone());
}
TypedASTNodeKind::Function(f) => {
// Skip generic functions - they'll be added as specialized versions when needed
if !f.parameters.is_empty() {
@ -322,6 +325,7 @@ impl Monomorphizer {
let mut needs = Vec::new();
let new_kind = match &expr.kind {
TypedExprKind::Int(_)
| TypedExprKind::TypedInt(_, _)
| TypedExprKind::Float(_)
| TypedExprKind::Bool(_)
| TypedExprKind::String(_)
@ -342,6 +346,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 +776,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)))
@ -777,11 +790,13 @@ impl Monomorphizer {
fn type_to_type_annot(&self, ty: &Type) -> TypeAnnot {
match ty {
Type::Int => TypeAnnot::Var("int".to_string()),
Type::U8 => TypeAnnot::Var("u8".to_string()),
Type::Float => TypeAnnot::Var("float".to_string()),
Type::Bool => TypeAnnot::Var("bool".to_string()),
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 +916,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 +950,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))
@ -975,6 +991,9 @@ fn check_node_for_typevars(node: &TypedASTNode) -> Result<(), MonomorphizationEr
TypedASTNodeKind::Function(f) => {
check_function_for_typevars(f)?;
}
TypedASTNodeKind::Const(c) => {
// Consts should not have typevars
}
TypedASTNodeKind::Struct(s) => {
check_struct_for_typevars(s)?;
}
@ -1216,6 +1235,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

@ -149,6 +149,16 @@ impl Parser {
attributes,
})
}
Some(Token::KeywordConst) => {
self.next();
let const_def = self.parse_const()?;
let end = self.peek_span().unwrap_or(start..start).end;
Ok(ASTNode {
kind: ASTNodeKind::Const(const_def),
span: Span::new(&(start..end), self.file.clone()),
attributes,
})
}
Some(Token::KeywordStruct) => {
self.next();
let struct_def = self.parse_struct()?;
@ -211,12 +221,33 @@ impl Parser {
}
Some(token) => {
let span = self.peek_span().unwrap_or(start..start);
self.error(format!("Unexpected token at top level: {:?}. Expected declarations like 'fn', 'struct', 'enum', 'impl', 'trait', 'use', 'load', or 'extern'", token), span)
self.error(format!("Unexpected token at top level: {:?}. Expected declarations like 'fn', 'struct', 'enum', 'impl', 'trait', 'const', 'use', 'load', or 'extern'", token), span)
}
None => self.error("Unexpected end of file at top level. Expected declarations like 'fn', 'struct', 'enum', etc.".to_string(), start..start),
}
}
fn parse_const(&mut self) -> Result<Const, ParseError> {
let start = self.peek_span().unwrap_or(0..0).start;
let name = match self.next() {
Some((Token::Variable(n), _)) => n,
Some((_, span)) => return self.error("Expected constant name after 'const' keyword. Example: const PI = 3.14".to_string(), span),
None => return self.error("Expected constant name after 'const' keyword. Example: const PI = 3.14".to_string(), start..start),
};
let typ = if matches!(self.peek(), Some(Token::Colon)) {
self.next();
Some(self.parse_type_annot()?)
} else {
None
};
self.expect(Token::Assign)?;
let value = self.parse_expr()?;
Ok(Const { name, typ, value })
}
fn parse_attribute(&mut self) -> Result<Attribute, ParseError> {
self.expect(Token::At)?;
let start = self.peek_span().unwrap_or(0..0).start;
@ -883,6 +914,7 @@ impl Parser {
Some((Token::KeywordInt, _)) => TypeAnnot::Cons("int".to_string(), vec![]),
Some((Token::KeywordFloat, _)) => TypeAnnot::Cons("float".to_string(), vec![]),
Some((Token::KeywordString, _)) => TypeAnnot::Cons("string".to_string(), vec![]),
Some((Token::KeywordU8, _)) => TypeAnnot::Cons("u8".to_string(), vec![]),
Some((Token::LParen, _)) => {
// Check for unit type: ()
if matches!(self.peek(), Some(Token::RParen)) {
@ -912,8 +944,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)) => {
@ -1157,14 +1204,44 @@ impl Parser {
Ok(left)
}
fn parse_bitwise_and_expr(&mut self) -> Result<Expr, ParseError> {
fn parse_shift_expr(&mut self) -> Result<Expr, ParseError> {
let mut left = self.parse_eq_expr()?;
loop {
let op = if matches!(self.peek(), Some(Token::LShift)) {
Some(BinOp::LShift)
} else if matches!(self.peek(), Some(Token::RShift)) {
Some(BinOp::RShift)
} else {
None
};
if let Some(op) = op {
let start = left.span.start;
self.next();
let right = self.parse_eq_expr()?;
let end = right.span.end;
left = Expr {
kind: ExprKind::BinOp(Box::new(left), op, Box::new(right)),
span: Span::new(&(start..end), self.file.clone()),
attributes: Vec::new(),
};
} else {
break;
}
}
Ok(left)
}
fn parse_bitwise_and_expr(&mut self) -> Result<Expr, ParseError> {
let mut left = self.parse_shift_expr()?;
loop {
if matches!(self.peek(), Some(Token::BitAnd)) {
let start = left.span.start;
self.next();
let right = self.parse_eq_expr()?;
let right = self.parse_shift_expr()?;
let end = right.span.end;
left = Expr {
kind: ExprKind::BinOp(Box::new(left), BinOp::BitwiseAnd, Box::new(right)),
@ -1450,6 +1527,15 @@ impl Parser {
attributes: Vec::new(),
})
}
Some(Token::TypedInt((n, t))) => {
self.next();
let end = self.peek_span().unwrap_or(start..start).end;
Ok(Expr {
kind: ExprKind::TypedInt(n, t),
span: Span::new(&(start..end), self.file.clone()),
attributes: Vec::new(),
})
}
Some(Token::Float(f)) => {
self.next();
let end = self.peek_span().unwrap_or(start..start).end;
@ -1607,25 +1693,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

@ -6,12 +6,14 @@ use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum Type {
Int,
U8,
Float,
Bool,
String,
Unit,
Never,
Array(Box<Type>),
FixedArray(Box<Type>, usize),
Ptr(Box<Type>),
Tuple(Vec<Type>),
Function(Vec<Type>, Box<Type>),
@ -26,12 +28,14 @@ impl Type {
pub fn to_string(&self) -> String {
match self {
Type::Int => "int".to_string(),
Type::U8 => "u8".to_string(),
Type::Float => "float".to_string(),
Type::Bool => "bool".to_string(),
Type::String => "string".to_string(),
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();
@ -366,6 +370,351 @@ impl TypeChecker {
}
fn add_builtin_functions(&mut self) {
// Builtin types
self.env.types.insert(
"Vector3".to_string(),
TypeInfo {
kind: TypeInfoKind::Struct(vec![
("x".to_string(), TypeAnnot::Var("float".to_string())),
("y".to_string(), TypeAnnot::Var("float".to_string())),
("z".to_string(), TypeAnnot::Var("float".to_string())),
]),
parameters: vec![],
},
);
self.env.types.insert(
"Color".to_string(),
TypeInfo {
kind: TypeInfoKind::Struct(vec![
("r".to_string(), TypeAnnot::Var("u8".to_string())),
("g".to_string(), TypeAnnot::Var("u8".to_string())),
("b".to_string(), TypeAnnot::Var("u8".to_string())),
("a".to_string(), TypeAnnot::Var("u8".to_string())),
]),
parameters: vec![],
},
);
self.env.types.insert(
"Shader".to_string(),
TypeInfo {
kind: TypeInfoKind::Struct(vec![
("id".to_string(), TypeAnnot::Var("int".to_string())),
]),
parameters: vec![],
},
);
self.env.types.insert(
"Vector2".to_string(),
TypeInfo {
kind: TypeInfoKind::Struct(vec![
("x".to_string(), TypeAnnot::Var("float".to_string())),
("y".to_string(), TypeAnnot::Var("float".to_string())),
]),
parameters: vec![],
},
);
self.env.types.insert(
"sockaddr_in".to_string(),
TypeInfo {
kind: TypeInfoKind::Struct(vec![
("sin_family".to_string(), TypeAnnot::Var("u16".to_string())),
("sin_port".to_string(), TypeAnnot::Var("u16".to_string())),
("sin_addr".to_string(), TypeAnnot::Cons("in_addr".to_string(), vec![])),
("sin_zero".to_string(), TypeAnnot::FixedArray(Box::new(TypeAnnot::Var("u8".to_string())), 8)),
]),
parameters: vec![],
},
);
// Builtin functions
// Raylib
self.env.functions.insert(
"init_window".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int, Type::Int, Type::String],
return_type: Type::Unit,
},
);
self.env.functions.insert(
"close_window".to_string(),
FunctionType {
type_params: vec![],
params: vec![],
return_type: Type::Unit,
},
);
self.env.functions.insert(
"window_should_close".to_string(),
FunctionType {
type_params: vec![],
params: vec![],
return_type: Type::Bool,
},
);
self.env.functions.insert(
"set_target_fps".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int],
return_type: Type::Unit,
},
);
self.env.functions.insert(
"disable_cursor".to_string(),
FunctionType {
type_params: vec![],
params: vec![],
return_type: Type::Unit,
},
);
self.env.functions.insert(
"get_char_pressed".to_string(),
FunctionType {
type_params: vec![],
params: vec![],
return_type: Type::Int,
},
);
self.env.functions.insert(
"is_key_pressed".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int],
return_type: Type::Bool,
},
);
self.env.functions.insert(
"is_key_down".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int],
return_type: Type::Bool,
},
);
self.env.functions.insert(
"get_mouse_delta".to_string(),
FunctionType {
type_params: vec![],
params: vec![],
return_type: Type::Struct("Vector2".to_string(), vec![]),
},
);
self.env.functions.insert(
"is_mouse_button_down".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int],
return_type: Type::Bool,
},
);
self.env.functions.insert(
"get_random_value".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int, Type::Int],
return_type: Type::Int,
},
);
self.env.functions.insert(
"get_frame_time".to_string(),
FunctionType {
type_params: vec![],
params: vec![],
return_type: Type::Float,
},
);
self.env.functions.insert(
"begin_drawing".to_string(),
FunctionType {
type_params: vec![],
params: vec![],
return_type: Type::Unit,
},
);
self.env.functions.insert(
"end_drawing".to_string(),
FunctionType {
type_params: vec![],
params: vec![],
return_type: Type::Unit,
},
);
self.env.functions.insert(
"clear_background".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int, Type::Int, Type::Int, Type::Int],
return_type: Type::Unit,
},
);
self.env.functions.insert(
"draw_text".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::String, Type::Int, Type::Int, Type::Int, Type::Int, Type::Int, Type::Int, Type::Int],
return_type: Type::Unit,
},
);
self.env.functions.insert(
"draw_fps".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int, Type::Int],
return_type: Type::Unit,
},
);
// Math functions
self.env.functions.insert(
"sinf".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Float],
return_type: Type::Float,
},
);
self.env.functions.insert(
"cosf".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Float],
return_type: Type::Float,
},
);
self.env.functions.insert(
"sqrtf".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Float],
return_type: Type::Float,
},
);
self.env.functions.insert(
"fmaxf".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Float, Type::Float],
return_type: Type::Float,
},
);
self.env.functions.insert(
"floorf".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Float],
return_type: Type::Float,
},
);
self.env.functions.insert(
"powf".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Float, Type::Float],
return_type: Type::Float,
},
);
self.env.functions.insert(
"expf".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Float],
return_type: Type::Float,
},
);
// Noise functions
self.env.functions.insert(
"grad2".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int, Type::Float, Type::Float],
return_type: Type::Float,
},
);
self.env.functions.insert(
"simplex_noise_2d".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Float, Type::Float],
return_type: Type::Float,
},
);
self.env.functions.insert(
"fbm_noise".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Float, Type::Float, Type::Int],
return_type: Type::Float,
},
);
self.env.functions.insert(
"get_terrain_height".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Float, Type::Float],
return_type: Type::Float,
},
);
// Socket functions
self.env.functions.insert(
"socket".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int, Type::Int, Type::Int],
return_type: Type::Int,
},
);
self.env.functions.insert(
"sendto".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int, Type::Ptr(Box::new(Type::U8)), Type::Int, Type::Int, Type::Ptr(Box::new(Type::Unit)), Type::Int],
return_type: Type::Int,
},
);
self.env.functions.insert(
"recvfrom".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int, Type::Ptr(Box::new(Type::U8)), Type::Int, Type::Int, Type::Ptr(Box::new(Type::Unit)), Type::Ptr(Box::new(Type::Int))],
return_type: Type::Int,
},
);
self.env.functions.insert(
"close".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int],
return_type: Type::Int,
},
);
self.env.functions.insert(
"htonl".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int],
return_type: Type::Int,
},
);
self.env.functions.insert(
"htons".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int],
return_type: Type::Int,
},
);
self.env.functions.insert(
"inet_pton".to_string(),
FunctionType {
type_params: vec![],
params: vec![Type::Int, Type::String, Type::Ptr(Box::new(Type::Unit))],
return_type: Type::Int,
},
);
// ODE Physics Engine functions
self.env.functions.insert(
"ode_init".to_string(),
@ -1560,6 +1909,19 @@ impl TypeChecker {
fn typecheck_node(&mut self, node: &ASTNode) -> Result<TypedASTNode, TypeError> {
let ty = match &node.kind {
ASTNodeKind::Const(c) => {
let typed_value = self.typecheck_expr(&c.value)?;
return Ok(TypedASTNode {
kind: TypedASTNodeKind::Const(TypedConst {
name: c.name.clone(),
typ: c.typ.clone(),
value: typed_value.clone(),
}),
span: node.span.clone(),
attributes: node.attributes.clone(),
ty: typed_value.ty,
});
}
ASTNodeKind::Function(f) => {
let typed_func = self.typecheck_function(f)?;
let ty = typed_func.ty.clone();
@ -1761,6 +2123,16 @@ impl TypeChecker {
fn typecheck_expr(&mut self, expr: &Expr) -> Result<TypedExpr, TypeError> {
let (kind, ty) = match &expr.kind {
ExprKind::Int(n) => (TypedExprKind::Int(*n), Type::Int),
ExprKind::TypedInt(n, t) => {
let ty = match t.as_str() {
"u8" => Type::U8,
_ => return Err(TypeError {
kind: TypeErrorKind::UndefinedType(t.clone()),
span: expr.span.clone(),
}),
};
(TypedExprKind::TypedInt(*n, t.clone()), ty)
},
ExprKind::Float(f) => (TypedExprKind::Float(*f), Type::Float),
ExprKind::Bool(b) => (TypedExprKind::Bool(*b), Type::Bool),
ExprKind::String(s) => (TypedExprKind::String(s.clone()), Type::String),
@ -1814,6 +2186,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();
@ -1840,7 +2220,7 @@ impl TypeChecker {
let typed_right = self.typecheck_expr(right)?;
let result_type = match op {
BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div | BinOp::Mod | BinOp::BitwiseAnd | BinOp::BitwiseOr => {
BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div | BinOp::Mod | BinOp::BitwiseAnd | BinOp::BitwiseOr | BinOp::LShift | BinOp::RShift => {
if !self.types_compatible(&typed_left.ty, &typed_right.ty) {
return Err(TypeError {
kind: TypeErrorKind::TypeMismatch(
@ -2171,7 +2551,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()),
@ -2836,13 +3216,14 @@ impl TypeChecker {
TypeInfoKind::Enum(_) => Type::Enum(name.clone(), substituted_args),
}
} else {
match name.as_str() {
"int" => Type::Int,
"float" => Type::Float,
"bool" => Type::Bool,
"string" => Type::String,
"unit" => Type::Unit,
"never" => Type::Never,
match name.as_str() {
"int" => Type::Int,
"u8" => Type::U8,
"float" => Type::Float,
"bool" => Type::Bool,
"string" => Type::String,
"unit" => Type::Unit,
"never" => Type::Never,
_ => Type::Generic(name.clone(), substituted_args),
}
}
@ -2850,6 +3231,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
@ -2893,6 +3277,7 @@ impl TypeChecker {
match name.as_str() {
"int" => Type::Int,
"u8" => Type::U8,
"float" => Type::Float,
"bool" => Type::Bool,
"string" => Type::String,
@ -2923,6 +3308,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))),
}
}
@ -2931,12 +3317,14 @@ impl TypeChecker {
match (t1, t2) {
(Type::Unknown, _) | (_, Type::Unknown) => true,
(Type::Int, Type::Int) => true,
(Type::U8, Type::U8) => true,
(Type::Float, Type::Float) => true,
(Type::Bool, Type::Bool) => true,
(Type::String, Type::String) => true,
(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