From a62432bfbbebd2635e6d321d2740ac3d2c60c3d5 Mon Sep 17 00:00:00 2001 From: Masashi Date: Mon, 15 Dec 2025 21:42:52 +0530 Subject: [PATCH 1/2] better typechecker errors --- src/main.rs | 55 ++++++++++++++++++++++++++++++++++++++--- src/typechecker.rs | 56 ++++++++++++++++++++++++++++++++++++++++++ tests/control_flow.sui | 2 +- 3 files changed, 108 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 3dc3ed4..38918dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -95,6 +95,56 @@ fn format_parse_error(source: &str, error: &suicmez::parser::ParseError) -> Stri format!("Parse error: {} (at byte {})", error.message, error.span.start) } +fn format_type_error(source: &str, error: &suicmez::typechecker::TypeError) -> String { + // Find the line containing the error + let lines: Vec<&str> = source.lines().collect(); + let mut current_pos = 0; + + for (line_idx, line) in lines.iter().enumerate() { + let line_start = current_pos; + let line_end = current_pos + line.len(); + + // Check if the error span intersects with this line + if error.span.start < line_end && error.span.end > line_start { + let mut result = String::new(); + + // Print the error message + result.push_str(&format!("Type error: {}\n", error.kind)); + + // Print the line number and content + result.push_str(&format!("{} | {}\n", line_idx + 1, line)); + + // Calculate column positions within the line + let line_start_col = error.span.start.saturating_sub(line_start); + let line_end_col = (error.span.end - line_start).min(line.len()); + + // Print spaces and squiggly line for the span + result.push_str(&format!("{} | ", " ".repeat((line_idx + 1).to_string().len()))); + for _ in 0..line_start_col { + result.push(' '); + } + for _ in line_start_col..line_end_col { + result.push('~'); + } + result.push('\n'); + + // Print caret at the start position + result.push_str(&format!("{} | ", " ".repeat((line_idx + 1).to_string().len()))); + for _ in 0..line_start_col { + result.push(' '); + } + result.push('^'); + + return result; + } + + current_pos = line_end + 1; // +1 for the newline character + } + + // Fallback if we can't find the line + format!("Type error: {} (at byte {})", error.kind, error.span.start) +} + fn run_file(filename: &str) -> Result<(), String> { // Read the source file let source = fs::read_to_string(filename) @@ -134,10 +184,7 @@ fn run_file(filename: &str) -> Result<(), String> { // Typecheck the AST let mut typechecker = TypeChecker::new(); let typed_nodes = typechecker.typecheck_program(&lowered_nodes).map_err(|e| { - format!( - "Type error at {}:{}: {:?}", - e.span.file, e.span.start, e.kind - ) + format_type_error(&source, &e) })?; println!( diff --git a/src/typechecker.rs b/src/typechecker.rs index 5c594ef..4ed2bbb 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -1,6 +1,7 @@ // src/typechecker.rs use crate::ast::*; use std::collections::HashMap; +use std::fmt; #[derive(Debug, Clone, PartialEq)] pub enum Type { @@ -86,6 +87,61 @@ pub enum TypeErrorKind { Other(String), } +impl fmt::Display for TypeErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TypeErrorKind::TypeMismatch(expected, actual) => { + write!(f, "Type mismatch: expected {}, found {}", expected.to_string(), actual.to_string()) + } + TypeErrorKind::UndefinedVariable(name) => { + write!(f, "Undefined variable '{}'", name) + } + TypeErrorKind::UndefinedType(name) => { + write!(f, "Undefined type '{}'", name) + } + TypeErrorKind::UndefinedFunction(name) => { + write!(f, "Undefined function '{}'", name) + } + TypeErrorKind::UndefinedField(field, ty) => { + write!(f, "Undefined field '{}' on type {}", field, ty.to_string()) + } + TypeErrorKind::UndefinedVariant(enum_name, variant) => { + write!(f, "Undefined variant '{}' in enum '{}'", variant, enum_name) + } + TypeErrorKind::ArityMismatch(expected, actual) => { + write!(f, "Function expects {} arguments, but {} were provided", expected, actual) + } + TypeErrorKind::NotAFunction(ty) => { + write!(f, "Expected a function, but found {}", ty.to_string()) + } + TypeErrorKind::NotAnArray(ty) => { + write!(f, "Expected an array, but found {}", ty.to_string()) + } + TypeErrorKind::NotAStruct(ty) => { + write!(f, "Expected a struct, but found {}", ty.to_string()) + } + TypeErrorKind::NotAnEnum(ty) => { + write!(f, "Expected an enum, but found {}", ty.to_string()) + } + TypeErrorKind::InvalidCast(from, to) => { + write!(f, "Invalid cast from {} to {}", from.to_string(), to.to_string()) + } + TypeErrorKind::InvalidPattern(msg) => { + write!(f, "Invalid pattern: {}", msg) + } + TypeErrorKind::MutableityError(msg) => { + write!(f, "Mutability error: {}", msg) + } + TypeErrorKind::LinearityError(msg) => { + write!(f, "Linearity error: {}", msg) + } + TypeErrorKind::Other(msg) => { + write!(f, "{}", msg) + } + } + } +} + #[derive(Clone)] struct VarInfo { ty: Type, diff --git a/tests/control_flow.sui b/tests/control_flow.sui index af58199..391b8cd 100644 --- a/tests/control_flow.sui +++ b/tests/control_flow.sui @@ -5,7 +5,7 @@ fn main() -> int do else 0 - let i = 0; + let mut i = 0; while i < 5 i = i + 1; i From c9a749300615ee63b998c5c9f15769bcd042ce05 Mon Sep 17 00:00:00 2001 From: Masashi Date: Tue, 16 Dec 2025 01:49:49 +0530 Subject: [PATCH 2/2] codegen basics --- simple_test.c | 18 ++ src/c_ir.rs | 157 ++++++++++++ src/codegen/declaration_transpiler.rs | 185 ++++++++++++++ src/codegen/mod.rs | 3 + src/codegen/statements_transpiler.rs | 340 ++++++++++++++++++++++++++ src/codegen/transpiler.rs | 282 +++++++++++++++++++++ src/lambda_lower.rs | 43 ++-- src/lib.rs | 8 +- src/main.rs | 83 +++++-- src/typechecker.rs | 20 +- tests/basic_types.c | 16 ++ tests/control_flow.c | 21 ++ tests/structs.c | 19 ++ tests/structs.sui | 4 +- tests/traits.sui | 4 +- 15 files changed, 1152 insertions(+), 51 deletions(-) create mode 100644 simple_test.c create mode 100644 src/codegen/mod.rs create mode 100644 tests/basic_types.c create mode 100644 tests/control_flow.c create mode 100644 tests/structs.c diff --git a/simple_test.c b/simple_test.c new file mode 100644 index 0000000..9f52f8f --- /dev/null +++ b/simple_test.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +int simple_add(int x, int y); +int main(void); + + +int simple_add(int x, int y) { + (x + y); +} + +int main(void) { + int result = simple_add(5, 3); + return result; +} + diff --git a/src/c_ir.rs b/src/c_ir.rs index e69de29..dbe85d4 100644 --- a/src/c_ir.rs +++ b/src/c_ir.rs @@ -0,0 +1,157 @@ +#[derive(Debug, Clone)] +pub enum CType { + Void, + Int, + Float, + Bool, + Char, + Ptr(Box), + Struct(String), + UnnamedStruct(Vec), + Array(Box, usize), // type and size + Func(Vec, Box), // args and return +} + +impl CType { + pub fn to_string(&self) -> String { + match self { + CType::Void => "void".to_string(), + CType::Int => "int".to_string(), + CType::Float => "float".to_string(), + CType::Bool => "bool".to_string(), + CType::Char => "char".to_string(), + CType::Ptr(inner) => format!("{}*", inner.to_string()), + CType::Struct(name) => format!("struct {}", name), + CType::UnnamedStruct(fields) => { + let field_strs: Vec = fields + .iter() + .map(|f| format!(" {} {};", f.ty.to_string(), f.name)) + .collect(); + format!("struct {{\n{}\n}}", field_strs.join("\n")) + } + CType::Array(inner, size) => format!("{}[{}]", inner.to_string(), size), + CType::Func(args, ret) => { + let arg_strs: Vec = args.iter().map(|t| t.to_string()).collect(); + format!("{} (*)({})", ret.to_string(), arg_strs.join(", ")) + } + } + } +} + +#[derive(Debug, Clone)] +pub struct CVarDecl { + pub name: String, + pub ty: CType, + pub initializer: Option, +} + +#[derive(Debug, Clone)] +pub struct CStructDecl { + pub name: String, + pub fields: Vec, +} + +#[derive(Debug, Clone)] +pub struct CFuncDecl { + pub name: String, + pub return_type: CType, + pub params: Vec, + pub body: Option>, +} + +#[derive(Debug, Clone)] +pub enum CExpr { + IntLit(i64), + FloatLit(f64), + BoolLit(bool), + StringLit(String), + Var(String), + Call(String, Vec), + BinOp(Box, CBinaryOp, Box), + UnOp(CUnaryOp, Box), + Cast(Box, CType), + StructLit(String, Vec<(String, CExpr)>), + EnumLit(String, String, Vec), // enum_name, variant_name, args + ArrayLit(Vec), + Index(Box, Box), + Dot(Box, String), + AddrOf(Box), + Deref(Box), +} + +#[derive(Debug, Clone)] +pub enum CBinaryOp { + Add, + Sub, + Mul, + Div, + Mod, + Eq, + Neq, + Lt, + Gt, + Leq, + Geq, + And, + Or, +} + +impl CBinaryOp { + pub fn to_string(&self) -> &'static str { + match self { + CBinaryOp::Add => "+", + CBinaryOp::Sub => "-", + CBinaryOp::Mul => "*", + CBinaryOp::Div => "/", + CBinaryOp::Mod => "%", + CBinaryOp::Eq => "==", + CBinaryOp::Neq => "!=", + CBinaryOp::Lt => "<", + CBinaryOp::Gt => ">", + CBinaryOp::Leq => "<=", + CBinaryOp::Geq => ">=", + CBinaryOp::And => "&&", + CBinaryOp::Or => "||", + } + } +} + +#[derive(Debug, Clone)] +pub enum CUnaryOp { + Neg, + Not, + Ref, + Deref, +} + +impl CUnaryOp { + pub fn to_string(&self) -> &'static str { + match self { + CUnaryOp::Neg => "-", + CUnaryOp::Not => "!", + CUnaryOp::Ref => "&", + CUnaryOp::Deref => "*", + } + } +} + +#[derive(Debug, Clone)] +pub enum CStmt { + VarDecl(CVarDecl), + Expr(CExpr), + Assign(CExpr, CExpr), + If(CExpr, Vec, Option>), + While(CExpr, Vec), + For(CVarDecl, CExpr, CExpr, Vec), // init, cond, incr, body + Return(Option), + Break, + Continue, + Block(Vec), +} + +#[derive(Debug, Clone)] +pub enum CToplevel { + StructDecl(CStructDecl), + FuncDecl(CFuncDecl), + VarDecl(CVarDecl), +} diff --git a/src/codegen/declaration_transpiler.rs b/src/codegen/declaration_transpiler.rs index e69de29..3f82ccf 100644 --- a/src/codegen/declaration_transpiler.rs +++ b/src/codegen/declaration_transpiler.rs @@ -0,0 +1,185 @@ +use crate::ast::*; +use crate::c_ir::*; +use crate::typechecker::Type; +use std::collections::HashMap; + +pub struct DeclarationTranspiler { + type_map: HashMap, +} + +impl DeclarationTranspiler { + pub fn new() -> Self { + DeclarationTranspiler { + type_map: HashMap::new(), + } + } + + pub fn transpile_struct(&self, struct_: &TypedStruct) -> Result { + let mut fields = Vec::new(); + + for field in &struct_.fields { + let field_type = self.type_annot_to_ctype(&Some(field.field_type.clone()))?; + fields.push(CVarDecl { + name: field.name.clone(), + ty: field_type, + initializer: None, + }); + } + + Ok(CStructDecl { + name: struct_.name.clone(), + fields, + }) + } + + pub fn transpile_function(&self, func: &TypedFunction) -> Result { + let return_type = match &func.return_type { + Some(type_annot) => self.type_annot_to_ctype(&Some(type_annot.clone()))?, + None => CType::Void, + }; + + let mut params = Vec::new(); + for (_binding_id, name, type_annot) in &func.args { + let param_type = match type_annot { + Some(annot) => self.type_annot_to_ctype(&Some(annot.clone()))?, + None => { + return Err(format!( + "Function parameter {} missing type annotation", + name + )); + } + }; + params.push(CVarDecl { + name: name.clone(), + ty: param_type, + initializer: None, + }); + } + + // Note: body will be transpiled separately by statements transpiler + Ok(CFuncDecl { + name: func.name.clone(), + return_type, + params, + body: None, + }) + } + + pub fn transpile_enum(&self, enum_: &TypedEnum) -> Result, String> { + let mut structs = Vec::new(); + + // For each variant, create a struct + for (i, variant) in enum_.variants.iter().enumerate() { + let struct_name = format!("{}_{}", enum_.name, variant.name); + let mut fields = Vec::new(); + + // Add variant fields (no discriminant in variant struct) + for (j, field_type) in variant.fields.iter().enumerate() { + let field_name = format!("field_{}", j); + let c_type = self.type_annot_to_ctype(&Some(field_type.clone()))?; + fields.push(CVarDecl { + name: field_name, + ty: c_type, + initializer: None, + }); + } + + structs.push(CStructDecl { + name: struct_name, + fields, + }); + } + + // Create union of all variants + let union_name = format!("{}_union", enum_.name); + let mut union_fields = Vec::new(); + for variant in &enum_.variants { + let field_name = variant.name.to_lowercase(); + let struct_name = format!("{}_{}", enum_.name, variant.name); + union_fields.push(CVarDecl { + name: field_name, + ty: CType::Struct(struct_name), + initializer: None, + }); + } + + let union_name_clone = union_name.clone(); + structs.push(CStructDecl { + name: union_name, + fields: union_fields, + }); + + // Create main enum struct + let enum_fields = vec![ + CVarDecl { + name: "discriminant".to_string(), + ty: CType::Int, + initializer: None, + }, + CVarDecl { + name: "data".to_string(), + ty: CType::Struct(union_name_clone), + initializer: None, + }, + ]; + + structs.push(CStructDecl { + name: enum_.name.clone(), + fields: enum_fields, + }); + + Ok(structs) + } + + fn type_annot_to_ctype(&self, annot: &Option) -> Result { + match annot { + Some(TypeAnnot::Var(name)) => match name.as_str() { + "int" => Ok(CType::Int), + "float" => Ok(CType::Float), + "bool" => Ok(CType::Bool), + "string" => Ok(CType::Ptr(Box::new(CType::Char))), + _ => Ok(CType::Struct(name.clone())), // Assume struct + }, + Some(TypeAnnot::Cons(name, args)) if args.is_empty() => match name.as_str() { + "int" => Ok(CType::Int), + "float" => Ok(CType::Float), + "bool" => Ok(CType::Bool), + "string" => Ok(CType::Ptr(Box::new(CType::Char))), + _ => Ok(CType::Struct(name.clone())), // Assume struct + }, + Some(TypeAnnot::Cons(name, _args)) => { + // Generic types - for now just use the base name + Ok(CType::Struct(name.clone())) + } + Some(TypeAnnot::Ptr(inner)) => { + let inner_type = self.type_annot_to_ctype(&Some(*inner.clone()))?; + Ok(CType::Ptr(Box::new(inner_type))) + } + Some(TypeAnnot::Array(inner)) => { + let inner_type = self.type_annot_to_ctype(&Some(*inner.clone()))?; + Ok(CType::Ptr(Box::new(inner_type))) + } + Some(TypeAnnot::Tuple(fields)) => { + let mut c_fields = Vec::new(); + for (i, field_annot) in fields.iter().enumerate() { + let field_type = self.type_annot_to_ctype(&Some(field_annot.clone()))?; + c_fields.push(CVarDecl { + name: format!("field{}", i), + ty: field_type, + initializer: None, + }); + } + Ok(CType::UnnamedStruct(c_fields)) + } + Some(TypeAnnot::Function(args, ret)) => { + let mut c_args = Vec::new(); + for arg in args { + c_args.push(self.type_annot_to_ctype(&Some(arg.clone()))?); + } + let c_ret = self.type_annot_to_ctype(&Some(*ret.clone()))?; + Ok(CType::Func(c_args, Box::new(c_ret))) + } + _ => Ok(CType::Void), // Default + } + } +} diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs new file mode 100644 index 0000000..7014f92 --- /dev/null +++ b/src/codegen/mod.rs @@ -0,0 +1,3 @@ +pub mod declaration_transpiler; +pub mod statements_transpiler; +pub mod transpiler; diff --git a/src/codegen/statements_transpiler.rs b/src/codegen/statements_transpiler.rs index e69de29..bdd38de 100644 --- a/src/codegen/statements_transpiler.rs +++ b/src/codegen/statements_transpiler.rs @@ -0,0 +1,340 @@ +use crate::ast::*; +use crate::c_ir::*; + +pub struct StatementsTranspiler; + +impl StatementsTranspiler { + pub fn new() -> Self { + StatementsTranspiler + } + + pub fn transpile_expr(&self, expr: &TypedExpr) -> Result { + match &expr.kind { + TypedExprKind::Int(i) => Ok(CExpr::IntLit(*i)), + TypedExprKind::Float(f) => Ok(CExpr::FloatLit(*f)), + TypedExprKind::Bool(b) => Ok(CExpr::BoolLit(*b)), + TypedExprKind::String(s) => Ok(CExpr::StringLit(s.clone())), + TypedExprKind::Variable(name) => Ok(CExpr::Var(name.clone())), + TypedExprKind::Call(func, args) => { + let func_expr = self.transpile_expr(func)?; + let func_name = match func_expr { + CExpr::Var(name) => name, + _ => return Err("Function calls must be on variables for now".to_string()), + }; + let c_args = args + .iter() + .map(|arg| self.transpile_expr(arg)) + .collect::, _>>()?; + Ok(CExpr::Call(func_name, c_args)) + } + TypedExprKind::BinOp(lhs, op, rhs) => { + let c_lhs = self.transpile_expr(lhs)?; + let c_rhs = self.transpile_expr(rhs)?; + let c_op = self.binop_to_c_binop(op)?; + Ok(CExpr::BinOp(Box::new(c_lhs), c_op, Box::new(c_rhs))) + } + TypedExprKind::UnOp(op, expr) => { + let c_expr = self.transpile_expr(expr)?; + let c_op = self.unop_to_c_unop(op)?; + Ok(CExpr::UnOp(c_op, Box::new(c_expr))) + } + TypedExprKind::Index(array, index) => { + let c_array = self.transpile_expr(array)?; + let c_index = self.transpile_expr(index)?; + Ok(CExpr::Index(Box::new(c_array), Box::new(c_index))) + } + TypedExprKind::Dot(obj, field) => { + let c_obj = self.transpile_expr(obj)?; + Ok(CExpr::Dot(Box::new(c_obj), field.clone())) + } + TypedExprKind::StructLit(struct_name, fields) => { + let c_fields = fields + .iter() + .map(|(name, expr)| { + let c_expr = self.transpile_expr(expr)?; + Ok::<(String, CExpr), String>((name.clone(), c_expr)) + }) + .collect::, _>>()?; + Ok(CExpr::StructLit(struct_name.clone(), c_fields)) + } + TypedExprKind::Array(array_exprs) => { + let c_exprs = array_exprs + .iter() + .map(|expr| self.transpile_expr(expr)) + .collect::, _>>()?; + 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 + let c_type = self.type_annot_to_ctype(type_annot)?; + Ok(CExpr::Cast(Box::new(c_expr), c_type)) + } + TypedExprKind::Tuple(_) => { + // Simplified: treat as void for now + Ok(CExpr::IntLit(0)) + } + TypedExprKind::EnumLit(enum_name, variant_name, args) => { + let c_args = args + .iter() + .map(|arg| self.transpile_expr(arg)) + .collect::, _>>()?; + Ok(CExpr::EnumLit( + enum_name.clone(), + variant_name.clone(), + c_args, + )) + } + TypedExprKind::If(cond, then_expr, else_expr) => { + // Conditional expressions - for now, simplify to function call + // This is not ideal but works for basic cases + Err("Conditional expressions not yet supported".to_string()) + } + _ => Err(format!("Unsupported expression: {:?}", expr.kind)), + } + } + + pub fn transpile_stmt(&self, expr: &TypedExpr) -> Result { + match &expr.kind { + TypedExprKind::Let(_binding_id, name, _kind, _type_annot, init_expr) => { + let c_type = self.type_to_ctype(&expr.ty)?; + let initializer = Some(self.transpile_expr(init_expr)?); + let var_decl = CVarDecl { + name: name.clone(), + ty: c_type, + initializer, + }; + Ok(CStmt::VarDecl(var_decl)) + } + TypedExprKind::Assign(lhs, rhs) => { + let c_lhs = self.transpile_expr(lhs)?; + let c_rhs = self.transpile_expr(rhs)?; + Ok(CStmt::Assign(c_lhs, c_rhs)) + } + TypedExprKind::Return(ret_expr) => { + let c_ret = match ret_expr { + Some(expr) => Some(self.transpile_expr(expr)?), + None => None, + }; + Ok(CStmt::Return(c_ret)) + } + TypedExprKind::If(cond, then_expr, else_expr) => { + let c_cond = self.transpile_expr(cond)?; + let then_stmts = self.expr_to_stmts(then_expr)?; + let else_stmts = match else_expr { + Some(else_expr) => Some(self.expr_to_stmts(else_expr)?), + None => None, + }; + Ok(CStmt::If(c_cond, then_stmts, else_stmts)) + } + TypedExprKind::While(cond, body) => { + let c_cond = self.transpile_expr(cond)?; + let body_stmts = self.expr_to_stmts(body)?; + Ok(CStmt::While(c_cond, body_stmts)) + } + TypedExprKind::Do(exprs) => { + let mut stmts = Vec::new(); + for expr in exprs { + stmts.push(self.transpile_stmt(expr)?); + } + Ok(CStmt::Block(stmts)) + } + TypedExprKind::For(_binding_id, var_name, iterable, body) => { + // Simplified for loop handling + // For now, assume range iteration + match &iterable.kind { + TypedExprKind::Range(start, end) => { + let start_expr = self.transpile_expr(start)?; + let end_expr = self.transpile_expr(end)?; + // Create a simple for loop: for(int i = start; i < end; i++) + let init = CVarDecl { + name: var_name.clone(), + ty: CType::Int, + initializer: Some(start_expr), + }; + let cond = CExpr::BinOp( + Box::new(CExpr::Var(var_name.clone())), + CBinaryOp::Lt, + Box::new(end_expr), + ); + let incr = CExpr::UnOp(CUnaryOp::Neg, Box::new(CExpr::IntLit(-1))); // i++ + let incr_stmt = CStmt::Assign( + CExpr::Var(var_name.clone()), + CExpr::BinOp( + Box::new(CExpr::Var(var_name.clone())), + CBinaryOp::Add, + Box::new(CExpr::IntLit(1)), + ), + ); + let body_stmts = self.expr_to_stmts(body)?; + Ok(CStmt::For(init, cond, CExpr::IntLit(1), body_stmts)) + } + _ => Err("Only range iteration supported for for loops".to_string()), + } + } + TypedExprKind::Break => Ok(CStmt::Break), + TypedExprKind::Continue => Ok(CStmt::Continue), + _ => { + // For other expressions, treat as expression statements + let c_expr = self.transpile_expr(expr)?; + Ok(CStmt::Expr(c_expr)) + } + } + } + + pub fn expr_to_stmts(&self, expr: &TypedExpr) -> Result, String> { + match &expr.kind { + TypedExprKind::Do(stmts) => { + let mut c_stmts = Vec::new(); + for (i, stmt) in stmts.iter().enumerate() { + if i == stmts.len() - 1 { + // Last expression in a block should be returned + match &stmt.kind { + TypedExprKind::Return(_) => { + c_stmts.push(self.transpile_stmt(stmt)?); + } + _ => { + // Convert to return statement + let c_expr = self.transpile_expr(stmt)?; + c_stmts.push(CStmt::Return(Some(c_expr))); + } + } + } else { + c_stmts.push(self.transpile_stmt(stmt)?); + } + } + Ok(c_stmts) + } + _ => Ok(vec![self.transpile_stmt(expr)?]), + } + } + + fn binop_to_c_binop(&self, op: &BinOp) -> Result { + match op { + BinOp::Add => Ok(CBinaryOp::Add), + BinOp::Sub => Ok(CBinaryOp::Sub), + BinOp::Mul => Ok(CBinaryOp::Mul), + BinOp::Div => Ok(CBinaryOp::Div), + BinOp::Mod => Ok(CBinaryOp::Mod), + BinOp::Eq => Ok(CBinaryOp::Eq), + BinOp::Neq => Ok(CBinaryOp::Neq), + BinOp::Lt => Ok(CBinaryOp::Lt), + BinOp::Gt => Ok(CBinaryOp::Gt), + BinOp::Leq => Ok(CBinaryOp::Leq), + BinOp::Geq => Ok(CBinaryOp::Geq), + BinOp::And => Ok(CBinaryOp::And), + BinOp::Or => Ok(CBinaryOp::Or), + } + } + + fn unop_to_c_unop(&self, op: &UnOp) -> Result { + match op { + UnOp::Neg => Ok(CUnaryOp::Neg), + UnOp::Not => Ok(CUnaryOp::Not), + UnOp::Ref => Ok(CUnaryOp::Ref), + UnOp::Deref => Ok(CUnaryOp::Deref), + } + } + + fn type_to_ctype(&self, ty: &crate::typechecker::Type) -> Result { + match ty { + crate::typechecker::Type::Int => Ok(CType::Int), + crate::typechecker::Type::Float => Ok(CType::Float), + crate::typechecker::Type::Bool => Ok(CType::Bool), + crate::typechecker::Type::String => Ok(CType::Ptr(Box::new(CType::Char))), + crate::typechecker::Type::Unit => Ok(CType::Void), + crate::typechecker::Type::Ptr(inner) => { + Ok(CType::Ptr(Box::new(self.type_to_ctype(inner)?))) + } + crate::typechecker::Type::Array(inner) => { + Ok(CType::Ptr(Box::new(self.type_to_ctype(inner)?))) + } + crate::typechecker::Type::Struct(name, _) => Ok(CType::Struct(name.clone())), + crate::typechecker::Type::Enum(name, _) => Ok(CType::Struct(name.clone())), + crate::typechecker::Type::Tuple(types) => { + let mut fields = Vec::new(); + for (i, inner_ty) in types.iter().enumerate() { + let c_type = self.type_to_ctype(inner_ty)?; + fields.push(CVarDecl { + name: format!("field{}", i), + ty: c_type, + initializer: None, + }); + } + Ok(CType::UnnamedStruct(fields)) + } + crate::typechecker::Type::Function(args, ret) => { + let mut c_args = Vec::new(); + for arg in args { + c_args.push(self.type_to_ctype(arg)?); + } + let c_ret = self.type_to_ctype(ret)?; + Ok(CType::Func(c_args, Box::new(c_ret))) + } + crate::typechecker::Type::Generic(name, _args) => { + // Generic types should have been monomorphized away, + // but if they remain, treat them as struct types + // For now, just use the base name + Ok(CType::Struct(name.clone())) + } + crate::typechecker::Type::TypeVar(name) => { + // Type variables should have been resolved during monomorphization + Err(format!("Unresolved type variable: {}", name)) + } + crate::typechecker::Type::Never => Ok(CType::Void), + crate::typechecker::Type::Unknown => Err("Unknown type".to_string()), + } + } + + fn type_annot_to_ctype(&self, annot: &TypeAnnot) -> Result { + match annot { + TypeAnnot::Var(name) => match name.as_str() { + "int" => Ok(CType::Int), + "float" => Ok(CType::Float), + "bool" => Ok(CType::Bool), + "string" => Ok(CType::Ptr(Box::new(CType::Char))), + _ => Ok(CType::Struct(name.clone())), + }, + TypeAnnot::Cons(name, args) if args.is_empty() => match name.as_str() { + "int" => Ok(CType::Int), + "float" => Ok(CType::Float), + "bool" => Ok(CType::Bool), + "string" => Ok(CType::Ptr(Box::new(CType::Char))), + _ => Ok(CType::Struct(name.clone())), + }, + TypeAnnot::Cons(name, _args) => { + // Generic types - for now just use the base name + Ok(CType::Struct(name.clone())) + } + TypeAnnot::Ptr(inner) => { + let inner_type = self.type_annot_to_ctype(inner)?; + Ok(CType::Ptr(Box::new(inner_type))) + } + TypeAnnot::Array(inner) => { + let inner_type = self.type_annot_to_ctype(inner)?; + Ok(CType::Ptr(Box::new(inner_type))) + } + TypeAnnot::Tuple(fields) => { + let mut c_fields = Vec::new(); + for (i, field_annot) in fields.iter().enumerate() { + let field_type = self.type_annot_to_ctype(field_annot)?; + c_fields.push(CVarDecl { + name: format!("field{}", i), + ty: field_type, + initializer: None, + }); + } + Ok(CType::UnnamedStruct(c_fields)) + } + TypeAnnot::Function(args, ret) => { + let mut c_args = Vec::new(); + for arg in args { + c_args.push(self.type_annot_to_ctype(arg)?); + } + let c_ret = self.type_annot_to_ctype(ret)?; + Ok(CType::Func(c_args, Box::new(c_ret))) + } + _ => Err(format!("Unsupported type annotation: {:?}", annot)), + } + } +} diff --git a/src/codegen/transpiler.rs b/src/codegen/transpiler.rs index e69de29..1804cfe 100644 --- a/src/codegen/transpiler.rs +++ b/src/codegen/transpiler.rs @@ -0,0 +1,282 @@ +use crate::ast::*; +use crate::c_ir::*; +use crate::codegen::declaration_transpiler::DeclarationTranspiler; +use crate::codegen::statements_transpiler::StatementsTranspiler; +use std::collections::HashMap; + +pub struct Transpiler { + structs: HashMap, + functions: Vec, + globals: Vec, + decl_transpiler: DeclarationTranspiler, + stmt_transpiler: StatementsTranspiler, +} + +impl Transpiler { + pub fn new() -> Self { + Transpiler { + structs: HashMap::new(), + functions: Vec::new(), + globals: Vec::new(), + decl_transpiler: DeclarationTranspiler::new(), + stmt_transpiler: StatementsTranspiler::new(), + } + } + + pub fn transpile_program(&mut self, nodes: &[TypedASTNode]) -> Result { + // First pass: collect declarations + for node in nodes { + self.collect_declaration(node)?; + } + + // Second pass: transpile function bodies + self.transpile_function_bodies(nodes)?; + + // Generate C code + let mut output = String::new(); + + // Add includes + output.push_str("#include \n"); + output.push_str("#include \n"); + output.push_str("#include \n"); + output.push_str("#include \n\n"); + + // Generate struct declarations + for struct_decl in self.structs.values() { + output.push_str(&self.generate_struct_decl(struct_decl)); + output.push_str(";\n\n"); + } + + // Generate function declarations (prototypes) + for func in &self.functions { + output.push_str(&self.generate_func_proto(func)); + output.push_str(";\n"); + } + output.push_str("\n"); + + // Generate global variables + for global in &self.globals { + output.push_str(&self.generate_var_decl(global)); + output.push_str(";\n"); + } + output.push_str("\n"); + + // Generate function definitions + for func in &self.functions { + output.push_str(&self.generate_func_def(func)); + output.push_str("\n"); + } + + Ok(output) + } + + fn collect_declaration(&mut self, node: &TypedASTNode) -> Result<(), String> { + match &node.kind { + TypedASTNodeKind::Struct(s) => { + let struct_decl = self.decl_transpiler.transpile_struct(s)?; + self.structs.insert(s.name.clone(), struct_decl); + } + TypedASTNodeKind::Enum(e) => { + let enum_structs = self.decl_transpiler.transpile_enum(e)?; + for struct_decl in enum_structs { + self.structs.insert(struct_decl.name.clone(), struct_decl); + } + } + TypedASTNodeKind::Function(f) => { + let mut func_decl = self.decl_transpiler.transpile_function(f)?; + // Body will be filled later + func_decl.body = Some(Vec::new()); + self.functions.push(func_decl); + } + TypedASTNodeKind::Extern(e) => { + // For externs, we might need to add function prototypes + // But for now, skip as they're handled differently + } + _ => { + // Other node types (traits, etc.) - handle later + } + } + Ok(()) + } + + fn transpile_function_bodies(&mut self, nodes: &[TypedASTNode]) -> Result<(), String> { + for node in nodes { + if let TypedASTNodeKind::Function(f) = &node.kind { + // Find the corresponding function declaration + if let Some(func_decl) = self.functions.iter_mut().find(|fd| fd.name == f.name) { + let body_stmts = self.stmt_transpiler.expr_to_stmts(&f.body)?; + func_decl.body = Some(body_stmts); + } + } + } + Ok(()) + } + + fn generate_struct_decl(&self, struct_decl: &CStructDecl) -> String { + let mut output = format!("struct {} {{\n", struct_decl.name); + for field in &struct_decl.fields { + output.push_str(&format!(" {} {};\n", field.ty.to_string(), field.name)); + } + output.push_str("}"); + output + } + + fn generate_func_proto(&self, func: &CFuncDecl) -> String { + let params_str = if func.params.is_empty() { + "void".to_string() + } else { + func.params + .iter() + .map(|p| format!("{} {}", p.ty.to_string(), p.name)) + .collect::>() + .join(", ") + }; + format!( + "{} {}({})", + func.return_type.to_string(), + func.name, + params_str + ) + } + + fn generate_func_def(&self, func: &CFuncDecl) -> String { + let proto = self.generate_func_proto(func); + let mut output = format!("{} {{\n", proto); + + if let Some(body) = &func.body { + for stmt in body { + output.push_str(&self.generate_stmt(stmt)); + } + } + + output.push_str("}\n"); + output + } + + fn generate_var_decl(&self, var: &CVarDecl) -> String { + let mut output = format!("{} {}", var.ty.to_string(), var.name); + if let Some(init) = &var.initializer { + output.push_str(&format!(" = {}", self.generate_expr(init))); + } + output + } + + fn generate_stmt(&self, stmt: &CStmt) -> String { + match stmt { + CStmt::VarDecl(var) => format!(" {};\n", self.generate_var_decl(var)), + CStmt::Expr(expr) => format!(" {};\n", self.generate_expr(expr)), + CStmt::Assign(lhs, rhs) => format!( + " {} = {};\n", + self.generate_expr(lhs), + self.generate_expr(rhs) + ), + CStmt::Return(Some(expr)) => format!(" return {};\n", self.generate_expr(expr)), + CStmt::Return(None) => " return;\n".to_string(), + CStmt::If(cond, then_stmts, else_stmts) => { + let mut output = format!(" if ({}) {{\n", self.generate_expr(cond)); + for stmt in then_stmts { + output.push_str(&format!(" {}", self.generate_stmt(stmt))); + } + output.push_str(" }"); + if let Some(else_stmts) = else_stmts { + output.push_str(" else {\n"); + for stmt in else_stmts { + output.push_str(&format!(" {}", self.generate_stmt(stmt))); + } + output.push_str(" }"); + } + output.push_str("\n"); + output + } + CStmt::While(cond, body) => { + let mut output = format!(" while ({}) {{\n", self.generate_expr(cond)); + for stmt in body { + output.push_str(&format!(" {}", self.generate_stmt(stmt))); + } + output.push_str(" }\n"); + output + } + CStmt::Block(stmts) => { + let mut output = " {\n".to_string(); + for stmt in stmts { + output.push_str(&format!(" {}", self.generate_stmt(stmt))); + } + output.push_str(" }\n"); + output + } + _ => "// TODO: unimplemented stmt\n".to_string(), + } + } + + fn generate_expr(&self, expr: &CExpr) -> String { + match expr { + CExpr::IntLit(i) => format!("{}", i), + CExpr::FloatLit(f) => format!("{:.6}", f), + CExpr::BoolLit(b) => format!("{}", b), + CExpr::StringLit(s) => format!("\"{}\"", s), + CExpr::Var(name) => name.clone(), + CExpr::Call(func, args) => { + let args_str = args + .iter() + .map(|arg| self.generate_expr(arg)) + .collect::>() + .join(", "); + format!("{}({})", func, args_str) + } + CExpr::BinOp(lhs, op, rhs) => { + format!( + "({} {} {})", + self.generate_expr(lhs), + op.to_string(), + self.generate_expr(rhs) + ) + } + CExpr::UnOp(op, expr) => { + format!("{}{}", op.to_string(), self.generate_expr(expr)) + } + CExpr::Cast(expr, ty) => { + format!("({}) {}", ty.to_string(), self.generate_expr(expr)) + } + CExpr::AddrOf(expr) => format!("&{}", self.generate_expr(expr)), + CExpr::Deref(expr) => format!("*{}", self.generate_expr(expr)), + CExpr::Dot(expr, field) => format!("{}.{}", self.generate_expr(expr), field), + CExpr::Index(array, index) => format!( + "{}[{}]", + self.generate_expr(array), + self.generate_expr(index) + ), + CExpr::StructLit(struct_name, fields) => { + let field_inits: Vec = fields + .iter() + .map(|(name, expr)| format!(".{} = {}", name, self.generate_expr(expr))) + .collect(); + format!("(struct {}){{ {} }}", struct_name, field_inits.join(", ")) + } + CExpr::EnumLit(enum_name, variant_name, args) => { + // Find the variant index - for simplicity, assume variants are in order + // TODO: This should be stored properly + let variant_index = 0; // Placeholder - need to map variant name to index + + let variant_struct_name = format!("{}_{}", enum_name, variant_name); + let union_field_name = variant_name.to_lowercase(); + + let struct_init = if args.is_empty() { + "{}".to_string() + } else { + let field_inits: Vec = args + .iter() + .enumerate() + .map(|(i, arg)| format!(".field_{} = {}", i, self.generate_expr(arg))) + .collect(); + format!("{{ {} }}", field_inits.join(", ")) + }; + + format!( + "({}){{ .discriminant = {}, .data = {{ .{} = ({}{}) }} }}", + enum_name, variant_index, union_field_name, variant_struct_name, struct_init + ) + } + _ => "// TODO: unimplemented expr".to_string(), + } + } +} diff --git a/src/lambda_lower.rs b/src/lambda_lower.rs index 08ce1db..6ccc465 100644 --- a/src/lambda_lower.rs +++ b/src/lambda_lower.rs @@ -17,14 +17,24 @@ impl LambdaLowerer { } } - fn collect_free_vars(&self, expr: &Expr, lambda_params: &[String]) -> std::collections::HashSet { + fn collect_free_vars( + &self, + expr: &Expr, + lambda_params: &[String], + ) -> std::collections::HashSet { let mut free_vars = std::collections::HashSet::new(); let mut local_scope = std::collections::HashSet::new(); self.collect_free_vars_expr(expr, lambda_params, &mut free_vars, &mut local_scope); free_vars } - fn collect_free_vars_expr(&self, expr: &Expr, lambda_params: &[String], free_vars: &mut std::collections::HashSet, local_scope: &mut std::collections::HashSet) { + fn collect_free_vars_expr( + &self, + expr: &Expr, + lambda_params: &[String], + free_vars: &mut std::collections::HashSet, + local_scope: &mut std::collections::HashSet, + ) { match &expr.kind { ExprKind::Variable(name) => { if !lambda_params.contains(name) && !local_scope.contains(name) { @@ -136,7 +146,12 @@ impl LambdaLowerer { self.collect_free_vars_expr(end, lambda_params, free_vars, local_scope); } // Terminal expressions don't contain variables - ExprKind::Int(_) | ExprKind::Float(_) | ExprKind::Bool(_) | ExprKind::String(_) | ExprKind::Break | ExprKind::Continue => {} + ExprKind::Int(_) + | ExprKind::Float(_) + | ExprKind::Bool(_) + | ExprKind::String(_) + | ExprKind::Break + | ExprKind::Continue => {} } } @@ -183,7 +198,8 @@ impl LambdaLowerer { let new_kind = match &expr.kind { ExprKind::Lambda(args, body) => { // Collect free variables (captured variables) - let lambda_params: Vec = args.iter().map(|(name, _)| name.clone()).collect(); + let lambda_params: Vec = + args.iter().map(|(name, _)| name.clone()).collect(); let free_vars = self.collect_free_vars(body, &lambda_params); if free_vars.is_empty() { @@ -212,9 +228,7 @@ impl LambdaLowerer { }; // Store the generated function - self.generated_functions - .borrow_mut() - .push(lambda_func); + self.generated_functions.borrow_mut().push(lambda_func); // Replace the lambda with a reference to the generated function ExprKind::Variable(lambda_name) @@ -244,10 +258,7 @@ impl LambdaLowerer { ExprKind::If(cond, then_expr, else_expr) => { let lowered_cond = self.lower_expr(cond)?; let lowered_then = self.lower_expr(then_expr)?; - let lowered_else = else_expr - .as_ref() - .map(|e| self.lower_expr(e)) - .transpose()?; + let lowered_else = else_expr.as_ref().map(|e| self.lower_expr(e)).transpose()?; ExprKind::If( Box::new(lowered_cond), Box::new(lowered_then), @@ -308,10 +319,7 @@ impl LambdaLowerer { ExprKind::Dot(Box::new(lowered_obj), field.clone()) } ExprKind::EarlyReturn(expr) => { - let lowered_expr = expr - .as_ref() - .map(|e| self.lower_expr(e)) - .transpose()?; + let lowered_expr = expr.as_ref().map(|e| self.lower_expr(e)).transpose()?; ExprKind::EarlyReturn(lowered_expr.map(Box::new)) } ExprKind::OptionalChain(obj, field) => { @@ -319,10 +327,7 @@ impl LambdaLowerer { ExprKind::OptionalChain(lowered_obj.map(Box::new), field.clone()) } ExprKind::Return(expr) => { - let lowered_expr = expr - .as_ref() - .map(|e| self.lower_expr(e)) - .transpose()?; + let lowered_expr = expr.as_ref().map(|e| self.lower_expr(e)).transpose()?; ExprKind::Return(lowered_expr.map(Box::new)) } ExprKind::Array(exprs) => { diff --git a/src/lib.rs b/src/lib.rs index 352e25a..dc1f045 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,10 @@ pub const EXTENSION: &str = ".sui"; pub mod ast; -pub mod lexer; -pub mod parser; +pub mod c_ir; +pub mod codegen; pub mod lambda_lower; -pub mod typechecker; +pub mod lexer; pub mod monomorphize; +pub mod parser; +pub mod typechecker; diff --git a/src/main.rs b/src/main.rs index 38918dc..c5c96b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,12 @@ use logos::Logos; use std::fs; use suicmez::{ - lexer::Token, parser::Parser, lambda_lower::LambdaLowerer, typechecker::TypeChecker, + codegen::transpiler::Transpiler, + lambda_lower::LambdaLowerer, + lexer::Token, monomorphize::{Monomorphizer, check_no_typevars}, + parser::Parser, + typechecker::TypeChecker, }; fn main() { @@ -45,7 +49,6 @@ fn run_test_suite() { } fn format_parse_error(source: &str, error: &suicmez::parser::ParseError) -> String { - // Find the line containing the error let lines: Vec<&str> = source.lines().collect(); let mut current_pos = 0; @@ -69,7 +72,10 @@ fn format_parse_error(source: &str, error: &suicmez::parser::ParseError) -> Stri let line_end_col = (error.span.end - line_start).min(line.len()); // Print spaces and squiggly line for the span - result.push_str(&format!("{} | ", " ".repeat((line_idx + 1).to_string().len()))); + result.push_str(&format!( + "{} | ", + " ".repeat((line_idx + 1).to_string().len()) + )); for _ in 0..line_start_col { result.push(' '); } @@ -79,7 +85,10 @@ fn format_parse_error(source: &str, error: &suicmez::parser::ParseError) -> Stri result.push('\n'); // Print caret at the start position - result.push_str(&format!("{} | ", " ".repeat((line_idx + 1).to_string().len()))); + result.push_str(&format!( + "{} | ", + " ".repeat((line_idx + 1).to_string().len()) + )); for _ in 0..line_start_col { result.push(' '); } @@ -92,7 +101,10 @@ fn format_parse_error(source: &str, error: &suicmez::parser::ParseError) -> Stri } // Fallback if we can't find the line - format!("Parse error: {} (at byte {})", error.message, error.span.start) + format!( + "Parse error: {} (at byte {})", + error.message, error.span.start + ) } fn format_type_error(source: &str, error: &suicmez::typechecker::TypeError) -> String { @@ -119,7 +131,10 @@ fn format_type_error(source: &str, error: &suicmez::typechecker::TypeError) -> S let line_end_col = (error.span.end - line_start).min(line.len()); // Print spaces and squiggly line for the span - result.push_str(&format!("{} | ", " ".repeat((line_idx + 1).to_string().len()))); + result.push_str(&format!( + "{} | ", + " ".repeat((line_idx + 1).to_string().len()) + )); for _ in 0..line_start_col { result.push(' '); } @@ -129,7 +144,10 @@ fn format_type_error(source: &str, error: &suicmez::typechecker::TypeError) -> S result.push('\n'); // Print caret at the start position - result.push_str(&format!("{} | ", " ".repeat((line_idx + 1).to_string().len()))); + result.push_str(&format!( + "{} | ", + " ".repeat((line_idx + 1).to_string().len()) + )); for _ in 0..line_start_col { result.push(' '); } @@ -176,16 +194,20 @@ fn run_file(filename: &str) -> Result<(), String> { // Lower lambdas to generated functions let lowerer = LambdaLowerer::new(); - let lowered_nodes = lowerer.lower_program(&ast_nodes) + let lowered_nodes = lowerer + .lower_program(&ast_nodes) .map_err(|e| format!("Lambda lowering error: {}", e))?; - println!("Lambda lowering passed! {} nodes after lowering.", lowered_nodes.len()); + println!( + "Lambda lowering passed! {} nodes after lowering.", + lowered_nodes.len() + ); // Typecheck the AST let mut typechecker = TypeChecker::new(); - let typed_nodes = typechecker.typecheck_program(&lowered_nodes).map_err(|e| { - format_type_error(&source, &e) - })?; + let typed_nodes = typechecker + .typecheck_program(&lowered_nodes) + .map_err(|e| format_type_error(&source, &e))?; println!( "Type checking passed! {} nodes typechecked.", @@ -226,17 +248,19 @@ fn run_file(filename: &str) -> Result<(), String> { // Monomorphize the AST let monomorphizer = Monomorphizer::new(); - let mono_nodes = monomorphizer.monomorphize_program(&typed_nodes).map_err(|e| { - format!( - "Monomorphization error: {}{}", - e.message, - if let Some(span) = &e.span { - format!(" at {}:{}", span.file, span.start) - } else { - String::new() - } - ) - })?; + let mono_nodes = monomorphizer + .monomorphize_program(&typed_nodes) + .map_err(|e| { + format!( + "Monomorphization error: {}{}", + e.message, + if let Some(span) = &e.span { + format!(" at {}:{}", span.file, span.start) + } else { + String::new() + } + ) + })?; println!( "Monomorphization passed! {} nodes after specialization.", @@ -290,5 +314,18 @@ fn run_file(filename: &str) -> Result<(), String> { println!("Type variable check passed! No type variables remain in AST."); + // Generate C code + let mut transpiler = Transpiler::new(); + let c_code = transpiler + .transpile_program(&mono_nodes) + .map_err(|e| format!("Code generation error: {}", e))?; + + // Write C code to file + let c_filename = filename.replace(".sui", ".c"); + fs::write(&c_filename, &c_code) + .map_err(|e| format!("Error writing C file {}: {}", c_filename, e))?; + + println!("C code generated successfully: {}", c_filename); + Ok(()) } diff --git a/src/typechecker.rs b/src/typechecker.rs index 4ed2bbb..63ecda5 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -91,7 +91,12 @@ impl fmt::Display for TypeErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { TypeErrorKind::TypeMismatch(expected, actual) => { - write!(f, "Type mismatch: expected {}, found {}", expected.to_string(), actual.to_string()) + write!( + f, + "Type mismatch: expected {}, found {}", + expected.to_string(), + actual.to_string() + ) } TypeErrorKind::UndefinedVariable(name) => { write!(f, "Undefined variable '{}'", name) @@ -109,7 +114,11 @@ impl fmt::Display for TypeErrorKind { write!(f, "Undefined variant '{}' in enum '{}'", variant, enum_name) } TypeErrorKind::ArityMismatch(expected, actual) => { - write!(f, "Function expects {} arguments, but {} were provided", expected, actual) + write!( + f, + "Function expects {} arguments, but {} were provided", + expected, actual + ) } TypeErrorKind::NotAFunction(ty) => { write!(f, "Expected a function, but found {}", ty.to_string()) @@ -124,7 +133,12 @@ impl fmt::Display for TypeErrorKind { write!(f, "Expected an enum, but found {}", ty.to_string()) } TypeErrorKind::InvalidCast(from, to) => { - write!(f, "Invalid cast from {} to {}", from.to_string(), to.to_string()) + write!( + f, + "Invalid cast from {} to {}", + from.to_string(), + to.to_string() + ) } TypeErrorKind::InvalidPattern(msg) => { write!(f, "Invalid pattern: {}", msg) diff --git a/tests/basic_types.c b/tests/basic_types.c new file mode 100644 index 0000000..85ba69b --- /dev/null +++ b/tests/basic_types.c @@ -0,0 +1,16 @@ +#include +#include +#include +#include + +int main(void); + + +int main(void) { + int x = 5; + float y = 10.500000; + bool z = true; + char* s = "hello"; + return x; +} + diff --git a/tests/control_flow.c b/tests/control_flow.c new file mode 100644 index 0000000..a6acdcf --- /dev/null +++ b/tests/control_flow.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include + +int main(void); + + +int main(void) { + if (true) { + 1; + } else { + 0; + } + int i = 0; + while ((i < 5)) { + i = (i + 1); + } + return i; +} + diff --git a/tests/structs.c b/tests/structs.c new file mode 100644 index 0000000..d2a2577 --- /dev/null +++ b/tests/structs.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +struct Point { + int x; + int y; +}; + +int main(void); + + +int main(void) { + struct Point p = (struct Point){ .x = 5, .y = 10 }; + struct Person person = (struct Person){ .name = "Alice", .age = 30 }; + return (p.x + person.age); +} + diff --git a/tests/structs.sui b/tests/structs.sui index f837276..0638ea6 100644 --- a/tests/structs.sui +++ b/tests/structs.sui @@ -9,8 +9,8 @@ struct Person age: T, end -fn main() -> int do +fn main() -> int do let p = Point { x: 5, y: 10 }; let person = Person { name: "Alice", age: 30 }; - p.x + p.x + person.age end diff --git a/tests/traits.sui b/tests/traits.sui index e724364..e1c8c79 100644 --- a/tests/traits.sui +++ b/tests/traits.sui @@ -13,5 +13,7 @@ impl Number : Show end fn main() -> int do - 42 + let number = Number {value: 40}; + number.show(); + 0 end