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 {
|
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
|
// 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.
|
// 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 anyhow::Result;
|
||||||
|
|
||||||
use crate::ast::{Expr, ExprKind, Stmt, Type};
|
use crate::ast::{Expr, ExprKind, Op, Type};
|
||||||
|
|
||||||
use super::{type_to_llvm, LlvmIrCodegen};
|
use super::{type_to_llvm, LlvmIrCodegen};
|
||||||
|
|
||||||
pub struct ExprLlvm<'a>(String, &'a Expr<Type>);
|
pub struct ExprLlvm<'a>(String, &'a Expr<Type>);
|
||||||
|
|
||||||
impl<'a> ExprLlvm<'a> {
|
impl<'a> ExprLlvm<'a> {
|
||||||
pub fn name(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,27 +20,80 @@ impl<W: Write> LlvmIrCodegen<W> {
|
||||||
expr: &'a Expr<Type>,
|
expr: &'a Expr<Type>,
|
||||||
) -> Result<ExprLlvm<'a>> {
|
) -> Result<ExprLlvm<'a>> {
|
||||||
let ty_str = type_to_llvm(&expr.ty);
|
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 {
|
match &expr.kind {
|
||||||
ExprKind::Int(n) => {
|
ExprKind::Int(n) => {
|
||||||
writeln!(self.writer, "%{} = alloca {}", expr_id, ty_str)?;
|
expr_ref = format!("{}", n);
|
||||||
writeln!(
|
// writeln!(self.writer, "%{} = alloca {}", expr_id, ty_str)?;
|
||||||
self.writer,
|
// writeln!(
|
||||||
"store {} {}, {}* %{}",
|
// self.writer,
|
||||||
ty_str, n, ty_str, expr_id
|
// "store {} {}, {}* %{}",
|
||||||
)?;
|
// ty_str, n, ty_str, expr_id
|
||||||
|
// )?;
|
||||||
}
|
}
|
||||||
ExprKind::Var(name) => match self.var_env.lookup(name) {
|
ExprKind::Var(name) => match self.var_env.lookup(name) {
|
||||||
Some((_, name)) => {
|
Some((_, name)) => {
|
||||||
expr_id = name.clone();
|
expr_ref = name.clone();
|
||||||
}
|
}
|
||||||
None => bail!("Unbound name {name:?}"),
|
None => bail!("Unbound name {name:?}"),
|
||||||
},
|
},
|
||||||
ExprKind::BinOp(_, _, _) => todo!(),
|
ExprKind::BinOp(left, op, right) => {
|
||||||
ExprKind::Call(_, _) => todo!(),
|
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;
|
let ctr = self.ctr;
|
||||||
self.ctr += 1;
|
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> {
|
impl<W: Write> CodegenBackend for LlvmIrCodegen<W> {
|
||||||
fn convert(&mut self, program: Vec<Decl<Type>>) -> Result<()> {
|
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
|
// Convert all functions
|
||||||
for func in program.iter().filter_map(|decl| match decl {
|
for func in program.iter().filter_map(|decl| match decl {
|
||||||
Decl::Func(v) => Some(v),
|
Decl::Func(v) => Some(v),
|
||||||
_ => None,
|
_ => None,
|
||||||
}) {
|
}) {
|
||||||
// Write the function header
|
let (_, ret_ty) = match &func.ty {
|
||||||
println!("Func type: {:?}", func.ty);
|
|
||||||
let (func_args_ty, ret_ty) = match &func.ty {
|
|
||||||
Type::Func(args, ret) => (args, ret),
|
Type::Func(args, ret) => (args, ret),
|
||||||
_ => unreachable!(""),
|
_ => 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!(
|
writeln!(
|
||||||
self.writer,
|
self.writer,
|
||||||
"define {} @{} () {{",
|
"define {} {} ({}) {{",
|
||||||
type_to_llvm(ret_ty),
|
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)?;
|
writeln!(self.writer, "{}:", entry_block_name)?;
|
||||||
|
|
||||||
|
self.var_env.push();
|
||||||
self.convert_stmts(&func.stmts)?;
|
self.convert_stmts(&func.stmts)?;
|
||||||
|
self.var_env.pop();
|
||||||
|
|
||||||
writeln!(self.writer, "}}")?;
|
writeln!(self.writer, "}}")?;
|
||||||
|
self.var_env.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -18,25 +18,14 @@ impl<W: Write> LlvmIrCodegen<W> {
|
||||||
Stmt::Let(name, _, expr) => {
|
Stmt::Let(name, _, expr) => {
|
||||||
let ty = expr.ty.clone();
|
let ty = expr.ty.clone();
|
||||||
let expr = self.convert_expr(&expr)?;
|
let expr = self.convert_expr(&expr)?;
|
||||||
let new_name = expr.name().to_owned();
|
let new_name = expr.as_str().to_owned();
|
||||||
|
|
||||||
// TODO:
|
|
||||||
self.var_env.insert(name.clone(), (ty, new_name));
|
self.var_env.insert(name.clone(), (ty, new_name));
|
||||||
}
|
}
|
||||||
Stmt::Return(expr) => match expr {
|
Stmt::Return(expr) => match expr {
|
||||||
Some(ret_val) => {
|
Some(ret_val) => {
|
||||||
let ret_ty = type_to_llvm(&ret_val.ty);
|
let ret_ty = type_to_llvm(&ret_val.ty);
|
||||||
let ret_val = self.convert_expr(ret_val)?;
|
let ret_val = self.convert_expr(ret_val)?;
|
||||||
let ret_name = self.gensym("ret");
|
writeln!(self.writer, "ret {} {}", ret_ty, ret_val.as_str())?;
|
||||||
writeln!(
|
|
||||||
self.writer,
|
|
||||||
"%{} = load {}, {}* %{}",
|
|
||||||
ret_name,
|
|
||||||
ret_ty,
|
|
||||||
ret_ty,
|
|
||||||
ret_val.name()
|
|
||||||
)?;
|
|
||||||
writeln!(self.writer, "ret {} %{}", ret_ty, ret_name)?;
|
|
||||||
}
|
}
|
||||||
None => writeln!(self.writer, "ret void")?,
|
None => writeln!(self.writer, "ret void")?,
|
||||||
},
|
},
|
||||||
|
|
64
src/utils.rs
64
src/utils.rs
|
@ -1,36 +1,34 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct LayeredEnv<K, V> {
|
pub struct LayeredEnv<K, V> {
|
||||||
parent: Option<Box<LayeredEnv<K, V>>>,
|
inner: Option<InnerEnv<K, V>>,
|
||||||
inner_map: HashMap<K, V>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Hash + Eq, V> LayeredEnv<K, V> {
|
#[derive(Debug)]
|
||||||
pub fn new() -> Self {
|
struct InnerEnv<K, V> {
|
||||||
LayeredEnv {
|
parent: Option<Box<InnerEnv<K, V>>>,
|
||||||
|
map: HashMap<K, V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Hash + Eq, V> InnerEnv<K, V> {
|
||||||
|
fn new() -> Self {
|
||||||
|
InnerEnv {
|
||||||
parent: None,
|
parent: None,
|
||||||
inner_map: HashMap::new(),
|
map: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, key: K, value: V) {
|
fn with_parent(parent: InnerEnv<K, V>) -> Self {
|
||||||
self.inner_map.insert(key, value);
|
InnerEnv {
|
||||||
}
|
parent: Some(Box::new(parent)),
|
||||||
|
map: HashMap::new(),
|
||||||
pub fn push(self) -> LayeredEnv<K, V> {
|
|
||||||
LayeredEnv {
|
|
||||||
parent: Some(Box::new(self)),
|
|
||||||
inner_map: HashMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(self) -> LayeredEnv<K, V> {
|
fn lookup(&self, key: &K) -> Option<&V> {
|
||||||
*self.parent.unwrap()
|
match self.map.get(key) {
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lookup(&self, key: &K) -> Option<&V> {
|
|
||||||
match self.inner_map.get(key) {
|
|
||||||
Some(v) => Some(v),
|
Some(v) => Some(v),
|
||||||
None => match self.parent.as_ref() {
|
None => match self.parent.as_ref() {
|
||||||
Some(p) => p.lookup(key),
|
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