aboutsummaryrefslogtreecommitdiffstats
path: root/src/compiler.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler.rs')
-rw-r--r--src/compiler.rs159
1 files changed, 133 insertions, 26 deletions
diff --git a/src/compiler.rs b/src/compiler.rs
index 1a6cfc6..ad97783 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -79,7 +79,7 @@ impl From<&Value> for Type {
Value::Float(_) => Type::Float,
Value::Bool(_) => Type::Bool,
Value::String(_) => Type::String,
- Value::Function(block) => block.ty.clone(),
+ Value::Function(_, block) => block.ty.clone(),
_ => Type::Void,
}
}
@@ -94,24 +94,69 @@ impl Type {
Type::Float => Value::Float(1.0),
Type::Bool => Value::Bool(true),
Type::String => Value::String(Rc::new("".to_string())),
- Type::Function(_, _) => Value::Function(Rc::new(Block::from_type(self))),
+ Type::Function(_, _) => Value::Function(
+ Vec::new(),
+ Rc::new(Block::from_type(self))),
}
}
}
+#[derive(Clone)]
struct Variable {
name: String,
typ: Type,
scope: usize,
+ slot: usize,
+
+ outer_slot: usize,
+ outer_upvalue: bool,
+
active: bool,
+ upvalue: bool,
+ captured: bool,
}
struct Frame {
stack: Vec<Variable>,
+ upvalues: Vec<Variable>,
scope: usize,
variables_below: usize,
}
+impl Frame {
+ fn find_local(&self, name: &str) -> Option<Variable> {
+ for var in self.stack.iter().rev() {
+ if var.name == name && var.active {
+ return Some(var.clone());
+ }
+ }
+ None
+ }
+
+ fn find_upvalue(&self, name: &str) -> Option<Variable> {
+ for var in self.upvalues.iter().rev() {
+ if var.name == name && var.active {
+ return Some(var.clone());
+ }
+ }
+ None
+ }
+
+ fn add_upvalue(&mut self, variable: Variable) -> Variable {
+ println!("{} - UPDOG", variable.name);
+ let new_variable = Variable {
+ outer_upvalue: variable.upvalue,
+ outer_slot: variable.slot,
+ slot: self.upvalues.len(),
+ active: true,
+ upvalue: true,
+ ..variable
+ };
+ self.upvalues.push(new_variable.clone());
+ new_variable
+ }
+}
+
struct Compiler {
curr: usize,
tokens: TokenStream,
@@ -130,6 +175,7 @@ macro_rules! push_frame {
{
$compiler.frames.push(Frame {
stack: Vec::new(),
+ upvalues: Vec::new(),
scope: 0,
variables_below: $compiler.frame().variables_below + $compiler.stack().len(),
});
@@ -154,8 +200,13 @@ macro_rules! push_scope {
$code;
$compiler.frame_mut().scope -= 1;
- for _ in ss..$compiler.stack().len() {
- $block.add(Op::Pop, $compiler.line());
+
+ for var in $compiler.frame().stack[ss..$compiler.stack().len()].iter().rev() {
+ if var.captured {
+ $block.add(Op::PopUpvalue, $compiler.line());
+ } else {
+ $block.add(Op::Pop, $compiler.line());
+ }
}
$compiler.stack_mut().truncate(ss);
};
@@ -170,6 +221,7 @@ impl Compiler {
frames: vec![Frame {
stack: Vec::new(),
+ upvalues: Vec::new(),
scope: 0,
variables_below: 0,
}],
@@ -388,16 +440,36 @@ impl Compiler {
}
}
- fn find_local(&self, name: &str, _block: &Block) -> Option<(usize, Type, usize)> {
- let frame = self.frame();
- for (slot, var) in frame.stack.iter().enumerate().rev() {
- if var.name == name && var.active {
- return Some((slot, var.typ.clone(), var.scope));
+ fn find_and_capture_variable<'a, I>(name: &str, mut iterator: I) -> Option<Variable>
+ where I: Iterator<Item = &'a mut Frame> {
+ if let Some(frame) = iterator.next() {
+ if let Some(res) = frame.find_local(name) {
+ frame.stack[res.slot].captured = true;
+ return Some(res);
+ }
+ if let Some(res) = frame.find_upvalue(name) {
+ return Some(res);
+ }
+
+ if let Some(res) = Self::find_and_capture_variable(name, iterator) {
+ return Some(frame.add_upvalue(res));
}
}
None
}
+ fn find_variable(&mut self, name: &str) -> Option<Variable> {
+ if let Some(res) = self.frame().find_local(name) {
+ return Some(res);
+ }
+
+ if let Some(res) = self.frame().find_upvalue(name) {
+ return Some(res);
+ }
+
+ return Self::find_and_capture_variable(name, self.frames.iter_mut().rev());
+ }
+
fn call(&mut self, block: &mut Block) {
expect!(self, Token::LeftParen, "Expected '(' at start of function call.");
@@ -423,17 +495,15 @@ impl Compiler {
}
block.add(Op::Call(arity), self.line());
-
- for _ in 0..arity {
- block.add(Op::Pop, self.line());
- }
}
fn function(&mut self, block: &mut Block) {
expect!(self, Token::Fn, "Expected 'fn' at start of function.");
- let name = if !self.stack()[self.stack().len() - 1].active {
- &self.stack()[self.stack().len() - 1].name
+ let top = self.stack().len() - 1;
+ let name = if !self.stack()[top].active {
+ self.stack_mut()[top].active = true;
+ &self.stack()[top].name
} else {
"anonumus function"
};
@@ -441,6 +511,7 @@ impl Compiler {
let mut args = Vec::new();
let mut return_type = Type::Void;
let mut function_block = Block::new(name, &self.current_file, self.line());
+
let _ret = push_frame!(self, function_block, {
loop {
match self.peek() {
@@ -479,17 +550,35 @@ impl Compiler {
}
self.scope(&mut function_block);
+
+ for var in self.frame().upvalues.iter() {
+ function_block.ups.push((var.outer_slot, var.outer_upvalue, var.typ.clone()));
+ }
+ println!("{:?}", function_block.ups);
+ // TODO(ed): Send the original place to find the upvalues,
+ // so we know from where to copy them.
});
- if !matches!(function_block.last_op(), Some(&Op::Return)) {
- function_block.add(Op::Constant(Value::Nil), self.line());
- function_block.add(Op::Return, self.line());
+ let mut prev = function_block.ops.len() - 1;
+ loop {
+ match function_block.ops[prev] {
+ Op::Pop | Op::PopUpvalue => {}
+ Op::Return => { break; } ,
+ _ => {
+ function_block.add(Op::Constant(Value::Nil), self.line());
+ function_block.add(Op::Return, self.line());
+ break;
+ }
+ }
+ prev -= 1;
}
function_block.ty = Type::Function(args, Box::new(return_type));
let function_block = Rc::new(function_block);
- block.add(Op::Constant(Value::Function(Rc::clone(&function_block))), self.line());
+
+ let func = Op::Constant(Value::Function(Vec::new(), Rc::clone(&function_block)));
+ block.add(func, self.line());
self.blocks.push(function_block);
}
@@ -498,16 +587,20 @@ impl Compiler {
Token::Identifier(name) => name,
__ => unreachable!(),
};
- if let Some((slot, _, _)) = self.find_local(&name, block) {
- block.add(Op::ReadLocal(slot), self.line());
+ if let Some(var) = self.find_variable(&name) {
+ if var.upvalue {
+ block.add(Op::ReadUpvalue(var.slot), self.line());
+ } else {
+ block.add(Op::ReadLocal(var.slot), self.line());
+ }
} else {
error!(self, format!("Using undefined variable {}.", name));
}
}
fn define_variable(&mut self, name: &str, typ: Type, block: &mut Block) -> Result<usize, ()> {
- if let Some((_, _, level)) = self.find_local(&name, block) {
- if level == self.frame().scope {
+ if let Some(var) = self.find_variable(&name) {
+ if var.scope == self.frame().scope {
error!(self, format!("Multiple definitions of {} in this block.", name));
return Err(());
}
@@ -517,9 +610,14 @@ impl Compiler {
let scope = self.frame().scope;
self.stack_mut().push(Variable {
name: String::from(name),
+ captured: false,
+ outer_upvalue: false,
+ outer_slot: 0,
+ slot,
typ,
scope,
- active: false
+ active: false,
+ upvalue: false,
});
Ok(slot)
}
@@ -535,9 +633,13 @@ impl Compiler {
}
fn assign(&mut self, name: &str, block: &mut Block) {
- if let Some((slot, _, _)) = self.find_local(&name, block) {
+ if let Some(var) = self.find_variable(&name) {
self.expression(block);
- block.add(Op::Assign(slot), self.line());
+ if var.upvalue {
+ block.add(Op::AssignUpvalue(var.slot), self.line());
+ } else {
+ block.add(Op::AssignLocal(var.slot), self.line());
+ }
} else {
error!(self, format!("Using undefined variable {}.", name));
}
@@ -747,8 +849,13 @@ impl Compiler {
self.stack_mut().push(Variable {
name: String::from("/main/"),
typ: Type::Void,
+ outer_upvalue: false,
+ outer_slot: 0,
+ slot: 0,
scope: 0,
active: false,
+ captured: false,
+ upvalue: false,
});
let mut block = Block::new(name, file, 0);