diff --git a/.gitignore b/.gitignore index b8ffeee..3d984ce 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ # Hidden files **/.* +# scratchpad +/scratchpad + # But not: !.gitignore !.git diff --git a/src/common/lambda.rs b/src/common/lambda.rs index 2b78144..9ba3a60 100644 --- a/src/common/lambda.rs +++ b/src/common/lambda.rs @@ -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; @@ -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 => { diff --git a/src/common/opcode.rs b/src/common/opcode.rs index c2cce4a..6be86b4 100644 --- a/src/common/opcode.rs +++ b/src/common/opcode.rs @@ -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 { diff --git a/src/compiler/gen.rs b/src/compiler/gen.rs index a6e628b..0b76583 100644 --- a/src/compiler/gen.rs +++ b/src/compiler/gen.rs @@ -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? @@ -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. @@ -239,20 +241,30 @@ 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. @@ -260,11 +272,16 @@ impl Compiler { pub fn destructure(&mut self, pattern: Spanned, 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); @@ -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. @@ -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, diff --git a/src/vm/stack.rs b/src/vm/stack.rs index c1d88a1..f2c4055 100644 --- a/src/vm/stack.rs +++ b/src/vm/stack.rs @@ -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] diff --git a/src/vm/vm.rs b/src/vm/vm.rs index 829be69..95f27c6 100644 --- a/src/vm/vm.rs +++ b/src/vm/vm.rs @@ -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(), @@ -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); @@ -126,7 +129,7 @@ impl VM { }; // println!("---"); } - // println!("after: {:?}", self.stack.stack); + println!("after: {:?}", self.stack.stack); // println!("---"); return Ok(()); @@ -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] @@ -301,6 +310,7 @@ impl VM { } self.stack.push_data(t[index].clone()); + self.stack.push_data(Data::Tuple(t)); self.done() } diff --git a/tests/snippets/tuple.pn b/tests/snippets/tuple.pn index ea51476..9bdb9e1 100644 --- a/tests/snippets/tuple.pn +++ b/tests/snippets/tuple.pn @@ -3,4 +3,4 @@ x = (true, false) -(w, z) = x +(w, x) = x