Mutable locals

This commit is contained in:
mars 2022-03-26 16:45:22 -06:00
parent 90c8836309
commit 916b2a653e
2 changed files with 88 additions and 18 deletions

View File

@ -101,4 +101,32 @@ mod tests {
let code_fn = jit_fn::<(i64, i64), i64>(source);
assert_eq!(code_fn((2, 3)), 15);
}
#[test]
fn let_shadowing() {
let source = r#"
let_shadowing(i64 a, i64 b) i64 {
let c = a + b;
let c = (c * b);
let c = (c + a);
let c = (c * a);
c
}"#;
let code_fn = jit_fn::<(i64, i64), i64>(source);
assert_eq!(code_fn((2, 3)), 34);
}
#[test]
fn let_mutable() {
let source = r#"
let_mutable(i64 a, i64 b) i64 {
let mut c = a + b;
c = (c * b);
c = (c + a);
c = (c * a);
c
}"#;
let code_fn = jit_fn::<(i64, i64), i64>(source);
assert_eq!(code_fn((2, 3)), 34);
}
}

View File

@ -7,7 +7,14 @@ use cranelift::prelude::*;
pub struct FunctionTranslator<'a> {
pub int: types::Type,
pub builder: FunctionBuilder<'a>,
pub locals: Vec<(String, Value)>,
pub locals: Vec<Local>,
pub variable_index: usize,
}
pub struct Local {
pub name: String,
pub var: Variable,
pub mutable: bool,
}
impl<'a> FunctionTranslator<'a> {
@ -16,6 +23,7 @@ impl<'a> FunctionTranslator<'a> {
int,
builder,
locals: Vec::new(),
variable_index: 0,
}
}
@ -62,7 +70,8 @@ impl<'a> FunctionTranslator<'a> {
for (i, arg) in signature.args.iter().enumerate() {
if let Some(name) = arg.name {
let val = self.builder.block_params(entry_block)[i];
self.locals.push((name.to_string(), val));
let var = self.add_local(self.int, name, false);
self.builder.def_var(var, val);
}
}
@ -86,12 +95,9 @@ impl<'a> FunctionTranslator<'a> {
self.translate_expr(expr);
}
Let { var, mutable, expr } => {
if *mutable {
unimplemented!("Mutable variables")
}
let val = self.translate_expr(expr);
self.locals.push((var.to_string(), val));
let var = self.add_local(self.int, var, *mutable);
self.builder.def_var(var, val);
}
_ => unimplemented!(),
}
@ -100,26 +106,53 @@ impl<'a> FunctionTranslator<'a> {
pub fn translate_expr(&mut self, expr: &ast::Expr) -> Value {
use ast::Expr::*;
match expr {
Local(name) => self
.get_local(name)
.expect(&format!("Unrecognized local {}", name)),
Local(name) => {
let var = self
.get_local(name)
.expect(&format!("Unrecognized local {}", name))
.var;
self.builder.use_var(var)
}
Literal(ast::Literal::DecimalInteger(literal)) => {
// TODO parse integers while building ast so that codegen doesn't have to
let val: i64 = literal.parse().unwrap();
self.builder.ins().iconst(self.int, val)
}
BinaryOp(op, terms) => {
let lhs = self.translate_expr(&terms.0);
let rhs = self.translate_expr(&terms.1);
self.translate_binary_op(op, lhs, rhs)
}
BinaryOp(op, terms) => self.translate_binary_op(op, &terms.0, &terms.1),
// TODO the AST doesn't need this either
Group(expr) => self.translate_expr(expr),
_ => unimplemented!("Expression: {:#?}", expr),
}
}
pub fn translate_binary_op(&mut self, op: &ast::BinaryOp, lhs: Value, rhs: Value) -> Value {
pub fn translate_binary_op(
&mut self,
op: &ast::BinaryOp,
lhs: &ast::Expr,
rhs: &ast::Expr,
) -> Value {
let rhs = self.translate_expr(rhs);
if *op == ast::BinaryOp::Assign {
if let ast::Expr::Local(var) = lhs {
let local = self
.get_local(var)
.expect(&format!("Unrecognized local {}", var));
if !local.mutable {
panic!("Attempted to assign to immutable variable {}", var);
}
let var = local.var;
self.builder.def_var(var, rhs);
return rhs;
} else {
unimplemented!("Assign to non-local lhs");
}
}
let lhs = self.translate_expr(lhs);
use ast::BinaryOp::*;
let ins = self.builder.ins();
match op {
@ -131,7 +164,16 @@ impl<'a> FunctionTranslator<'a> {
}
}
pub fn get_local(&self, name: &str) -> Option<Value> {
self.locals.iter().rev().find(|l| &l.0 == name).map(|l| l.1)
pub fn get_local(&self, name: &str) -> Option<&Local> {
self.locals.iter().rev().find(|l| &l.name == name)
}
pub fn add_local(&mut self, var_type: types::Type, name: &str, mutable: bool) -> Variable {
let name = name.to_string();
let var = Variable::new(self.variable_index);
self.builder.declare_var(var, var_type);
self.locals.push(Local { name, var, mutable });
self.variable_index += 1;
var
}
}