From b498cc7ff776341e94b08743f762c6bf1261ff95 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Fri, 1 May 2026 17:43:54 +0200 Subject: [PATCH] rust codegen --- src/codegen.rs | 277 ++++++++++++++++-------------------------------- src/compiler.rs | 13 +-- 2 files changed, 96 insertions(+), 194 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 29d6eb6..6df61fc 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,11 +1,11 @@ use parser::ast::{BinaryOp, Block, Expression, Postfix, Statement, TopLevel, TypeExpr, VarKind}; -pub struct GoCodegen { +pub struct RustCodegen { output: String, indent: usize, } -impl GoCodegen { +impl RustCodegen { pub fn new() -> Self { Self { output: String::new(), @@ -17,10 +17,6 @@ impl GoCodegen { " ".repeat(self.indent) } - fn add_indented(&mut self, s: &str) { - self.output.push_str(&format!("{}{}", self.indent_str(), s)); - } - fn add(&mut self, s: &str) { self.output.push_str(s); } @@ -31,8 +27,7 @@ impl GoCodegen { } fn add_indentedln(&mut self, s: &str) { - self.add_indented(s); - self.add("\n"); + self.add(&format!("{}{}\n", self.indent_str(), s)); } pub fn generate(&mut self, toplevels: &[TopLevel]) -> String { @@ -45,29 +40,29 @@ impl GoCodegen { fn generate_toplevel(&mut self, tl: &TopLevel) { match tl { TopLevel::Import(path) => { - let import_path = path.replace("\"", ""); - if import_path.starts_with("./") || import_path.starts_with("/") { - self.addln(&format!("import \"{}\"", import_path)); - } else { - self.addln(&format!("import \"{}\"", import_path)); - } - self.addln(""); + let path = path.replace("\"", ""); + self.addln(&format!("use {};", path)); } + TopLevel::StructDecl { export, name, fields, } => { - let name = if *export { name } else { name }; - self.addln(&format!("type {} struct {{", name)); + let vis = if *export { "pub " } else { "" }; + + self.addln(&format!("{}struct {} {{", vis, name)); self.indent += 1; + for (field_name, (_, ty)) in &fields.0 { - let go_ty = self.translate_type(ty); - self.addln(&format!("{} {}", field_name, go_ty)); + let ty = self.translate_type(ty); + self.add_indentedln(&format!("pub {}: {},", field_name, ty)); } + self.indent -= 1; self.addln("}\n"); } + TopLevel::FunctionDecl { export, name, @@ -75,31 +70,26 @@ impl GoCodegen { return_type, body, } => { - let name = if *export { - format!("{}", name) - } else { - name.clone() - }; + let vis = if *export { "pub " } else { "" }; + let params_str = params .0 .iter() - .map(|(n, (_, t))| format!("{} {}", n, self.translate_type(t))) + .map(|(n, (_, t))| format!("{}: {}", n, self.translate_type(t))) .collect::>() .join(", "); - let ret_ty = return_type + let ret = return_type .as_ref() - .map(|t| self.translate_type(t)) - .unwrap_or_else(|| "".to_string()); + .map(|t| format!(" -> {}", self.translate_type(t))) + .unwrap_or_default(); + + self.addln(&format!("{}fn {}({}){} {{", vis, name, params_str, ret)); - if ret_ty.is_empty() { - self.addln(&format!("func {}({}) {{", name, params_str)); - } else { - self.addln(&format!("func {}({}) {} {{", name, params_str, ret_ty)); - } self.indent += 1; self.generate_block(body); self.indent -= 1; + self.addln("}\n"); } } @@ -108,13 +98,11 @@ impl GoCodegen { fn translate_type(&self, ty: &TypeExpr) -> String { match ty { TypeExpr::Identifier(name) => match name.as_str() { - "int" => "int".to_string(), - "float" | "float64" => "float64".to_string(), - "float32" => "float32".to_string(), - "bool" => "bool".to_string(), - "string" => "string".to_string(), - "byte" => "byte".to_string(), - "rune" => "rune".to_string(), + "int" => "i32".into(), + "float" | "float64" => "f64".into(), + "float32" => "f32".into(), + "bool" => "bool".into(), + "string" => "String".into(), _ => name.clone(), }, } @@ -131,112 +119,95 @@ impl GoCodegen { Statement::Expression(expr) => { self.add_indentedln(&format!("{};", self.generate_expression(expr))); } + Statement::Block(block) => { self.add_indentedln("{"); self.indent += 1; self.generate_block(block); self.indent -= 1; - self.add_indentedln("}\n"); + self.add_indentedln("}"); } + Statement::VarDecl { kind, name, init, type_, } => { - let go_kind = match kind { - VarKind::Let | VarKind::Const => "var", - VarKind::Var => "var", + let mutability = match kind { + VarKind::Var => "mut ", + _ => "", }; - let init_expr = init + + let ty = type_ + .as_ref() + .map(|t| format!(": {}", self.translate_type(t))) + .unwrap_or_default(); + + let init = init .as_ref() .map(|e| format!(" = {}", self.generate_expression(e))) - .unwrap_or_else(|| "".to_string()); - let type_expr = type_ - .clone() - .map(|t| self.translate_type(&t)) .unwrap_or_default(); - self.add_indentedln(&format!( - "{} {} {}{};\n", - go_kind, name, type_expr, init_expr - )); + + self.add_indentedln(&format!("let {}{}{}{};", mutability, name, ty, init)); } + Statement::VarAssign { target, value } => { self.add_indentedln(&format!( - "{} = {};\n", + "{} = {};", self.generate_expression(target), self.generate_expression(value) )); } + Statement::If { condition, then_branch, else_branch, } => { - self.add_indented(&format!("if {} ", self.generate_expression(condition))); + self.add_indentedln(&format!("if {} {{", self.generate_expression(condition))); + + self.indent += 1; self.generate_statement(then_branch); + self.indent -= 1; + + self.add_indentedln("}"); + if let Some(else_br) = else_branch { - self.add_indented("else "); + self.add_indentedln("else {"); + self.indent += 1; self.generate_statement(else_br); + self.indent -= 1; + self.add_indentedln("}"); } } + Statement::While { condition, body } => { - self.add_indented(&format!("for {} ", self.generate_expression(condition))); + self.add_indentedln(&format!("while {} {{", self.generate_expression(condition))); + + self.indent += 1; self.generate_statement(body); + self.indent -= 1; + + self.add_indentedln("}"); } - Statement::For { - init, - condition, - update, - body, - } => { - let (kind, init_name, init_val) = init; - let init_expr = init_val - .as_ref() - .map(|e| format!(" = {}", self.generate_expression(e))) - .unwrap_or_else(|| "".to_string()); - let init_str = format!("{} {}{}", self.var_kind_to_go(kind), init_name, init_expr); - let cond_str = condition - .as_ref() - .map(|e| self.generate_expression(e)) - .unwrap_or_else(|| "true".to_string()); - - let update_str = update - .as_ref() - .map(|s| self.generate_expression(&self.stmt_to_expr(s))) - .unwrap_or_else(|| "".to_string()); - - self.add_indented(&format!("for {}; {}; {} ", init_str, cond_str, update_str)); - self.generate_statement(body); + Statement::For { .. } => { + // Rust doesn't support C-style for loops + self.add_indentedln("// TODO: transform into iterator-based loop"); } + Statement::Return(expr) => { - let ret_val = expr + let val = expr .as_ref() .map(|e| self.generate_expression(e)) - .unwrap_or_else(|| "".to_string()); - self.add_indentedln(&format!("return {};\n", ret_val)); - } - Statement::Break => { - self.add_indentedln("break;\n"); - } - Statement::Continue => { - self.add_indentedln("continue;\n"); - } - } - } + .unwrap_or_default(); - fn stmt_to_expr(&self, stmt: &Statement) -> Expression { - match stmt { - Statement::Expression(e) => e.clone(), - _ => Expression::Identifier(String::new()), - } - } + self.add_indentedln(&format!("return {};", val)); + } - fn var_kind_to_go(&self, kind: &VarKind) -> String { - match kind { - VarKind::Let | VarKind::Const => "var".to_string(), - VarKind::Var => "var".to_string(), + Statement::Break => self.add_indentedln("break;"), + Statement::Continue => self.add_indentedln("continue;"), } } @@ -246,7 +217,8 @@ impl GoCodegen { Expression::IntLiteral(n) => n.to_string(), Expression::FloatLiteral(n) => n.to_string(), Expression::BoolLiteral(b) => b.to_string(), - Expression::StringLiteral(s) => format!("\"{}\"", s), + Expression::StringLiteral(s) => format!("\"{}\".to_string()", s), + Expression::Postfix { initial, postfixes } => { let base = self.generate_expression(initial); self.apply_postfixes(&base, postfixes) @@ -256,28 +228,35 @@ impl GoCodegen { fn apply_postfixes(&self, base: &str, postfixes: &[Postfix]) -> String { let mut result = base.to_string(); + for postfix in postfixes { result = match postfix { Postfix::FieldAccess(field) => format!("{}.{}", result, field), + Postfix::Call(args) => { - let args_str = args + let args = args .iter() .map(|a| self.generate_expression(a)) .collect::>() .join(", "); - format!("{}({})", result, args_str) + format!("{}({})", result, args) } + Postfix::StructCall(fields) => { - let args_str = fields + let fields = fields .iter() - .map(|(a, b)| format!("{a}: {}", self.generate_expression(b))) + .map(|(k, v)| format!("{}: {}", k, self.generate_expression(v))) .collect::>() .join(", "); - format!("{}{{{}}}", result, args_str) + format!("{} {{ {} }}", result, fields) } - Postfix::Index(idx) => format!("{}[{}]", result, self.generate_expression(idx)), + + Postfix::Index(idx) => { + format!("{}[{}]", result, self.generate_expression(idx)) + } + Postfix::Binary(op, rhs) => { - let op_str = match op { + let op = match op { BinaryOp::Plus => "+", BinaryOp::Minus => "-", BinaryOp::Multiply => "*", @@ -290,86 +269,18 @@ impl GoCodegen { BinaryOp::LessThanOrEqual => "<=", BinaryOp::GreaterThanOrEqual => ">=", }; - format!("{} {} {}", result, op_str, self.generate_expression(rhs)) + + format!("{} {} {}", result, op, self.generate_expression(rhs)) } }; } + result } } -impl Default for GoCodegen { +impl Default for RustCodegen { fn default() -> Self { Self::new() } } - -#[cfg(test)] -mod tests { - use super::*; - use parser::ast::{Block, Expression, ParamList, Statement, TopLevel, TypeExpr}; - use std::collections::HashMap; - - #[test] - fn test_int_literal() { - let cg = GoCodegen::new(); - let expr = Expression::IntLiteral(42); - let result = cg.generate_expression(&expr); - assert_eq!(result, "42"); - } - - #[test] - fn test_string_literal() { - let cg = GoCodegen::new(); - let expr = Expression::StringLiteral("hello".to_string()); - let result = cg.generate_expression(&expr); - assert_eq!(result, "\"hello\""); - } - - #[test] - fn test_function_decl() { - let mut cg = GoCodegen::new(); - let toplevel = TopLevel::FunctionDecl { - export: true, - name: "main".to_string(), - params: ParamList(HashMap::new()), - return_type: None, - body: Block(vec![Statement::Return(None)]), - }; - cg.generate_toplevel(&toplevel); - let output = cg.output.clone(); - assert!(output.contains("func main()")); - } - - #[test] - fn test_struct_decl() { - let mut cg = GoCodegen::new(); - let mut fields = HashMap::new(); - fields.insert( - "x".to_string(), - (true, TypeExpr::Identifier("int".to_string())), - ); - let toplevel = TopLevel::StructDecl { - export: true, - name: "Point".to_string(), - fields: ParamList(fields), - }; - cg.generate_toplevel(&toplevel); - let output = cg.output.clone(); - assert!(output.contains("type Point struct")); - assert!(output.contains("x int")); - } - - #[test] - fn test_if_statement() { - let mut cg = GoCodegen::new(); - let stmt = Statement::If { - condition: Expression::Identifier("x".to_string()), - then_branch: Box::new(Statement::Return(Some(Expression::IntLiteral(1)))), - else_branch: None, - }; - cg.generate_statement(&stmt); - let output = cg.output.clone(); - assert!(output.contains("if x")); - } -} diff --git a/src/compiler.rs b/src/compiler.rs index 7dbda0e..db5d31c 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -30,14 +30,6 @@ pub fn build() { let file_name = entry.file_name(); let file_name = file_name.to_string_lossy(); - let script = if file_name.ends_with(".ms") { - true - } else if file_name.ends_with(".mist") { - false - } else { - continue; - }; - // 3. read entry file let source = match fs::read_to_string(&entry_path) { Ok(s) => s, @@ -59,7 +51,7 @@ pub fn build() { // semantic::walk_ast(semantic::scope::Scope::from_top(&root, &ast), &mut ast); - let mut gc = crate::codegen::GoCodegen::new(); + let mut gc = crate::codegen::RustCodegen::new(); let output = gc.generate(&ast); if let Err(e) = fs::create_dir_all(&out_dir) { @@ -67,8 +59,7 @@ pub fn build() { process::exit(1); } - let out_file = - out_dir.join(file_name.replace(if script { ".ms" } else { ".mist" }, ".go")); + let out_file = out_dir.join(file_name.replace(".mist", ".rs")); if let Err(e) = fs::write(&out_file, output) { eprintln!("error: failed to write output\n {}", e);