forked from Rust-related/RustPython
Add support for defaults to bytecode, compiler and VM
This commit is contained in:
9
Cargo.lock
generated
9
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
// extern crate env_logger;
|
||||
extern crate serde;
|
||||
|
||||
@@ -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),
|
||||
|
||||
45
vm/src/vm.rs
45
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user