Add support for defaults to bytecode, compiler and VM

This commit is contained in:
Daniel Watkins
2018-08-27 22:16:51 -04:00
parent 0787115d07
commit 2cecd362fe
9 changed files with 113 additions and 25 deletions

9
Cargo.lock generated
View File

@@ -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"

View File

@@ -95,12 +95,12 @@ pub enum Statement {
ClassDef {
name: String,
body: Vec<LocatedStatement>,
args: Vec<String>,
args: Vec<(String, Option<Expression>)>,
// TODO: docstring: String,
},
FunctionDef {
name: String,
args: Vec<String>,
args: Vec<(String, Option<Expression>)>,
// docstring: String,
body: Vec<LocatedStatement>,
},
@@ -161,7 +161,7 @@ pub enum Expression {
name: String,
},
Lambda {
args: Vec<String>,
args: Vec<(String, Option<Expression>)>,
body: Box<Expression>,
},
True,

View File

@@ -308,12 +308,16 @@ FuncDef: ast::LocatedStatement = {
},
};
Parameters: Vec<String> = {
Parameters: Vec<(String, Option<ast::Expression>)> = {
"(" <a: TypedArgsList> ")" => a,
};
TypedArgsList: Vec<String> = {
<a: Comma<Identifier>> => a,
// parameters are (String, None), kwargs are (String, Some(Test)) where Test is
// the default
TypedArgsList: Vec<(String, Option<ast::Expression>)> = {
<a: Comma<Identifier>> => {
a.iter().map(|param| (param.clone(), None)).collect()
},
};
ClassDef: ast::LocatedStatement = {

View File

@@ -4,6 +4,7 @@ version = "0.1.0"
authors = ["Shing Lyu <shing.lyu@gmail.com>"]
[dependencies]
bitflags = "1.0.4"
log = "0.3"
rustpython_parser = {path = "../parser"}
serde = "1.0.66"

View File

@@ -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,
},

View File

@@ -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(),
});
}
}
}

View File

@@ -1,4 +1,6 @@
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate log;
// extern crate env_logger;
extern crate serde;

View File

@@ -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!("<class '{}'>", name),
PyObjectKind::Instance { dict: _ } => format!("<instance>"),
PyObjectKind::Code { code: _ } => format!("<code>"),
PyObjectKind::Function { code: _, scope: _ } => format!("<func>"),
PyObjectKind::Function { .. } => format!("<func>"),
PyObjectKind::BoundMethod { .. } => format!("<bound-method>"),
PyObjectKind::RustFunction { function: _ } => format!("<rustfunc>"),
PyObjectKind::Module { ref name, dict: _ } => format!("<module '{}'>", name),

View File

@@ -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
}