Skip to content

Commit

Permalink
working on stack initialization for tuple destructuring
Browse files Browse the repository at this point in the history
  • Loading branch information
slightknack committed Jan 23, 2021
1 parent 56f1597 commit a5832b5
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 28 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -7,6 +7,9 @@
# Hidden files
**/.*

# scratchpad
/scratchpad

# But not:
!.gitignore
!.git
Expand Down
7 changes: 4 additions & 3 deletions src/common/lambda.rs
Expand Up @@ -127,7 +127,8 @@ impl fmt::Display for Lambda {
index += consumed;
writeln!(f, "Load Con\t{}\t{:?}", constant_index, self.constants[constant_index])?;
},
Opcode::Del => { writeln!(f, "Delete \t\t--")?; },
Opcode::NotInit => { writeln!(f, "NotInit \t\tDeclare variable")?; }
Opcode::Del => { writeln!(f, "Delete \t\t--")?; },
Opcode::Capture => {
let (local_index, consumed) = build_number(&self.code[index..]);
index += consumed;
Expand Down Expand Up @@ -169,14 +170,14 @@ impl fmt::Display for Lambda {
Opcode::Tuple => {
let (length, consumed) = build_number(&self.code[index..]);
index += consumed;
writeln!(f, "Tuple \t{}\tValues tupled together", length)?;
writeln!(f, "Tuple \t{}\tValues tupled together", length)?;
},
Opcode::UnLabel => { writeln!(f, "UnLabel \t\t--")?; },
Opcode::UnData => { writeln!(f, "UnData \t\t--")?; },
Opcode::UnTuple => {
let (item_index, consumed) = build_number(&self.code[index..]);
index += consumed;
writeln!(f, "UnTuple\t{}\tItem accessed", item_index)?;
writeln!(f, "UnTuple \t{}\tItem accessed", item_index)?;
},
Opcode::Copy => { writeln!(f, "Copy \t\t--")?; },
Opcode::FFICall => {
Expand Down
36 changes: 19 additions & 17 deletions src/common/opcode.rs
Expand Up @@ -6,40 +6,42 @@
pub enum Opcode {
/// Load a constant.
Con = 0,
/// Load uninitialized Data.
NotInit = 1,
/// Delete a value off the stack.
Del = 1,
Del = 2,
/// Calls out to a Rust function via FFI
FFICall = 2,
FFICall = 3,
/// Copies topmost value on the stack.
Copy = 3,
Copy = 4,
/// Moves a variable onto the heap.
Capture = 4,
Capture = 5,
/// Save a constant into a variable.
Save = 5,
Save = 6,
/// Save a value to a captured variable.
SaveCap = 6,
SaveCap = 7,
/// Push a copy of a variable onto the stack.
Load = 7,
Load = 8,
/// Load a copy of a captured variable.
LoadCap = 8,
LoadCap = 9,
/// Call a function.
Call = 9,
Call = 10,
/// Return from a function.
Return = 10,
Return = 11,
/// Creates a closure over the current local environment.
Closure = 11,
Closure = 12,
/// Prints a value.
Print = 12,
Print = 13,
/// Constructs a label.
Label = 13,
Label = 14,
// Constructs a tuple.
Tuple = 14,
Tuple = 15,
/// Destructures atomic data by asserting it matches exactly.
UnData = 15,
UnData = 16,
/// Destructures a label.
UnLabel = 16,
UnLabel = 17,
/// Sestructures a tuple.
UnTuple = 17,
UnTuple = 18,
}

impl Opcode {
Expand Down
34 changes: 30 additions & 4 deletions src/compiler/gen.rs
Expand Up @@ -8,6 +8,9 @@ use crate::common::{
data::Data,
};

// TODO: do a pass where we hoist and resolve variables?
// may work well for types too.

use crate::compiler::{
cst::{CST, CSTPattern},
// TODO: CST specific pattern once where?
Expand Down Expand Up @@ -136,7 +139,6 @@ impl Compiler {
return None;
}


/// Tries to resolve a variable in enclosing scopes
/// if resolution it successful, it captures the variable in the original scope
/// then builds a chain of upvalues to hoist that upvalue where it's needed.
Expand Down Expand Up @@ -239,32 +241,47 @@ impl Compiler {
Ok(())
}

pub fn resolve_assign(&mut self, name: &str) {
// resolves the assignment of a variable
// returns true if the variable was declared.
pub fn resolve_assign(&mut self, name: &str) -> bool {
let mut declared = false;

let index = if let Some(i) = self.local(name) {
self.lambda.emit(Opcode::Save); i
} else if let Some(i) = self.captured_upvalue(name) {
self.lambda.emit(Opcode::SaveCap); i
} else {
self.declare(name.to_string());
declared = true;
self.lambda.emit(Opcode::Save);
self.locals.len() - 1
};

self.lambda.emit_bytes(&mut split_number(index));
return declared;
}

// TODO: simplify destructure.
// because declarations can only happen with Symbol,
// there can be at most one declaration

/// Destructures a pattern into
/// a series of unpack and assign instructions.
/// Instructions match against the topmost stack item.
/// Does delete the data that is matched against.
pub fn destructure(&mut self, pattern: Spanned<CSTPattern>, redeclare: bool) {
self.lambda.emit_span(&pattern.span);

// write to a new code buffer so we can schloop declarations in
let mut old_code = mem::replace(&mut self.lambda.code, vec![]);
let mut declare = false;

match pattern.item {
CSTPattern::Symbol(name) => {
if redeclare { self.declare(name.to_string()) }
self.resolve_assign(&name);
}
// new declarations grow the stack
declare = self.resolve_assign(&name);
},
CSTPattern::Data(expected) => {
self.data(expected);
self.lambda.emit(Opcode::UnData);
Expand All @@ -282,6 +299,13 @@ impl Compiler {
}
},
}

let mut added_code = mem::replace(&mut self.lambda.code, vec![]);

// if we declared a new variable, grow the stack
if declare && !redeclare { self.lambda.emit(Opcode::NotInit); }
self.lambda.code.append(&mut old_code);
self.lambda.code.append(&mut added_code);
}

/// Assign a value to a variable.
Expand Down Expand Up @@ -371,6 +395,8 @@ mod test {
let lambda = gen(desugar(parse(lex(source).unwrap()).unwrap()).unwrap()).unwrap();

let result = vec![
// 3 variabled declared in scope
(Opcode::NotInit as u8), (Opcode::NotInit as u8), (Opcode::NotInit as u8),
(Opcode::Con as u8), 128, (Opcode::Save as u8), 128, // con true, save to heck,
(Opcode::Con as u8), 129, (Opcode::Del as u8), // load unit, delete
(Opcode::Load as u8), 128, (Opcode::Save as u8), 129, // load heck, save to lol,
Expand Down
5 changes: 5 additions & 0 deletions src/vm/stack.rs
Expand Up @@ -102,6 +102,11 @@ impl Stack {
self.stack.push(Tagged::frame());
}

#[inline]
pub fn push_not_init(&mut self) {
self.stack.push(Tagged::new(Slot::NotInit));
}

/// Wraps the top data value on the stack in `Data::Heaped`,
/// data must not already be on the heap
#[inline]
Expand Down
16 changes: 13 additions & 3 deletions src/vm/vm.rs
Expand Up @@ -88,6 +88,7 @@ impl VM {

match opcode {
Opcode::Con => self.con(),
Opcode::NotInit => self.not_init(),
Opcode::Del => self.del(),
Opcode::FFICall => self.ffi_call(),
Opcode::Copy => self.copy_val(),
Expand All @@ -114,9 +115,11 @@ impl VM {
/// In the future, fibers will allow for error handling -
/// right now, error in Passerine are practically panics.
pub fn run(&mut self) -> Result<(), Trace> {
println!("{}", self.closure.lambda);

while !self.is_terminated() {
// println!("before: {:#?}", self.stack.stack);
// println!("executing: {:?}", Opcode::from_byte(self.peek_byte()));
println!("before: {:#?}", self.stack.stack);
println!("executing: {:?}", Opcode::from_byte(self.peek_byte()));
if let Err(error) = self.step() {
// TODO: clean up stack on error
println!("{}", error);
Expand All @@ -126,7 +129,7 @@ impl VM {
};
// println!("---");
}
// println!("after: {:?}", self.stack.stack);
println!("after: {:?}", self.stack.stack);
// println!("---");

return Ok(());
Expand All @@ -150,6 +153,12 @@ impl VM {
self.done()
}

#[inline]
pub fn not_init(&mut self) -> Result<(), Trace> {
self.stack.push_not_init();
self.done()
}

/// Moves the top value on the stack to the heap,
/// replacing it with a reference to the heapified value.
#[inline]
Expand Down Expand Up @@ -301,6 +310,7 @@ impl VM {
}

self.stack.push_data(t[index].clone());
self.stack.push_data(Data::Tuple(t));
self.done()
}

Expand Down
2 changes: 1 addition & 1 deletion tests/snippets/tuple.pn
Expand Up @@ -3,4 +3,4 @@

x = (true, false)

(w, z) = x
(w, x) = x

0 comments on commit a5832b5

Please sign in to comment.