Mutable locals
This commit is contained in:
parent
90c8836309
commit
916b2a653e
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue