Compare commits

...

2 commits

Author SHA1 Message Date
c9a7493006 codegen basics 2025-12-16 01:49:49 +05:30
a62432bfbb better typechecker errors 2025-12-15 21:42:52 +05:30
16 changed files with 1253 additions and 49 deletions

18
simple_test.c Normal file
View file

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

View file

@ -0,0 +1,157 @@
#[derive(Debug, Clone)]
pub enum CType {
Void,
Int,
Float,
Bool,
Char,
Ptr(Box<CType>),
Struct(String),
UnnamedStruct(Vec<CVarDecl>),
Array(Box<CType>, usize), // type and size
Func(Vec<CType>, Box<CType>), // 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<String> = 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<String> = 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<CExpr>,
}
#[derive(Debug, Clone)]
pub struct CStructDecl {
pub name: String,
pub fields: Vec<CVarDecl>,
}
#[derive(Debug, Clone)]
pub struct CFuncDecl {
pub name: String,
pub return_type: CType,
pub params: Vec<CVarDecl>,
pub body: Option<Vec<CStmt>>,
}
#[derive(Debug, Clone)]
pub enum CExpr {
IntLit(i64),
FloatLit(f64),
BoolLit(bool),
StringLit(String),
Var(String),
Call(String, Vec<CExpr>),
BinOp(Box<CExpr>, CBinaryOp, Box<CExpr>),
UnOp(CUnaryOp, Box<CExpr>),
Cast(Box<CExpr>, CType),
StructLit(String, Vec<(String, CExpr)>),
EnumLit(String, String, Vec<CExpr>), // enum_name, variant_name, args
ArrayLit(Vec<CExpr>),
Index(Box<CExpr>, Box<CExpr>),
Dot(Box<CExpr>, String),
AddrOf(Box<CExpr>),
Deref(Box<CExpr>),
}
#[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<CStmt>, Option<Vec<CStmt>>),
While(CExpr, Vec<CStmt>),
For(CVarDecl, CExpr, CExpr, Vec<CStmt>), // init, cond, incr, body
Return(Option<CExpr>),
Break,
Continue,
Block(Vec<CStmt>),
}
#[derive(Debug, Clone)]
pub enum CToplevel {
StructDecl(CStructDecl),
FuncDecl(CFuncDecl),
VarDecl(CVarDecl),
}

View file

@ -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<String, Type>,
}
impl DeclarationTranspiler {
pub fn new() -> Self {
DeclarationTranspiler {
type_map: HashMap::new(),
}
}
pub fn transpile_struct(&self, struct_: &TypedStruct) -> Result<CStructDecl, String> {
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<CFuncDecl, String> {
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<Vec<CStructDecl>, 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<TypeAnnot>) -> Result<CType, String> {
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
}
}
}

3
src/codegen/mod.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod declaration_transpiler;
pub mod statements_transpiler;
pub mod transpiler;

View file

@ -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<CExpr, String> {
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::<Result<Vec<_>, _>>()?;
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::<Result<Vec<_>, _>>()?;
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::<Result<Vec<_>, _>>()?;
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::<Result<Vec<_>, _>>()?;
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<CStmt, String> {
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<Vec<CStmt>, 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<CBinaryOp, String> {
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<CUnaryOp, String> {
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<CType, String> {
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<CType, String> {
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)),
}
}
}

View file

@ -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<String, CStructDecl>,
functions: Vec<CFuncDecl>,
globals: Vec<CVarDecl>,
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<String, String> {
// 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 <stdio.h>\n");
output.push_str("#include <stdlib.h>\n");
output.push_str("#include <stdbool.h>\n");
output.push_str("#include <string.h>\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::<Vec<_>>()
.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::<Vec<_>>()
.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<String> = 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<String> = 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(),
}
}
}

View file

@ -17,14 +17,24 @@ impl LambdaLowerer {
}
}
fn collect_free_vars(&self, expr: &Expr, lambda_params: &[String]) -> std::collections::HashSet<String> {
fn collect_free_vars(
&self,
expr: &Expr,
lambda_params: &[String],
) -> std::collections::HashSet<String> {
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<String>, local_scope: &mut std::collections::HashSet<String>) {
fn collect_free_vars_expr(
&self,
expr: &Expr,
lambda_params: &[String],
free_vars: &mut std::collections::HashSet<String>,
local_scope: &mut std::collections::HashSet<String>,
) {
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<String> = args.iter().map(|(name, _)| name.clone()).collect();
let lambda_params: Vec<String> =
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) => {

View file

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

View file

@ -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,66 @@ 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 {
// 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> {
@ -126,19 +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 at {}:{}: {:?}",
e.span.file, e.span.start, e.kind
)
})?;
let typed_nodes = typechecker
.typecheck_program(&lowered_nodes)
.map_err(|e| format_type_error(&source, &e))?;
println!(
"Type checking passed! {} nodes typechecked.",
@ -179,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.",
@ -243,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(())
}

View file

@ -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,75 @@ 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,

16
tests/basic_types.c Normal file
View file

@ -0,0 +1,16 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
int main(void);
int main(void) {
int x = 5;
float y = 10.500000;
bool z = true;
char* s = "hello";
return x;
}

21
tests/control_flow.c Normal file
View file

@ -0,0 +1,21 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
int main(void);
int main(void) {
if (true) {
1;
} else {
0;
}
int i = 0;
while ((i < 5)) {
i = (i + 1);
}
return i;
}

View file

@ -5,7 +5,7 @@ fn main() -> int do
else
0
let i = 0;
let mut i = 0;
while i < 5
i = i + 1;
i

19
tests/structs.c Normal file
View file

@ -0,0 +1,19 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
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);
}

View file

@ -9,8 +9,8 @@ struct Person<T>
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

View file

@ -13,5 +13,7 @@ impl Number : Show
end
fn main() -> int do
42
let number = Number {value: 40};
number.show();
0
end