diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/codegen/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/gogen/mod.rs b/src/gogen/mod.rs new file mode 100644 index 0000000..4402e36 --- /dev/null +++ b/src/gogen/mod.rs @@ -0,0 +1,283 @@ +use crate::parser::ast::{ + BinOperator, Class, Expression, Function, Program, Statement, Struct, TopLevel, TypeExpr, +}; + +pub fn generate(program: &Program) -> String { + let mut out = String::new(); + + out.push_str("package main\n\n"); + + // imports + let mut imports = vec![]; + for item in &program.statements { + if let TopLevel::Import(i) = item { + imports.push(i.path.clone()); + } + } + + if !imports.is_empty() { + out.push_str("import (\n"); + for imp in imports { + out.push_str(&format!(" {}\n", imp)); + } + out.push_str(")\n\n"); + } + + // rest + for item in &program.statements { + match item { + TopLevel::Function(f) => out.push_str(&gen_function(f)), + TopLevel::Struct(s) => out.push_str(&gen_struct(s)), + TopLevel::Class(c) => out.push_str(&gen_class(c)), + TopLevel::Import(_) => {} + } + out.push('\n'); + } + + out +} + +fn gen_struct(s: &Struct) -> String { + let mut out = format!("type {} struct {{\n", s.name); + + for field in &s.fields { + out.push_str(&format!( + " {} {}\n", + capitalize(&field.name), + gen_type(&field.type_expr) + )); + } + + out.push_str("}\n"); + out +} + +fn gen_class(c: &Class) -> String { + let mut out = String::new(); + + // struct + out.push_str(&format!("type {} struct {{\n", c.name)); + for field in &c.fields { + out.push_str(&format!( + " {} {}\n", + capitalize(&field.name), + gen_type(&field.type_expr) + )); + } + out.push_str("}\n\n"); + + // methods + for method in &c.methods { + out.push_str(&gen_method(c, method)); + out.push('\n'); + } + + out +} + +fn gen_function(f: &Function) -> String { + let mut out = format!("func {}(", f.name); + + // params + for (i, p) in f.params.iter().enumerate() { + if i > 0 { + out.push_str(", "); + } + out.push_str(&format!("{} {}", p.name, gen_type(&p.type_expr))); + } + + out.push(')'); + + // return + if let Some(ret) = &f.return_type { + out.push_str(&format!(" {}", gen_type(ret))); + } + + out.push_str(" {\n"); + + for stmt in &f.body { + out.push_str(&gen_statement(stmt)); + } + + out.push_str("}\n"); + out +} + +fn gen_method(class: &Class, f: &Function) -> String { + let mut out = format!("func (self *{}) {}(", class.name, f.name); + + for (i, p) in f.params.iter().enumerate() { + if i > 0 { + out.push_str(", "); + } + out.push_str(&format!("{} {}", p.name, gen_type(&p.type_expr))); + } + + out.push(')'); + + if let Some(ret) = &f.return_type { + out.push_str(&format!(" {}", gen_type(ret))); + } + + out.push_str(" {\n"); + + for stmt in &f.body { + out.push_str(&gen_statement(stmt)); + } + + out.push_str("}\n"); + + out +} + +fn gen_statement(stmt: &Statement) -> String { + match stmt { + Statement::Let(s) => { + let mut out = format!(" {} := {}", s.name, gen_expr(&s.value)); + + out.push_str(";\n"); + out + } + + Statement::Return(r) => match &r.value { + Some(v) => format!(" return {};\n", gen_expr(v)), + None => " return;\n".to_string(), + }, + + Statement::Expression(e) => { + format!(" {};\n", gen_expr(e)) + } + + Statement::If(i) => { + let mut out = format!(" if {} {{\n", gen_expr(&i.condition)); + + for stmt in &i.body { + out.push_str(&gen_statement(stmt)); + } + + out.push_str(" }"); + + if let Some(else_body) = &i.else_body { + out.push_str(" else {\n"); + for stmt in else_body { + out.push_str(&gen_statement(stmt)); + } + out.push_str(" }"); + } + + out.push('\n'); + out + } + + Statement::For(f) => { + let mut out = format!( + " for _, {} := range {} {{\n", + f.var, + gen_expr(&f.iterator) + ); + + for stmt in &f.body { + out.push_str(&gen_statement(stmt)); + } + + out.push_str(" }\n"); + out + } + } +} + +fn gen_expr(expr: &Expression) -> String { + match expr { + Expression::Identifier(name, _) => name.clone(), + Expression::Integer(v, _) => v.to_string(), + Expression::Float(v, _) => v.to_string(), + Expression::StringLit(s, _) => format!("\"{}\"", s), + Expression::Bool(b, _) => b.to_string(), + + Expression::BinaryOp(b) => format!( + "{} {} {}", + gen_expr(&b.left), + op_to_str(&b.op), + gen_expr(&b.right) + ), + + Expression::Call(c) => { + let args = c.args.iter().map(gen_expr).collect::>().join(", "); + format!("{}({})", gen_expr(&c.callee), args) + } + + Expression::FieldAccess(f) => { + format!("{}.{}", gen_expr(&f.object), capitalize(&f.field)) + } + + Expression::StructInit(s) => { + let mut out = format!("{}{{", s.name); + + for (i, (name, val)) in s.fields.iter().enumerate() { + if i > 0 { + out.push_str(", "); + } + out.push_str(&format!("{}: {}", capitalize(name), gen_expr(val))); + } + + out.push('}'); + out + } + + Expression::ArrayLiteral(arr) => { + let elems = arr + .elements + .iter() + .map(gen_expr) + .collect::>() + .join(", "); + format!("[]any{{{}}}", elems) // simple version + } + + _ => todo!(), + } +} + +fn gen_type(t: &TypeExpr) -> String { + match t { + TypeExpr::Named(n) => match n.as_str() { + "int" => "int".into(), + "float" => "float64".into(), + "string" => "string".into(), + _ => n.clone(), + }, + + TypeExpr::Array(inner) => { + format!("[]{}", gen_type(inner)) + } + + TypeExpr::Optional(inner) => { + format!("*{}", gen_type(inner)) // pointer for optional + } + } +} + +fn capitalize(s: &str) -> String { + let mut chars = s.chars(); + match chars.next() { + Some(c) => c.to_uppercase().collect::() + chars.as_str(), + None => String::new(), + } +} + +fn op_to_str(op: &BinOperator) -> &'static str { + match op { + BinOperator::Add => "+", + BinOperator::Sub => "-", + BinOperator::Mul => "*", + BinOperator::Div => "/", + BinOperator::Eq => "==", + BinOperator::NotEq => "!=", + BinOperator::Lt => "<", + BinOperator::Gt => ">", + BinOperator::LtEq => "<=", + BinOperator::GtEq => ">=", + BinOperator::And => "&&", + BinOperator::Or => "||", + } +} diff --git a/src/codegen/sourcemap.rs b/src/gogen/sourcemap.rs similarity index 100% rename from src/codegen/sourcemap.rs rename to src/gogen/sourcemap.rs diff --git a/src/main.rs b/src/main.rs index 6675a01..91278ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ -pub mod codegen; pub mod errors; +pub mod gogen; pub mod parser; use std::fs; @@ -48,8 +48,9 @@ fn cmd_build(path: &str) { match parser::parse(&source) { Ok(ast) => { println!("parsed {} top-level items", ast.statements.len()); - println!("{:#?}", ast.statements) - // codegen will go here + let contents = gogen::generate(&ast); + fs::write(path.to_string() + ".go", contents).unwrap(); + println!("build successful"); } Err(e) => { eprintln!("parse error:\n{}", e);