diff --git a/Cargo.lock b/Cargo.lock index 16e8a0999e..8903683732 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,7 +62,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bitflags" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -91,7 +91,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -160,7 +160,7 @@ name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -466,6 +466,7 @@ dependencies = [ name = "rustpython_vm" version = "0.1.0" dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "rustpython_parser 0.0.1", "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", @@ -703,7 +704,7 @@ dependencies = [ "checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" "checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" -"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" diff --git a/parser/src/ast.rs b/parser/src/ast.rs index b71f68e9bf..38318093a8 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -95,12 +95,12 @@ pub enum Statement { ClassDef { name: String, body: Vec, - args: Vec, + args: Vec<(String, Option)>, // TODO: docstring: String, }, FunctionDef { name: String, - args: Vec, + args: Vec<(String, Option)>, // docstring: String, body: Vec, }, @@ -161,7 +161,7 @@ pub enum Expression { name: String, }, Lambda { - args: Vec, + args: Vec<(String, Option)>, body: Box, }, True, diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop index 644ef6f1cf..cb6b68489d 100644 --- a/parser/src/python.lalrpop +++ b/parser/src/python.lalrpop @@ -308,12 +308,16 @@ FuncDef: ast::LocatedStatement = { }, }; -Parameters: Vec = { +Parameters: Vec<(String, Option)> = { "(" ")" => a, }; -TypedArgsList: Vec = { - > => a, +// parameters are (String, None), kwargs are (String, Some(Test)) where Test is +// the default +TypedArgsList: Vec<(String, Option)> = { + > => { + a.iter().map(|param| (param.clone(), None)).collect() + }, }; ClassDef: ast::LocatedStatement = { diff --git a/vm/Cargo.toml b/vm/Cargo.toml index ce702b42be..0ce4293859 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Shing Lyu "] [dependencies] +bitflags = "1.0.4" log = "0.3" rustpython_parser = {path = "../parser"} serde = "1.0.66" diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 8d3d0dde21..595bc53009 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -37,6 +37,12 @@ impl CodeObject { } } +bitflags! { + pub struct FunctionOpArg: u8 { + const HAS_DEFAULTS = 0x01; + } +} + pub type Label = usize; #[derive(Debug, Clone, PartialEq)] @@ -88,7 +94,9 @@ pub enum Instruction { JumpIfFalse { target: Label, }, - MakeFunction, + MakeFunction { + flags: FunctionOpArg, + }, CallFunction { count: usize, }, diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 35fef828ed..ab1e2d5507 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -350,8 +350,22 @@ impl Compiler { } ast::Statement::FunctionDef { name, args, body } => { // Create bytecode for this function: - self.code_object_stack - .push(CodeObject::new(args.to_vec(), None)); + let mut names = vec![]; + let mut default_elements = vec![]; + + for (name, default) in args { + names.push(name.clone()); + if let Some(default) = default { + default_elements.push(default.clone()); + } + } + + let have_kwargs = default_elements.len() > 0; + self.compile_expression(&ast::Expression::Tuple { + elements: default_elements, + }); + + self.code_object_stack.push(CodeObject::new(names, None)); self.compile_statements(body); // Emit None at end: @@ -371,7 +385,11 @@ impl Compiler { }); // Turn code object into function object: - self.emit(Instruction::MakeFunction); + let mut flags = bytecode::FunctionOpArg::empty(); + if have_kwargs { + flags = flags | bytecode::FunctionOpArg::HAS_DEFAULTS; + } + self.emit(Instruction::MakeFunction { flags: flags }); self.emit(Instruction::StoreName { name: name.to_string(), }); @@ -400,7 +418,9 @@ impl Compiler { }, }); // Turn code object into function object: - self.emit(Instruction::MakeFunction); + self.emit(Instruction::MakeFunction { + flags: bytecode::FunctionOpArg::empty(), + }); self.emit(Instruction::LoadConst { value: bytecode::Constant::String { @@ -409,7 +429,9 @@ impl Compiler { }); for base in args { - self.emit(Instruction::LoadName { name: base.clone() }); + self.emit(Instruction::LoadName { + name: base.0.clone(), + }); } self.emit(Instruction::CallFunction { count: 2 + args.len(), @@ -720,8 +742,10 @@ impl Compiler { }); } ast::Expression::Lambda { args, body } => { - self.code_object_stack - .push(CodeObject::new(args.to_vec(), None)); + self.code_object_stack.push(CodeObject::new( + args.iter().map(|(name, _default)| name.clone()).collect(), + None, + )); self.compile_expression(body); self.emit(Instruction::ReturnValue); let code = self.code_object_stack.pop().unwrap(); @@ -734,7 +758,9 @@ impl Compiler { }, }); // Turn code object into function object: - self.emit(Instruction::MakeFunction); + self.emit(Instruction::MakeFunction { + flags: bytecode::FunctionOpArg::empty(), + }); } } } diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 4def7ad50c..ed0248694e 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -1,4 +1,6 @@ #[macro_use] +extern crate bitflags; +#[macro_use] extern crate log; // extern crate env_logger; extern crate serde; diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 036cc877e0..10e7eeac16 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -267,11 +267,17 @@ impl PyContext { ) } - pub fn new_function(&self, code_obj: PyObjectRef, scope: PyObjectRef) -> PyObjectRef { + pub fn new_function( + &self, + code_obj: PyObjectRef, + scope: PyObjectRef, + defaults: PyObjectRef, + ) -> PyObjectRef { PyObject::new( PyObjectKind::Function { code: code_obj, scope: scope, + defaults: defaults, }, self.function_type(), ) @@ -566,6 +572,7 @@ pub enum PyObjectKind { Function { code: PyObjectRef, scope: PyObjectRef, + defaults: PyObjectRef, }, BoundMethod { function: PyObjectRef, @@ -613,7 +620,7 @@ impl fmt::Debug for PyObjectKind { } => write!(f, "slice"), &PyObjectKind::NameError { name: _ } => write!(f, "NameError"), &PyObjectKind::Code { ref code } => write!(f, "code: {:?}", code), - &PyObjectKind::Function { code: _, scope: _ } => write!(f, "function"), + &PyObjectKind::Function { .. } => write!(f, "function"), &PyObjectKind::BoundMethod { ref function, ref object, @@ -683,7 +690,7 @@ impl PyObject { } => format!("", name), PyObjectKind::Instance { dict: _ } => format!(""), PyObjectKind::Code { code: _ } => format!(""), - PyObjectKind::Function { code: _, scope: _ } => format!(""), + PyObjectKind::Function { .. } => format!(""), PyObjectKind::BoundMethod { .. } => format!(""), PyObjectKind::RustFunction { function: _ } => format!(""), PyObjectKind::Module { ref name, dict: _ } => format!("", name), diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 8747a33cf9..b1fee6a64a 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -516,10 +516,44 @@ impl VirtualMachine { PyObjectKind::Function { ref code, ref scope, + ref defaults, } => { let mut scope = self.ctx.new_scope(Some(scope.clone())); let code_object = copy_code(code.clone()); - for (name, value) in code_object.arg_names.iter().zip(args.args) { + let nargs = args.args.len(); + let nexpected_args = code_object.arg_names.len(); + let args = if nargs > nexpected_args { + return Err(self.new_type_error(format!( + "Expected {} arguments (got: {})", + nexpected_args, nargs + ))); + } else if nargs < nexpected_args { + // Use defaults if available + let nrequired_defaults = nexpected_args - nargs; + let available_defaults = match defaults.borrow().kind { + PyObjectKind::Tuple { ref elements } => elements.clone(), + PyObjectKind::None => vec![], + _ => panic!("function defaults not tuple or None"), + }; + if nrequired_defaults > available_defaults.len() { + return Err(self.new_type_error(format!( + "Expected {}-{} arguments (got: {})", + nexpected_args - available_defaults.len(), + nexpected_args, + nargs + ))); + } + let default_args = available_defaults + .clone() + .split_off(available_defaults.len() - nrequired_defaults); + let mut new_args = args.args.clone(); + new_args.extend(default_args); + new_args + } else { + // nargs == nexpected_args + args.args + }; + for (name, value) in code_object.arg_names.iter().zip(args) { scope.set_item(name, value); } let frame = Frame::new(code.clone(), scope); @@ -771,13 +805,18 @@ impl VirtualMachine { }; None } - bytecode::Instruction::MakeFunction => { + bytecode::Instruction::MakeFunction { flags } => { let _qualified_name = self.pop_value(); let code_obj = self.pop_value(); + let defaults = if flags.contains(bytecode::FunctionOpArg::HAS_DEFAULTS) { + self.pop_value() + } else { + self.get_none() + }; // pop argc arguments // argument: name, args, globals let scope = self.current_frame().locals.clone(); - let obj = self.ctx.new_function(code_obj, scope); + let obj = self.ctx.new_function(code_obj, scope, defaults); self.push_value(obj); None }