diff --git a/src/common/lambda.rs b/src/common/lambda.rs index 4978caf..86919a5 100644 --- a/src/common/lambda.rs +++ b/src/common/lambda.rs @@ -1,8 +1,13 @@ use std::fmt; -use crate::common::{number::build_number, opcode::Opcode, span::Span}; - -use crate::vm::data::Data; +use crate::{ + common::{ + number::build_number, + opcode::Opcode, + span::Span, + }, + vm::data::Data, +}; /// Represents a variable visible in the current scope. #[derive(Debug, Clone, PartialEq, Eq)] @@ -20,16 +25,16 @@ pub struct Lambda { // TODO: make this a list of variable names // So structs can be made, and state preserved in the repl. /// Number of variables declared in this scope. - pub decls: Vec, + pub decls: usize, /// Each byte is an opcode or a number-stream. - pub code: Vec, + pub code: Vec, /// Each usize indexes the bytecode op that begins each line. - pub spans: Vec<(usize, Span)>, + pub spans: Vec<(usize, Span)>, /// Number-stream indexed, used to load constants. pub constants: Vec, /// List of positions of locals in the scope where this lambda is defined, /// indexes must be gauranteed to be data on the heap. - pub captures: Vec, + pub captures: Vec, // TODO: delete FFI // / List of FFI functions (i.e. Rust functions) // / that can be called from this function. @@ -42,11 +47,11 @@ impl Lambda { /// Creates a new empty `Lambda` to be filled. pub fn empty() -> Lambda { Lambda { - decls: vec![], - code: vec![], - spans: vec![], + decls: 0, + code: vec![], + spans: vec![], constants: vec![], - captures: vec![], + captures: vec![], // ffi: vec![], } } @@ -81,13 +86,14 @@ impl Lambda { Opcode::Del => vec![], Opcode::FFICall => panic!(), Opcode::Copy => vec![], - // Opcode::Capture => vec![self.decls], // TODO: correct bounds check? - // Opcode::Save => vec![self.decls], + // Opcode::Capture => vec![self.decls], // TODO: correct bounds + // check? Opcode::Save => vec![self.decls], Opcode::SaveCap => vec![self.captures.len()], // Opcode::Load => vec![self.decls], Opcode::LoadCap => vec![self.captures.len()], Opcode::Call => vec![], - // Opcode::Return => vec![self.decls], // TODO: correct bounds check? + // Opcode::Return => vec![self.decls], // TODO: correct bounds + // check? Opcode::Closure => vec![], Opcode::Print => vec![], Opcode::Label => vec![], @@ -136,9 +142,7 @@ impl Lambda { } /// Emits an opcode as a byte. - pub fn emit(&mut self, op: Opcode) { - self.code.push(op as u8) - } + pub fn emit(&mut self, op: Opcode) { self.code.push(op as u8) } /// Emits a series of bytes. pub fn emit_bytes(&mut self, bytes: &mut Vec) { @@ -153,15 +157,13 @@ impl Lambda { } /// Removes the last emitted byte. - pub fn demit(&mut self) { - self.code.pop(); - } + pub fn demit(&mut self) { self.code.pop(); } /// Given some data, this function adds it to the constants table, /// and returns the data's index. - /// The constants table is push only, so constants are identified by their index. - /// The resulting usize can be split up into a number byte stream, - /// and be inserted into the bytecode. + /// The constants table is push only, so constants are identified by their + /// index. The resulting usize can be split up into a number byte + /// stream, and be inserted into the bytecode. pub fn index_data(&mut self, data: Data) -> usize { match self.constants.iter().position(|d| d == &data) { Some(d) => d, @@ -172,7 +174,8 @@ impl Lambda { } } - /// Look up the nearest span at or before the index of a specific bytecode op. + /// Look up the nearest span at or before the index of a specific bytecode + /// op. pub fn index_span(&self, index: usize) -> Span { let mut best = None; diff --git a/src/common/lit.rs b/src/common/lit.rs index 2624ff7..8dccd3b 100644 --- a/src/common/lit.rs +++ b/src/common/lit.rs @@ -1,8 +1,15 @@ use std::{ f64, - fmt::{Debug, Display, Formatter, Result}, + fmt::{ + Debug, + Display, + Formatter, + Result, + }, }; +use crate::vm::data::Data; + pub enum ArbInt { Small(u128), Large(Vec), @@ -35,8 +42,22 @@ pub enum Lit { Boolean(bool), } +impl Lit { + pub fn to_data(self) -> Data { + match self { + Lit::Float(f) => Data::Float(f), + Lit::Integer(i) => Data::Integer(i), + Lit::String(s) => Data::String(s), + Lit::Label(_, _) => todo!(), + Lit::Unit => Data::Unit, + Lit::Boolean(b) => Data::Boolean(b), + } + } +} + impl Display for Lit { - /// Displays some Passerine Data in a pretty manner, as if it were printed to console. + /// Displays some Passerine Data in a pretty manner, as if it were printed + /// to console. fn fmt(&self, f: &mut Formatter<'_>) -> Result { match self { Lit::Float(n) => write!(f, "{}", n), diff --git a/src/compiler/gen.rs b/src/compiler/gen.rs index cd2a955..5c39ec9 100644 --- a/src/compiler/gen.rs +++ b/src/compiler/gen.rs @@ -3,81 +3,73 @@ use std::{ rc::Rc, }; -use crate::common::{ - number::split_number, - span::{Span, Spanned}, - lambda::{Captured, Lambda}, - opcode::Opcode, - data::Data, -}; - // TODO: hoist and resolve types - -use crate::construct::{ - sst::{Scope, SST, SSTPattern}, - symbol::UniqueSymbol, - module::Module, -}; - -use crate::compiler::{lower::Lower, syntax::Syntax}; - -use crate::core::{ - ffi_core, - ffi::FFI, +use crate::{ + common::{ + lambda::{ + Captured, + Lambda, + }, + lit::Lit, + number::split_number, + opcode::Opcode, + span::{ + Span, + Spanned, + }, + }, + compiler::syntax::Syntax, + construct::{ + scope::Scope, + symbol::UniqueSymbol, + tree::{ + Base, + Pattern, + ScopedLambda, + SST, + }, + }, + core::{ + ffi::FFI, + ffi_core, + }, }; -// TODO: namespaces for FFIs? - -impl Lower for Module, Scope> { - type Out = Rc; - - /// Simple function that generates unoptimized bytecode from an `SST`. - /// Exposes the functionality of the `Compiler`. - fn lower(self) -> Result { - let module = Module::new(self.repr, (self.assoc, ffi_core())); - return module.lower(); - } -} - -impl Lower for Module, (Scope, FFI)> { - type Out = Rc; - - /// Generates unoptimized bytecode from a `SST`, - /// Given a specific FFI. Note that this doesn't even assume the core ffi, - /// So it's required you generate a core ffi with `core::ffi_core()`, - /// Then merge it with your ffi with `FFI::combine(...)`. - fn lower(self) -> Result { - let mut compiler = Compiler::base(self.assoc.1, self.assoc.0); - compiler.walk(&self.repr)?; - return Ok(Rc::new(compiler.lambda)); - } -} - -/// Compiler is a bytecode generator that walks an SST and produces (unoptimized) Bytecode. -/// There are plans to add a bytecode optimizer in the future. -/// Note that this struct should not be controlled manually, +/// Compiler is a bytecode generator that walks an SST and produces +/// (unoptimized) Bytecode. There are plans to add a bytecode optimizer in the +/// future. Note that this struct should not be controlled manually, /// use the `gen` function instead. pub struct Compiler { /// The previous compiler (when compiling nested scopes). enclosing: Option>, /// The current bytecode emission target. - lambda: Lambda, + lambda: Lambda, /// Names of symbols, // symbol_table: Vec, /// The foreign functional interface used to bind values - ffi: FFI, + ffi: FFI, /// The FFI functions that have been bound in this scope. ffi_names: Vec, // determined in hoisting - scope: Scope, + scope: Scope, } impl Compiler { + pub fn compile( + tree: Spanned, + scope: Scope, + ) -> Result, Syntax> { + let ffi = ffi_core(); + let mut compiler = Compiler::base(ffi, scope); + compiler.walk(&tree)?; + return Ok(Rc::new(compiler.lambda)); + } + /// Construct a new `Compiler`. pub fn base(ffi: FFI, scope: Scope) -> Compiler { Compiler { enclosing: None, - lambda: Lambda::empty(), + lambda: Lambda::empty(), ffi, ffi_names: vec![], scope, @@ -88,9 +80,9 @@ impl Compiler { /// keeping a reference to the old one in `self.enclosing`, /// and moving the FFI into the current compiler. pub fn enter_scope(&mut self, scope: Scope) { - let ffi = mem::replace(&mut self.ffi, FFI::new()); - let nested = Compiler::base(ffi, scope); - let enclosing = mem::replace(self, nested); + let ffi = mem::replace(&mut self.ffi, FFI::new()); + let nested = Compiler::base(ffi, scope); + let enclosing = mem::replace(self, nested); self.enclosing = Some(Box::new(enclosing)); } @@ -98,7 +90,7 @@ impl Compiler { /// returning the nested one for data (Lambda) extraction, /// and moving the FFI mappings back into the enclosing compiler. pub fn exit_scope(&mut self) -> Compiler { - let ffi = mem::replace(&mut self.ffi, FFI::new()); + let ffi = mem::replace(&mut self.ffi, FFI::new()); let enclosing = mem::replace(&mut self.enclosing, None); let nested = match enclosing { Some(compiler) => mem::replace(self, *compiler), @@ -109,9 +101,10 @@ impl Compiler { } /// Walks an SST to generate bytecode. - /// At this stage, the SST should've been verified, pruned, typechecked, etc. - /// A malformed SST will cause a panic, as SSTs should be correct at this stage, - /// and for them to be incorrect is an error in the compiler itself. + /// At this stage, the SST should've been verified, pruned, typechecked, + /// etc. A malformed SST will cause a panic, as SSTs should be correct + /// at this stage, and for them to be incorrect is an error in the + /// compiler itself. pub fn walk(&mut self, sst: &Spanned) -> Result<(), Syntax> { // TODO: move this to a better spot self.lambda.decls = self.scope.locals.len(); @@ -121,15 +114,26 @@ impl Compiler { // push left, push right, push center return match sst.item.clone() { - SST::Data(data) => Ok(self.data(data)), - SST::Symbol(unique) => Ok(self.symbol(unique)), - SST::Block(block) => self.block(block), - SST::Label(name, expression) => self.label(name, *expression), - SST::Tuple(tuple) => self.tuple(tuple), - SST::FFI { name, expression } => self.ffi(name, *expression, sst.span.clone()), - SST::Assign { pattern, expression } => self.assign(*pattern, *expression), - SST::Lambda { pattern, expression, scope } => self.lambda(*pattern, *expression, scope), - SST::Call { fun, arg } => self.call(*fun, *arg), + SST::Base(Base::Lit(lit)) => Ok(self.lit(lit)), + SST::Base(Base::Symbol(unique)) => Ok(self.symbol(unique)), + SST::Base(Base::Block(block)) => self.block(block), + // SST::Base(Base::Label(name, expression)) => { + // self.label(name, *expression) + // }, + SST::Base(Base::Label(_)) => todo!(), + SST::Base(Base::Tuple(tuple)) => self.tuple(tuple), + SST::Base(Base::FFI(name, expression)) => { + todo!() + // self.ffi(name, *expression, sst.span.clone()) + }, + SST::Base(Base::Assign(pattern, expression)) => { + self.assign(pattern, *expression) + }, + SST::ScopedLambda(ScopedLambda { arg, body, scope }) => { + self.lambda(arg, *body, scope) + }, + SST::Base(Base::Call(fun, arg)) => self.call(*fun, *arg), + SST::Base(Base::Module(_)) => todo!("need to handle modules"), }; } @@ -139,9 +143,11 @@ impl Compiler { /// Resovles a symbol lookup, e.g. something like `x`. pub fn symbol(&mut self, unique_symbol: UniqueSymbol) { let index = if let Some(i) = self.scope.local_index(unique_symbol) { - self.lambda.emit(Opcode::Load); i + self.lambda.emit(Opcode::Load); + i } else if let Some(i) = self.scope.nonlocal_index(unique_symbol) { - self.lambda.emit(Opcode::LoadCap); i + self.lambda.emit(Opcode::LoadCap); + i } else { // unreachable? todo!(); @@ -151,9 +157,9 @@ impl Compiler { } /// Takes a `Data` leaf and and produces some code to load the constant - pub fn data(&mut self, data: Data) { + pub fn lit(&mut self, lit: Lit) { self.lambda.emit(Opcode::Con); - let mut split = split_number(self.lambda.index_data(data)); + let mut split = split_number(self.lambda.index_data(lit.to_data())); self.lambda.emit_bytes(&mut split); } @@ -161,7 +167,7 @@ impl Compiler { /// Each sup-expression is walked, the last value is left on the stack. pub fn block(&mut self, children: Vec>) -> Result<(), Syntax> { if children.is_empty() { - self.data(Data::Unit); + self.lit(Lit::Unit); return Ok(()); } @@ -187,11 +193,16 @@ impl Compiler { /// Generates a Label construction /// that loads the variant, then wraps some data - pub fn label(&mut self, name: UniqueSymbol, expression: Spanned) -> Result<(), Syntax> { - self.walk(&expression)?; - self.data(Data::Kind(name.0)); - self.lambda.emit(Opcode::Label); - Ok(()) + pub fn label( + &mut self, + name: UniqueSymbol, + expression: Spanned, + ) -> Result<(), Syntax> { + todo!() + // self.walk(&expression)?; + // self.lit(Lit::Kind(name.0)); + // self.lambda.emit(Opcode::Label); + // Ok(()) } /// Generates a Tuple construction @@ -212,11 +223,16 @@ impl Compiler { // TODO: make a macro to map Passerine's data model to Rust's /// Makes a Rust function callable from Passerine, /// by keeping a reference to that function. - pub fn ffi(&mut self, name: String, expression: Spanned, span: Span) -> Result<(), Syntax> { + pub fn ffi( + &mut self, + name: String, + expression: Spanned, + span: Span, + ) -> Result<(), Syntax> { self.walk(&expression)?; - let function = self.ffi.get(&name) - .map_err(|s| Syntax::error(&s, &span))?; + let function = + self.ffi.get(&name).map_err(|s| Syntax::error(&s, &span))?; let index = match self.ffi_names.iter().position(|n| n == &name) { Some(p) => p, @@ -231,7 +247,8 @@ impl Compiler { // and replaces all names/symbols with indicies // before codgen. self.ffi_names.push(name); - self.lambda.add_ffi(function) + todo!("add FFI function") + // self.lambda.add_ffi(function) }, }; @@ -245,9 +262,11 @@ impl Compiler { /// returns true if the variable was declared. pub fn resolve_assign(&mut self, unique_symbol: UniqueSymbol) { let index = if let Some(i) = self.scope.local_index(unique_symbol) { - self.lambda.emit(Opcode::Save); i + self.lambda.emit(Opcode::Save); + i } else if let Some(i) = self.scope.nonlocal_index(unique_symbol) { - self.lambda.emit(Opcode::SaveCap); i + self.lambda.emit(Opcode::SaveCap); + i } else { // unreachable? todo!() @@ -260,23 +279,28 @@ impl Compiler { /// 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, redeclare: bool) { + pub fn destructure( + &mut self, + pattern: Spanned>, + redeclare: bool, + ) { self.lambda.emit_span(&pattern.span); match pattern.item { - SSTPattern::Symbol(unique_symbol) => { + Pattern::Symbol(unique_symbol) => { self.resolve_assign(unique_symbol); }, - SSTPattern::Data(expected) => { - self.data(expected); + Pattern::Lit(expected) => { + self.lit(expected); self.lambda.emit(Opcode::UnData); - } - SSTPattern::Label(name, pattern) => { - self.data(Data::Kind(name.0)); - self.lambda.emit(Opcode::UnLabel); - self.destructure(*pattern, redeclare); - } - SSTPattern::Tuple(tuple) => { + }, + Pattern::Label(name, pattern) => { + todo!() + // self.lit(Lit::Kind(name.0)); + // self.lambda.emit(Opcode::UnLabel); + // self.destructure(*pattern, redeclare); + }, + Pattern::Tuple(tuple) => { for (index, sub_pattern) in tuple.into_iter().enumerate() { self.lambda.emit(Opcode::UnTuple); self.lambda.emit_bytes(&mut split_number(index)); @@ -285,39 +309,42 @@ impl Compiler { // Delete the tuple moved to the top of the stack. self.lambda.emit(Opcode::Del); }, + Pattern::Chain(_) => todo!("handle pattern chains"), } } /// Assign a value to a variable. pub fn assign( &mut self, - pattern: Spanned, - expression: Spanned + pattern: Spanned>, + expression: Spanned, ) -> Result<(), Syntax> { // eval the expression self.walk(&expression)?; self.destructure(pattern, false); - self.data(Data::Unit); + self.lit(Lit::Unit); Ok(()) } /// Recursively compiles a lambda declaration in a new scope. pub fn lambda( &mut self, - pattern: Spanned, + pattern: Spanned>, expression: Spanned, scope: Scope, ) -> Result<(), Syntax> { // build a list of captures at the boundary let mut captures = vec![]; - for nonlocal in scope.nonlocals.iter() { + for nonlocal in scope.nonlocals.items().iter() { let captured = if self.scope.is_local(*nonlocal) { let index = self.scope.local_index(*nonlocal).unwrap(); self.lambda.emit(Opcode::Capture); self.lambda.emit_bytes(&mut split_number(index)); Captured::Local(index) } else { - Captured::Nonlocal(self.scope.nonlocal_index(*nonlocal).unwrap()) + Captured::Nonlocal( + self.scope.nonlocal_index(*nonlocal).unwrap(), + ) }; captures.push(captured); } @@ -336,21 +363,28 @@ impl Compiler { // return the result self.lambda.emit(Opcode::Return); - self.lambda.emit_bytes(&mut split_number(self.scope.locals.len())); + self.lambda + .emit_bytes(&mut split_number(self.scope.locals.len())); } let lambda = self.exit_scope().lambda; // push the lambda object onto the callee's stack. - let lambda_index = self.lambda.index_data(Data::Lambda(Rc::new(lambda))); - self.lambda.emit(Opcode::Closure); - self.lambda.emit_bytes(&mut split_number(lambda_index)); + todo!("insert lambda as data"); + // let lambda_index = + // self.lambda.index_lit(Data::Lambda(Rc::new(lambda))); + // self.lambda.emit(Opcode::Closure); + // self.lambda.emit_bytes(&mut split_number(lambda_index)); Ok(()) } /// When a function is called, the top two items are taken off the stack, /// The topmost item is expected to be a function. - pub fn call(&mut self, fun: Spanned, arg: Spanned) -> Result<(), Syntax> { + pub fn call( + &mut self, + fun: Spanned, + arg: Spanned, + ) -> Result<(), Syntax> { self.walk(&arg)?; self.walk(&fun)?; @@ -360,56 +394,4 @@ impl Compiler { } } -#[cfg(test)] -mod test { - use super::*; - use crate::construct::module::ThinModule; - use crate::common::source::Source; - - #[test] - fn constants() { - let source = Source::source("heck = true; lol = 0.0; lmao = false; eyy = \"GOod MoRNiNg, SiR\""); - let lambda = ThinModule::thin(source) - .lower().unwrap() - .lower().unwrap() - .lower().unwrap() - .lower().unwrap() - .lower().unwrap(); - - let result = vec![ - Data::Boolean(true), - Data::Unit, // from assignment - Data::Float(0.0), - Data::Boolean(false), - Data::String("GOod MoRNiNg, SiR".to_string()), - ]; - - assert_eq!(lambda.constants, result); - } - - #[test] - fn bytecode() { - let source = Source::source("heck = true; lol = heck; lmao = false"); - let lambda = ThinModule::thin(source) - .lower().unwrap() - .lower().unwrap() - .lower().unwrap() - .lower().unwrap() - .lower().unwrap(); - - let result = vec![ - (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, - (Opcode::Con as u8), 129, (Opcode::Del as u8), // load unit, delete - (Opcode::Con as u8), 130, (Opcode::Save as u8), 130, // con false, save to lmao - (Opcode::Con as u8), 129, // load unit - ]; - - assert_eq!(result, lambda.code); - } - - // NOTE: instead of veryfying bytecode output, - // write a test in vm::vm::test - // and check behaviour that way -} +// TODO: tests diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs index 46354a2..07ecb76 100644 --- a/src/compiler/mod.rs +++ b/src/compiler/mod.rs @@ -24,6 +24,6 @@ pub mod parse; // pub mod desugar; pub mod hoist; // pub mod unify; -// pub mod gen; +pub mod gen; pub mod syntax; diff --git a/src/construct/scope.rs b/src/construct/scope.rs index 4f16ae6..e561c13 100644 --- a/src/construct/scope.rs +++ b/src/construct/scope.rs @@ -52,6 +52,8 @@ impl VecSet { .map(|x| x.clone()) .collect() } + + pub fn len(&self) -> usize { self.members.len() } } #[derive(Debug, Clone, PartialEq)] diff --git a/src/core/control.rs b/src/core/control.rs index ee347c3..5b04bab 100644 --- a/src/core/control.rs +++ b/src/core/control.rs @@ -1,5 +1,7 @@ -use crate::common::data::Data; -use crate::core::extract::triop; +use crate::{ + core::extract::triop, + vm::data::Data, +}; /// An implementation of an if statement, as an FFI. /// Interesting idea, not sure if I'm going to keep it. @@ -11,7 +13,6 @@ pub fn if_choice(data: Data) -> Result { Err("\ Expected the condition to be a boolean.\n\ Note that Passerine does not have a notion of truthiness." - .to_string() - ) + .to_string()) } } diff --git a/src/core/extract.rs b/src/core/extract.rs index d975a6e..f915abe 100644 --- a/src/core/extract.rs +++ b/src/core/extract.rs @@ -1,4 +1,4 @@ -use crate::common::data::Data; +use crate::vm::data::Data; // TODO: macro for data extraction? // TODO: generalize binop/triop? @@ -16,7 +16,9 @@ pub fn binop(data: Data) -> (Data, Data) { /// A Rust tuple of three items. pub fn triop(data: Data) -> (Data, Data, Data) { match data { - Data::Tuple(t) if t.len() == 3 => (t[0].clone(), t[1].clone(), t[2].clone()), + Data::Tuple(t) if t.len() == 3 => { + (t[0].clone(), t[1].clone(), t[2].clone()) + }, _ => unreachable!("bad data layout passed to ffi"), } } diff --git a/src/core/ffi.rs b/src/core/ffi.rs index 67a21cc..71c30c9 100644 --- a/src/core/ffi.rs +++ b/src/core/ffi.rs @@ -1,8 +1,9 @@ use std::{ - rc::Rc, collections::HashMap, + rc::Rc, }; -use crate::common::data::Data; + +use crate::vm::data::Data; // TODO: have FFI function keep track of number of arguments // it takes, so this invariant can be checket at compile time? @@ -14,14 +15,14 @@ use crate::common::data::Data; pub struct FFIFunction(Rc Result>); impl FFIFunction { - pub fn new(function: Box Result>) -> FFIFunction { + pub fn new( + function: Box Result>, + ) -> FFIFunction { FFIFunction(Rc::new(function)) } #[inline] - pub fn call(&self, data: Data) -> Result { - (self.0)(data) - } + pub fn call(&self, data: Data) -> Result { (self.0)(data) } } impl std::fmt::Debug for FFIFunction { @@ -31,9 +32,7 @@ impl std::fmt::Debug for FFIFunction { } impl PartialEq for FFIFunction { - fn eq(&self, _other: &FFIFunction) -> bool { - return false; - } + fn eq(&self, _other: &FFIFunction) -> bool { return false; } } /// A foreign functional interface, mapping names to functions, @@ -46,14 +45,19 @@ pub struct FFI(HashMap); impl FFI { /// Creates a new empty Foreign Functional Interface. - pub fn new() -> FFI { - FFI(HashMap::new()) - } + pub fn new() -> FFI { FFI(HashMap::new()) } /// Returns true if the function has already been added to the `FFI`. - pub fn add(&mut self, name: &str, function: FFIFunction) -> Result<(), String> { + pub fn add( + &mut self, + name: &str, + function: FFIFunction, + ) -> Result<(), String> { match self.0.insert(name.to_string(), function) { - Some(_) => Err(format!("The ffi function '{}' has already been defined", name)), + Some(_) => Err(format!( + "The ffi function '{}' has already been defined", + name + )), None => Ok(()), } } @@ -85,6 +89,6 @@ impl FFI { Ok(()) } else { Err(mismatches) - } + }; } } diff --git a/src/core/io.rs b/src/core/io.rs index 614d0e6..1661988 100644 --- a/src/core/io.rs +++ b/src/core/io.rs @@ -1,4 +1,4 @@ -use crate::common::data::Data; +use crate::vm::data::Data; /// Prints some data to stdout with a trailing newline. pub fn println(data: Data) -> Result { diff --git a/src/core/logic.rs b/src/core/logic.rs index 401e249..67aab86 100644 --- a/src/core/logic.rs +++ b/src/core/logic.rs @@ -1,10 +1,12 @@ -use crate::common::data::Data; -use crate::core::extract::binop; +use crate::{ + core::extract::binop, + vm::data::Data, +}; // TODO: implement equality rather than just deriving PartialEq on Data. -// Rust hit it right on the nose with the difference between equality and partial equality -// TODO: equality vs partial equality in passerine? +// Rust hit it right on the nose with the difference between equality and +// partial equality TODO: equality vs partial equality in passerine? /// Returns `true` if the `Data` are equal, false otherwise. pub fn equal(data: Data) -> Result { @@ -15,7 +17,7 @@ pub fn equal(data: Data) -> Result { pub fn greater(data: Data) -> Result { // TODO: type coercion let result = match binop(data) { - (Data::Float(left), Data::Float(right)) => left > right, + (Data::Float(left), Data::Float(right)) => left > right, (Data::Integer(left), Data::Integer(right)) => left > right, _ => Err("Expected two numbers of the same type")?, }; @@ -26,7 +28,7 @@ pub fn greater(data: Data) -> Result { pub fn less(data: Data) -> Result { // TODO: type coercion let result = match binop(data) { - (Data::Float(left), Data::Float(right)) => left < right, + (Data::Float(left), Data::Float(right)) => left < right, (Data::Integer(left), Data::Integer(right)) => left < right, _ => Err("Expected two numbers of the same type")?, }; @@ -37,7 +39,7 @@ pub fn less(data: Data) -> Result { pub fn greater_equal(data: Data) -> Result { // TODO: type coercion let result = match binop(data) { - (Data::Float(left), Data::Float(right)) => left >= right, + (Data::Float(left), Data::Float(right)) => left >= right, (Data::Integer(left), Data::Integer(right)) => left >= right, _ => Err("Expected two numbers of the same type")?, }; @@ -48,7 +50,7 @@ pub fn greater_equal(data: Data) -> Result { pub fn less_equal(data: Data) -> Result { // TODO: type coercion let result = match binop(data) { - (Data::Float(left), Data::Float(right)) => left <= right, + (Data::Float(left), Data::Float(right)) => left <= right, (Data::Integer(left), Data::Integer(right)) => left <= right, _ => Err("Expected two numbers of the same type")?, }; diff --git a/src/core/math.rs b/src/core/math.rs index a9dc34c..a31c951 100644 --- a/src/core/math.rs +++ b/src/core/math.rs @@ -1,12 +1,16 @@ -use crate::common::data::Data; -use crate::core::extract::binop; +use crate::{ + core::extract::binop, + vm::data::Data, +}; /// Adds two numbers, concatenates two strings. pub fn add(data: Data) -> Result { let result = match binop(data) { - (Data::Float(l), Data::Float(r)) => Data::Float(l + r), + (Data::Float(l), Data::Float(r)) => Data::Float(l + r), (Data::Integer(l), Data::Integer(r)) => Data::Integer(l + r), - (Data::String(l), Data::String(r)) => Data::String(format!("{}{}", l, r)), + (Data::String(l), Data::String(r)) => { + Data::String(format!("{}{}", l, r)) + }, _ => Err("Addition between unsupported datatypes")?, }; @@ -16,7 +20,7 @@ pub fn add(data: Data) -> Result { /// Subtraction between two numbers. pub fn sub(data: Data) -> Result { let result = match binop(data) { - (Data::Float(l), Data::Float(r)) => Data::Float(l - r), + (Data::Float(l), Data::Float(r)) => Data::Float(l - r), (Data::Integer(l), Data::Integer(r)) => Data::Integer(l - r), _ => Err("Subtraction between unsupported datatypes")?, }; @@ -27,7 +31,7 @@ pub fn sub(data: Data) -> Result { /// Negation of a numbers. pub fn neg(data: Data) -> Result { let result = match data { - Data::Float(n) => Data::Float(-n), + Data::Float(n) => Data::Float(-n), Data::Integer(n) => Data::Integer(-n), _ => Err("Subtraction between unsupported datatypes")?, }; @@ -38,7 +42,7 @@ pub fn neg(data: Data) -> Result { /// Multiplication between two numbers. pub fn mul(data: Data) -> Result { let result = match binop(data) { - (Data::Float(l), Data::Float(r)) => Data::Float(l * r), + (Data::Float(l), Data::Float(r)) => Data::Float(l * r), (Data::Integer(l), Data::Integer(r)) => Data::Integer(l * r), _ => Err("Multiplication between unsupported datatypes")?, }; @@ -50,9 +54,13 @@ pub fn mul(data: Data) -> Result { /// Raises a runtime error if there is a division by zero. pub fn div(data: Data) -> Result { let result = match binop(data) { - (Data::Float(_), Data::Float(n)) if n == 0.0 => Err("Division by zero")?, + (Data::Float(_), Data::Float(n)) if n == 0.0 => { + Err("Division by zero")? + }, (Data::Float(l), Data::Float(r)) => Data::Float(l / r), - (Data::Integer(_), Data::Integer(n)) if n == 0 => Err("Division by zero")?, + (Data::Integer(_), Data::Integer(n)) if n == 0 => { + Err("Division by zero")? + }, (Data::Integer(l), Data::Integer(r)) => Data::Integer(l / r), _ => Err("Division between unsupported datatypes")?, }; @@ -64,9 +72,13 @@ pub fn div(data: Data) -> Result { /// Raises a runtime error if there is a division by zero. pub fn rem(data: Data) -> Result { let result = match binop(data) { - (Data::Float(_), Data::Float(r)) if r == 0.0 => Err("Division by zero")?, - (Data::Float(l), Data::Float(r)) => Data::Float(l.rem_euclid(r)), - (Data::Integer(_), Data::Integer(n)) if n == 0 => Err("Division by zero")?, + (Data::Float(_), Data::Float(r)) if r == 0.0 => { + Err("Division by zero")? + }, + (Data::Float(l), Data::Float(r)) => Data::Float(l.rem_euclid(r)), + (Data::Integer(_), Data::Integer(n)) if n == 0 => { + Err("Division by zero")? + }, (Data::Integer(l), Data::Integer(r)) => Data::Integer(l.rem_euclid(r)), _ => Err("Division between unsupported datatypes")?, }; @@ -77,10 +89,10 @@ pub fn rem(data: Data) -> Result { /// Number to a power pub fn pow(data: Data) -> Result { let result = match binop(data) { - (Data::Float(l), Data::Float(r)) => Data::Float(l.powf(r)), + (Data::Float(l), Data::Float(r)) => Data::Float(l.powf(r)), (Data::Integer(l), Data::Integer(r)) => Data::Integer(l.pow(r as u32)), _ => Err("Exponentiation between unsupported datatypes")?, }; return Ok(result); -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 168de38..1c606f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,13 +46,13 @@ //! > NOTE: For a more detail, read through the documentation //! for any of the components mentioned. //! -//! Within the compiler pipeline, source code is represented as a `Source` object. -//! A source is a reference to some code, with an associated path +//! Within the compiler pipeline, source code is represented as a `Source` +//! object. A source is a reference to some code, with an associated path //! telling which file it came from. //! //! Regions of source code can be marked with `Span`s, -//! Which are like `&strs` but with a reference-counted reference to the original `Source`, -//! methods for combining them, and so on. +//! Which are like `&strs` but with a reference-counted reference to the +//! original `Source`, methods for combining them, and so on. //! Spans are used throughout the compiler when reporting errors. //! Compiler Datastructures can be `Spanned` to indicate where they originated. //! @@ -63,9 +63,9 @@ //! which can be pretty-printed. //! //! The first phase of compilation is lexing. -//! The `Lexer` reads through a source, and produces a stream of `Spanned`s. -//! The `Lexer` is super simple - it greedily looks for the longest next token, -//! Then consumes it and advances by the token's length. +//! The `Lexer` reads through a source, and produces a stream of +//! `Spanned`s. The `Lexer` is super simple - it greedily looks for the +//! longest next token, Then consumes it and advances by the token's length. //! To lex a file, use the `compiler::lex::lex` function. //! //! The next phase of compilation is parsing. @@ -101,7 +101,7 @@ //! The `VM` is just a simple light stack-based VM. pub mod common; -// pub mod core; +pub mod core; pub mod compiler; pub mod vm; pub mod construct; @@ -109,9 +109,13 @@ pub mod construct; // exported functions: // TODO: clean up exports -pub use common::{source::Source, span::Spanned}; -pub use compiler::syntax::Syntax; use std::rc::Rc; + +pub use common::{ + source::Source, + span::Spanned, +}; +pub use compiler::syntax::Syntax; // pub use crate::core::ffi::FFI; // pub use vm::{vm::VM, trace::Trace}; @@ -128,8 +132,8 @@ use std::rc::Rc; // // /// Compiles a [`Source`] to some bytecode, // /// With a specific [`FFI`]. -// pub fn compile_with_ffi(source: Rc, ffi: FFI) -> Result { -// let tokens = ThinModule::thin(source).lower()?; +// pub fn compile_with_ffi(source: Rc, ffi: FFI) -> Result { let tokens = ThinModule::thin(source).lower()?; // let ast = tokens.lower()?; // let cst = ast.lower()?; // let sst = cst.lower()?; diff --git a/src/vm/vm.rs b/src/vm/vm.rs index 8ad7c0d..1d61c4d 100644 --- a/src/vm/vm.rs +++ b/src/vm/vm.rs @@ -1,17 +1,25 @@ use std::mem; -use crate::common::{ - lambda::Captured, number::build_number, opcode::Opcode, span::Span, -}; - -use crate::vm::{ - closure::Closure, data::Data, slot::Suspend, stack::Stack, trace::Trace, +use crate::{ + common::{ + lambda::Captured, + number::build_number, + opcode::Opcode, + span::Span, + }, + vm::{ + closure::Closure, + data::Data, + slot::Suspend, + stack::Stack, + trace::Trace, + }, }; // TODO: algebraic effects // more than just Trace, Runtime - mechanism for raising effects -// fiber scheduling environment handles FFI, no more holding refs to rust functions. -// TODO: convert VM to Fiber +// fiber scheduling environment handles FFI, no more holding refs to rust +// functions. TODO: convert VM to Fiber /// A `VM` executes bytecode lambda closures. /// (That's a mouthful - think bytecode + some context). @@ -21,15 +29,16 @@ use crate::vm::{ #[derive(Debug)] pub struct VM { pub closure: Closure, - pub stack: Stack, - pub ip: usize, + pub stack: Stack, + pub ip: usize, } // NOTE: use Opcode::same and Opcode.to_byte() rather than actual bytes -// Don't worry, the compiler *should* get rid of this overhead and just use bytes +// Don't worry, the compiler *should* get rid of this overhead and just use +// bytes -// this impl contains initialization, helper functions, and the core interpreter loop -// the next impl contains opcode implementations +// this impl contains initialization, helper functions, and the core interpreter +// loop the next impl contains opcode implementations impl VM { /// Initialize a new VM. /// To run the VM, a lambda must be passed to it through `run`. @@ -39,15 +48,13 @@ impl VM { stack: Stack::init(), ip: 0, }; - vm.stack.declare(vm.closure.lambda.decls.len()); + vm.stack.declare(vm.closure.lambda.decls); return vm; } /// Advances to the next instruction. #[inline] - pub fn next(&mut self) { - self.ip += 1; - } + pub fn next(&mut self) { self.ip += 1; } /// Advances IP, returns `Ok`. Used in Bytecode implementations. #[inline] pub fn done(&mut self) -> Result<(), Trace> { @@ -56,9 +63,7 @@ impl VM { } /// Returns the current instruction as a byte. #[inline] - pub fn peek_byte(&mut self) -> u8 { - self.closure.lambda.code[self.ip] - } + pub fn peek_byte(&mut self) -> u8 { self.closure.lambda.code[self.ip] } /// Advances IP and returns the current instruction as a byte. #[inline] pub fn next_byte(&mut self) -> u8 { @@ -132,8 +137,8 @@ impl VM { } /// Suspends the current lambda and runs a new one on the VM. - /// Runs until either success, in which it restores the state of the previous lambda, - /// Or failure, in which it returns the runtime error. + /// Runs until either success, in which it restores the state of the + /// previous lambda, Or failure, in which it returns the runtime error. /// In the future, fibers will allow for error handling - /// right now, error in Passerine are practically panics. pub fn run(&mut self) -> Result<(), Trace> { @@ -373,7 +378,8 @@ impl VM { self.done() } - /// Call a function on the top of the stack, passing the next value as an argument. + /// Call a function on the top of the stack, passing the next value as an + /// argument. pub fn call(&mut self) -> Result<(), Trace> { // get the function and argument to run let fun = match self.stack.pop_data() { @@ -410,7 +416,7 @@ impl VM { let old_closure = mem::replace(&mut self.closure, fun); let old_ip = mem::replace(&mut self.ip, 0); let suspend = Suspend { - ip: old_ip, + ip: old_ip, closure: old_closure, }; @@ -423,7 +429,7 @@ impl VM { // set up the stack for the function call // self.stack.push_frame(suspend); - self.stack.declare(self.closure.lambda.decls.len()); + self.stack.declare(self.closure.lambda.decls); self.stack.push_data(arg); // println!("{}", self.closure.lambda); @@ -467,7 +473,7 @@ impl VM { let mut closure = Closure::wrap(lambda); for captured in closure.lambda.captures.iter() - /* .rev */ + // .rev { let reference = match captured { Captured::Local(index) => match self.stack.local_data(*index) { @@ -537,16 +543,16 @@ impl VM { // // #[test] // fn functions() { -// let mut vm = inspect("iden = x -> x; y = true; iden ({ y = false; iden iden } (iden y))"); -// let identity = vm.stack.pop_data(); +// let mut vm = inspect("iden = x -> x; y = true; iden ({ y = false; +// iden iden } (iden y))"); let identity = vm.stack.pop_data(); // assert_eq!(identity, Data::Boolean(true)); // } // // #[test] // fn fun_scope() { // // y = (x -> { y = x; y ) 7.0; y -// let mut vm = inspect("one = 1.0\npi = 3.14\ne = 2.72\n\nx = w -> pi\nx 37.6"); -// let pi = vm.stack.pop_data(); +// let mut vm = inspect("one = 1.0\npi = 3.14\ne = 2.72\n\nx = w -> +// pi\nx 37.6"); let pi = vm.stack.pop_data(); // assert_eq!(pi, Data::Float(3.14)); // } // @@ -581,8 +587,8 @@ impl VM { // } // // // TODO: figure out how to make the following passerine code into a test -// // without entering into an infinite loop (which is the intended behaviour) -// // maybe try running it a large number of times, +// // without entering into an infinite loop (which is the intended +// behaviour) // maybe try running it a large number of times, // // and check the size of the stack? // // loop = () // // loop = y -> x -> {