diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..649cad9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,2 @@ +[*.rs] +max_line_length = 80 diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 9605293..9f9f4d4 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -64,10 +64,13 @@ pub enum Op { } impl Op { - pub fn check_types(&self, left_ty: &Type, right_ty: &Type) -> bool { + pub fn check_types(&self, left_ty: &Type, right_ty: &Type) -> Option { // TODO: since only binops work on integers right now, just check that the two sides are both // integers. this will have to change once && gets added. - matches!(left_ty, Type::Int) && matches!(right_ty, Type::Int) + match (left_ty, right_ty) { + (Type::Int, Type::Int) => Some(Type::Int), + _ => None, + } } } diff --git a/src/codegen/llvm_ir/expr.rs b/src/codegen/llvm_ir/expr.rs index efa0830..7b776f8 100644 --- a/src/codegen/llvm_ir/expr.rs +++ b/src/codegen/llvm_ir/expr.rs @@ -2,14 +2,14 @@ use std::io::Write; use anyhow::Result; -use crate::ast::{Expr, ExprKind, Stmt, Type}; +use crate::ast::{Expr, ExprKind, Op, Type}; use super::{type_to_llvm, LlvmIrCodegen}; pub struct ExprLlvm<'a>(String, &'a Expr); impl<'a> ExprLlvm<'a> { - pub fn name(&self) -> &str { + pub fn as_str(&self) -> &str { &self.0 } } @@ -20,27 +20,80 @@ impl LlvmIrCodegen { expr: &'a Expr, ) -> Result> { let ty_str = type_to_llvm(&expr.ty); - let mut expr_id = self.gensym("expr"); + let mut expr_ref = self.gensym(Some("%"), "expr"); match &expr.kind { ExprKind::Int(n) => { - writeln!(self.writer, "%{} = alloca {}", expr_id, ty_str)?; - writeln!( - self.writer, - "store {} {}, {}* %{}", - ty_str, n, ty_str, expr_id - )?; + expr_ref = format!("{}", n); + // writeln!(self.writer, "%{} = alloca {}", expr_id, ty_str)?; + // writeln!( + // self.writer, + // "store {} {}, {}* %{}", + // ty_str, n, ty_str, expr_id + // )?; } ExprKind::Var(name) => match self.var_env.lookup(name) { Some((_, name)) => { - expr_id = name.clone(); + expr_ref = name.clone(); } None => bail!("Unbound name {name:?}"), }, - ExprKind::BinOp(_, _, _) => todo!(), - ExprKind::Call(_, _) => todo!(), + ExprKind::BinOp(left, op, right) => { + let result_ty = match op.check_types(&left.ty, &right.ty) { + Some(v) => type_to_llvm(&v), + None => bail!("Invalid types on operation."), + }; + + let left = self.convert_expr(left)?; + + let right = self.convert_expr(right)?; + + match op { + Op::LessThan => todo!(), + Op::GreaterThan => todo!(), + Op::Plus => writeln!( + self.writer, + "{} = add {} {}, {}", + expr_ref, + result_ty, + left.as_str(), + right.as_str(), + )?, + } + } + ExprKind::Call(func, args) => match self.var_env.lookup(func) { + Some((Type::Func(func_args_ty, func_ret_ty), func_name)) => { + // Clone these so we aren't depending on a reference + let func_args_ty = func_args_ty.clone(); + let func_ret_ty = func_ret_ty.clone(); + let func_name = func_name.to_owned(); + + let mut args_str = Vec::new(); + for (call_arg, expected_ty) in args.iter().zip(func_args_ty.iter()) { + let call_ty = &call_arg.ty; + if call_ty != expected_ty { + bail!("Type error: expected {expected_ty:?}, got {call_ty:?}"); + } + + let expr = self.convert_expr(&call_arg)?; + let call_ty_str = type_to_llvm(&call_ty); + args_str.push(format!("{} {}", call_ty_str, expr.as_str())); + } + + let ret_ty_str = type_to_llvm(&func_ret_ty); + writeln!( + self.writer, + "{} = call {} {} ({})", + expr_ref, + ret_ty_str, + func_name, + args_str.join(", ") + )?; + } + _ => bail!("No function with name {func:?}"), + }, } - Ok(ExprLlvm(expr_id, expr)) + Ok(ExprLlvm(expr_ref, expr)) } } diff --git a/src/codegen/llvm_ir/mod.rs b/src/codegen/llvm_ir/mod.rs index 52a0909..aeaaded 100644 --- a/src/codegen/llvm_ir/mod.rs +++ b/src/codegen/llvm_ir/mod.rs @@ -27,10 +27,19 @@ impl LlvmIrCodegen { } } - pub fn gensym(&mut self, name: impl AsRef) -> String { + pub fn gensym( + &mut self, + prefix: Option<&str>, + name: impl AsRef, + ) -> String { let ctr = self.ctr; self.ctr += 1; - format!("{}.{}", name.as_ref(), ctr) + format!( + "{}{}.{}", + prefix.map(|s| s.as_ref()).unwrap_or(""), + name.as_ref(), + ctr + ) } } @@ -45,30 +54,67 @@ fn type_to_llvm(ty: &Type) -> String { impl CodegenBackend for LlvmIrCodegen { fn convert(&mut self, program: Vec>) -> Result<()> { + // First, create a global environment and add all functions, so they can be + // called within other functions + // + // This is in a separate loop so there's no dependency on function order + self.var_env = LayeredEnv::new(); + for func in program.iter().filter_map(|decl| match decl { + Decl::Func(v) => Some(v), + _ => None, + }) { + let func_name = match func.name.as_str() { + "main" => String::from("@main"), + _ => self.gensym(Some("@"), format!("func.{}", func.name)), + }; + let value = (func.ty.clone(), func_name); + self.var_env.insert(func.name.clone(), value); + } + // Convert all functions for func in program.iter().filter_map(|decl| match decl { Decl::Func(v) => Some(v), _ => None, }) { - // Write the function header - println!("Func type: {:?}", func.ty); - let (func_args_ty, ret_ty) = match &func.ty { + let (_, ret_ty) = match &func.ty { Type::Func(args, ret) => (args, ret), _ => unreachable!(""), }; + let (_, func_name) = + self.var_env.lookup(&func.name).expect("Just inserted."); + let func_name = func_name.to_owned(); + // Set the global environment by adding the arguments + let mut args_str = Vec::new(); + self.var_env.push(); + for arg in func.args.iter() { + let arg_name = self.gensym(Some("%"), &arg.name); + let arg_ty_str = type_to_llvm(&arg.ty); + args_str.push(format!("{} {}", arg_ty_str, arg_name)); + self + .var_env + .insert(arg.name.clone(), (arg.ty.clone(), arg_name)); + } + + // Write the function header writeln!( self.writer, - "define {} @{} () {{", + "define {} {} ({}) {{", type_to_llvm(ret_ty), - func.name + func_name, + args_str.join(", "), )?; - let entry_block_name = self.gensym("entry"); + + // Write the first block + let entry_block_name = self.gensym(None, "entry"); writeln!(self.writer, "{}:", entry_block_name)?; + self.var_env.push(); self.convert_stmts(&func.stmts)?; + self.var_env.pop(); writeln!(self.writer, "}}")?; + self.var_env.pop(); } Ok(()) diff --git a/src/codegen/llvm_ir/stmts.rs b/src/codegen/llvm_ir/stmts.rs index 5126f48..c1d968f 100644 --- a/src/codegen/llvm_ir/stmts.rs +++ b/src/codegen/llvm_ir/stmts.rs @@ -18,25 +18,14 @@ impl LlvmIrCodegen { Stmt::Let(name, _, expr) => { let ty = expr.ty.clone(); let expr = self.convert_expr(&expr)?; - let new_name = expr.name().to_owned(); - - // TODO: + let new_name = expr.as_str().to_owned(); self.var_env.insert(name.clone(), (ty, new_name)); } Stmt::Return(expr) => match expr { Some(ret_val) => { let ret_ty = type_to_llvm(&ret_val.ty); let ret_val = self.convert_expr(ret_val)?; - let ret_name = self.gensym("ret"); - writeln!( - self.writer, - "%{} = load {}, {}* %{}", - ret_name, - ret_ty, - ret_ty, - ret_val.name() - )?; - writeln!(self.writer, "ret {} %{}", ret_ty, ret_name)?; + writeln!(self.writer, "ret {} {}", ret_ty, ret_val.as_str())?; } None => writeln!(self.writer, "ret void")?, }, diff --git a/src/utils.rs b/src/utils.rs index 805d500..9be5dfb 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,36 +1,34 @@ use std::collections::HashMap; use std::hash::Hash; +#[derive(Debug)] pub struct LayeredEnv { - parent: Option>>, - inner_map: HashMap, + inner: Option>, } -impl LayeredEnv { - pub fn new() -> Self { - LayeredEnv { +#[derive(Debug)] +struct InnerEnv { + parent: Option>>, + map: HashMap, +} + +impl InnerEnv { + fn new() -> Self { + InnerEnv { parent: None, - inner_map: HashMap::new(), + map: HashMap::new(), } } - pub fn insert(&mut self, key: K, value: V) { - self.inner_map.insert(key, value); - } - - pub fn push(self) -> LayeredEnv { - LayeredEnv { - parent: Some(Box::new(self)), - inner_map: HashMap::new(), + fn with_parent(parent: InnerEnv) -> Self { + InnerEnv { + parent: Some(Box::new(parent)), + map: HashMap::new(), } } - pub fn pop(self) -> LayeredEnv { - *self.parent.unwrap() - } - - pub fn lookup(&self, key: &K) -> Option<&V> { - match self.inner_map.get(key) { + fn lookup(&self, key: &K) -> Option<&V> { + match self.map.get(key) { Some(v) => Some(v), None => match self.parent.as_ref() { Some(p) => p.lookup(key), @@ -39,3 +37,29 @@ impl LayeredEnv { } } } + +impl LayeredEnv { + pub fn new() -> Self { + LayeredEnv { + inner: Some(InnerEnv::new()), + } + } + + pub fn insert(&mut self, key: K, value: V) { + self.inner.as_mut().unwrap().map.insert(key, value); + } + + pub fn push(&mut self) { + let inner = self.inner.take().unwrap(); + self.inner = Some(InnerEnv::with_parent(inner)); + } + + pub fn pop(&mut self) { + let inner = self.inner.take().unwrap(); + self.inner = Some(*inner.parent.unwrap()); + } + + pub fn lookup(&self, key: &K) -> Option<&V> { + self.inner.as_ref().unwrap().lookup(key) + } +}