Functions
This commit is contained in:
parent
221beea478
commit
090589fc3e
6 changed files with 173 additions and 56 deletions
2
.editorconfig
Normal file
2
.editorconfig
Normal file
|
@ -0,0 +1,2 @@
|
|||
[*.rs]
|
||||
max_line_length = 80
|
|
@ -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<Type> {
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Type>);
|
||||
|
||||
impl<'a> ExprLlvm<'a> {
|
||||
pub fn name(&self) -> &str {
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
@ -20,27 +20,80 @@ impl<W: Write> LlvmIrCodegen<W> {
|
|||
expr: &'a Expr<Type>,
|
||||
) -> Result<ExprLlvm<'a>> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,10 +27,19 @@ impl<W> LlvmIrCodegen<W> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn gensym(&mut self, name: impl AsRef<str>) -> String {
|
||||
pub fn gensym(
|
||||
&mut self,
|
||||
prefix: Option<&str>,
|
||||
name: impl AsRef<str>,
|
||||
) -> 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<W: Write> CodegenBackend for LlvmIrCodegen<W> {
|
||||
fn convert(&mut self, program: Vec<Decl<Type>>) -> 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(())
|
||||
|
|
|
@ -18,25 +18,14 @@ impl<W: Write> LlvmIrCodegen<W> {
|
|||
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")?,
|
||||
},
|
||||
|
|
64
src/utils.rs
64
src/utils.rs
|
@ -1,36 +1,34 @@
|
|||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LayeredEnv<K, V> {
|
||||
parent: Option<Box<LayeredEnv<K, V>>>,
|
||||
inner_map: HashMap<K, V>,
|
||||
inner: Option<InnerEnv<K, V>>,
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V> LayeredEnv<K, V> {
|
||||
pub fn new() -> Self {
|
||||
LayeredEnv {
|
||||
#[derive(Debug)]
|
||||
struct InnerEnv<K, V> {
|
||||
parent: Option<Box<InnerEnv<K, V>>>,
|
||||
map: HashMap<K, V>,
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V> InnerEnv<K, V> {
|
||||
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<K, V> {
|
||||
LayeredEnv {
|
||||
parent: Some(Box::new(self)),
|
||||
inner_map: HashMap::new(),
|
||||
fn with_parent(parent: InnerEnv<K, V>) -> Self {
|
||||
InnerEnv {
|
||||
parent: Some(Box::new(parent)),
|
||||
map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop(self) -> LayeredEnv<K, V> {
|
||||
*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<K: Hash + Eq, V> LayeredEnv<K, V> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V> LayeredEnv<K, V> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue