Skip to content

Commit e380b9c

Browse files
committed
Add support for keyword arguments and **kwargs.
1 parent b8dd10b commit e380b9c

File tree

6 files changed

+136
-20
lines changed

6 files changed

+136
-20
lines changed

functional_tests/arguments.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
def f(foo, bar, *baz, qux, quux, fiz='buz', **kwargs):
2+
print('foo:', foo)
3+
print('bar:', bar)
4+
for x in baz:
5+
print('baz:', x)
6+
print('qux:', qux)
7+
print('quux:', quux)
8+
print('fiz:', fiz)
9+
# TODO: print kwargs
10+
print('--')
11+
12+
f(1, 2, qux=3, quux=4)
13+
f(1, 2, 3, qux=4, quux=5)
14+
f(1, 2, 3, 21, qux=4, quux=5)
15+
f(1, 2, qux=3, quux=4, fiz=5)
16+
f(1, 2, 3, qux=4, quux=5, fiz=6)
17+
f(1, 2, 3, 21, qux=4, quux=5, fiz=6)

src/marshal/decode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ pub fn read_object<R: io::Read>(reader: &mut R, store: &mut ObjectStore, primiti
224224
let lnotab = try!(read_object(reader, store, primitive_objects, references)); // TODO: decode this
225225
let code = Code {
226226
argcount: argcount as usize,
227-
kwonlyargcount: kwonlyargcount,
227+
kwonlyargcount: kwonlyargcount as usize,
228228
nlocals: nlocals,
229229
stacksize: stacksize,
230230
flags: flags,

src/objects/mod.rs

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
extern crate itertools;
22
use std::collections::HashMap;
3+
use std::collections::HashSet;
34
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
45
use std::fmt;
56
use self::itertools::Itertools;
@@ -12,7 +13,7 @@ use super::sandbox::EnvProxy;
1213
#[derive(Eq)]
1314
pub struct Code {
1415
pub argcount: usize,
15-
pub kwonlyargcount: u32,
16+
pub kwonlyargcount: usize,
1617
pub nlocals: u32,
1718
pub stacksize: u32,
1819
pub flags: u32,
@@ -32,6 +33,36 @@ impl Code {
3233
pub fn co_varargs(&self) -> bool {
3334
self.flags & 0x4 != 0
3435
}
36+
pub fn co_varkwargs(&self) -> bool {
37+
self.flags & 0x8 != 0
38+
}
39+
pub fn get_varargs_name(&self) -> Option<&String> {
40+
if self.co_varargs() {
41+
Some(self.varnames.get(self.argcount+self.kwonlyargcount).unwrap())
42+
}
43+
else {
44+
None
45+
}
46+
}
47+
pub fn get_varkwargs_name(&self) -> Option<&String> {
48+
if self.co_varkwargs() {
49+
let mut index = self.argcount+self.kwonlyargcount;
50+
if self.co_varkwargs() {
51+
index += 1;
52+
}
53+
Some(self.varnames.get(index).unwrap())
54+
}
55+
else {
56+
None
57+
}
58+
}
59+
pub fn keywords(&self) -> HashSet<&String> {
60+
let mut res = HashSet::new();
61+
for name in self.varnames[self.argcount..(self.argcount+self.kwonlyargcount)].iter() {
62+
res.insert(name);
63+
}
64+
res
65+
}
3566
}
3667

3768
#[derive(Debug)]
@@ -48,9 +79,10 @@ pub enum ObjectContent {
4879
List(Vec<ObjectRef>),
4980
Code(Box<Code>),
5081
Set(Vec<ObjectRef>),
82+
Dict(Vec<(ObjectRef, ObjectRef)>),
5183
FrozenSet(Vec<ObjectRef>),
5284
Bytes(Vec<u8>),
53-
Function(String, ObjectRef), // module, code
85+
Function(String, ObjectRef, HashMap<String, ObjectRef>), // module, code, default arguments
5486
Module(ObjectRef),
5587
PrimitiveNamespace, // __primitives__
5688
PrimitiveFunction(String),
@@ -131,12 +163,13 @@ impl ObjectRef {
131163
ObjectContent::Int(ref i) => i.to_string(),
132164
ObjectContent::Bytes(ref s) => "<bytes>".to_string(), // TODO
133165
ObjectContent::String(ref s) => format!("'{}'", s), // TODO: escape
166+
ObjectContent::Dict(ref l) => format!("{{{}}}", l.iter().map(|arg| { let (ref k, ref v) = *arg; format!("{}: {}", k.repr(store), v.repr(store))}).join(", ")),
134167
ObjectContent::Tuple(ref l) => format!("tuple({})", ObjectRef::repr_vec(l, store)),
135168
ObjectContent::List(ref l) => format!("[{}]", ObjectRef::repr_vec(l, store)),
136169
ObjectContent::Code(_) => "<code object>".to_string(),
137170
ObjectContent::Set(ref l) => format!("set({})", ObjectRef::repr_vec(l, store)),
138171
ObjectContent::FrozenSet(ref l) => format!("frozenset({})", ObjectRef::repr_vec(l, store)),
139-
ObjectContent::Function(ref module, ref _code) => {
172+
ObjectContent::Function(ref module, ref _code, ref _defaults) => {
140173
match obj.name {
141174
None => format!("<anonymous function in module {}>", module),
142175
Some(ref s) => format!("<function {} in module {}>", s, module),
@@ -167,7 +200,7 @@ impl ObjectRef {
167200
let func = store.deref(self);
168201
let ref name = func.name;
169202
match func.content {
170-
ObjectContent::Function(ref module_name, ref _code) => module_name.clone(),
203+
ObjectContent::Function(ref module_name, ref _code, ref _defaults) => module_name.clone(),
171204
ObjectContent::Module(ref _code) => name.clone().unwrap(),
172205
_ => panic!(format!("Not a function/module: {:?}", func)),
173206
}
@@ -241,6 +274,7 @@ pub struct PrimitiveObjects {
241274

242275
pub set_type: ObjectRef,
243276
pub frozenset_type: ObjectRef,
277+
pub dict_type: ObjectRef,
244278

245279
pub bytes_type: ObjectRef,
246280
pub str_type: ObjectRef,
@@ -300,6 +334,7 @@ impl PrimitiveObjects {
300334
let list_type = store.allocate(Object::new_class("list".to_string(), None, type_ref.clone(), vec![obj_ref.clone()]));
301335
let set_type = store.allocate(Object::new_class("set".to_string(), None, type_ref.clone(), vec![obj_ref.clone()]));
302336
let frozenset_type = store.allocate(Object::new_class("frozenset".to_string(), None, type_ref.clone(), vec![obj_ref.clone()]));
337+
let dict_type = store.allocate(Object::new_class("dict".to_string(), None, type_ref.clone(), vec![obj_ref.clone()]));
303338
let bytes_type = store.allocate(Object::new_class("bytes".to_string(), None, type_ref.clone(), vec![obj_ref.clone()]));
304339
let str_type = store.allocate(Object::new_class("str".to_string(), None, type_ref.clone(), vec![obj_ref.clone()]));
305340
let iterator_type = store.allocate(Object::new_class("iterator".to_string(), None, type_ref.clone(), vec![obj_ref.clone()]));
@@ -335,6 +370,7 @@ impl PrimitiveObjects {
335370
map.insert("list".to_string(), list_type.clone());
336371
map.insert("set".to_string(), set_type.clone());
337372
map.insert("frozenset".to_string(), frozenset_type.clone());
373+
map.insert("dict".to_string(), dict_type.clone());
338374
map.insert("bytes".to_string(), bytes_type.clone());
339375
map.insert("str".to_string(), str_type.clone());
340376
map.insert("function".to_string(), function_type.clone());
@@ -359,7 +395,7 @@ impl PrimitiveObjects {
359395
none_type: none_type, none: none,
360396
int_type: int_type, bool_type: bool_type, true_obj: true_obj, false_obj: false_obj,
361397
tuple_type: tuple_type, list_type: list_type,
362-
set_type: set_type, frozenset_type: frozenset_type,
398+
set_type: set_type, frozenset_type: frozenset_type, dict_type: dict_type,
363399
bytes_type: bytes_type, str_type: str_type,
364400
iterator_type: iterator_type,
365401
function_type: function_type, code_type: code_type,
@@ -389,14 +425,17 @@ impl PrimitiveObjects {
389425
pub fn new_set(&self, v: Vec<ObjectRef>) -> Object {
390426
Object::new_instance(None, self.set_type.clone(), ObjectContent::Set(v))
391427
}
428+
pub fn new_dict(&self, v: Vec<(ObjectRef, ObjectRef)>) -> Object {
429+
Object::new_instance(None, self.dict_type.clone(), ObjectContent::Dict(v))
430+
}
392431
pub fn new_frozenset(&self, v: Vec<ObjectRef>) -> Object {
393432
Object::new_instance(None, self.frozenset_type.clone(), ObjectContent::FrozenSet(v))
394433
}
395434
pub fn new_code(&self, c: Code) -> Object {
396435
Object::new_instance(None, self.code_type.clone(), ObjectContent::Code(Box::new(c)))
397436
}
398-
pub fn new_function(&self, name: String, module_name: String, code: ObjectRef) -> Object {
399-
Object::new_instance(Some(name), self.function_type.clone(), ObjectContent::Function(module_name, code))
437+
pub fn new_function(&self, name: String, module_name: String, code: ObjectRef, defaults: HashMap<String, ObjectRef>) -> Object {
438+
Object::new_instance(Some(name), self.function_type.clone(), ObjectContent::Function(module_name, code, defaults))
400439
}
401440
pub fn new_module(&self, name: String, code: ObjectRef) -> Object {
402441
Object::new_instance(Some(name), self.module.clone(), ObjectContent::Module(code))

src/primitives/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ fn build_class<EP: EnvProxy>(processor: &mut State<EP>, call_stack: &mut Vec<Fra
5858
let mut args_iter = args.into_iter();
5959
parse_first_arguments!("__primitives__.build_class", processor.store, args, args_iter,
6060
"func" "a function": {
61-
ObjectContent::Function(_, ref code_arg) => {
61+
ObjectContent::Function(_, ref code_arg, _) => {
6262
code = code_arg.clone();
6363
},
6464
},

src/processor/mod.rs

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::collections::HashMap;
1010
use std::io::Read;
1111
use std::cell::RefCell;
1212
use std::rc::Rc;
13+
use std::iter::FromIterator;
1314
use super::marshal;
1415
use super::state::{State, PyResult, unwind, raise, return_value};
1516
use super::sandbox::EnvProxy;
@@ -129,7 +130,7 @@ fn load_attr<EP: EnvProxy>(state: &mut State<EP>, obj: &Object, name: &String) -
129130
}
130131

131132
// Call a primitive / function / code object, with arguments.
132-
fn call_function<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame>, func_ref: &ObjectRef, mut args: Vec<ObjectRef>, kwargs: Vec<ObjectRef>) {
133+
fn call_function<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame>, func_ref: &ObjectRef, mut args: Vec<ObjectRef>, kwargs: Vec<(ObjectRef, ObjectRef)>) {
133134
// TODO: clone only if necessary
134135
match state.store.deref(func_ref).content.clone() {
135136
ObjectContent::Class(None) => {
@@ -141,33 +142,69 @@ fn call_function<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame
141142
let frame = call_stack.last_mut().unwrap();
142143
frame.var_stack.push(state.store.allocate(Object::new_instance(None, func_ref.clone(), ObjectContent::OtherObject)))
143144
},
144-
ObjectContent::Function(ref func_module, ref code_ref) => {
145+
ObjectContent::Function(ref func_module, ref code_ref, ref defaults) => {
145146
let code = state.store.deref(code_ref).content.clone();
146147
if let ObjectContent::Code(code) = code {
147-
if code.co_varargs() { // If it has a *args argument
148+
149+
let mut locals = Rc::new(RefCell::new(defaults.clone()));
150+
151+
if let Some(starargs_name) = code.get_varargs_name() { // If it has a *args argument
148152
if code.argcount > args.len() {
149153
panic!(format!("{}() takes at least {} arguments, but {} was/were given.", code.name, code.argcount, args.len()))
150154
};
151155
let to_vararg = args.drain(code.argcount..).collect();
152156
let obj_ref = state.store.allocate(state.primitive_objects.new_tuple(to_vararg));
153-
args.push(obj_ref);
157+
158+
// Bind *args
159+
assert_eq!(None, locals.borrow_mut().insert(starargs_name.clone(), obj_ref));
154160
}
155-
else if code.argcount != args.len() {
161+
else if code.argcount != args.len() { // If it has no *args argument
156162
panic!(format!("{}() takes {} arguments, but {} was/were given.", code.name, code.argcount, args.len()))
157163
};
158-
let mut locals = Rc::new(RefCell::new(HashMap::new()));
164+
165+
// Handle keyword arguments
166+
let mut remaining_kwargs = vec![]; // arguments that will go to **kwargs
167+
{
168+
let explicit_keywords = code.keywords();
169+
let mut locals = locals.borrow_mut();
170+
for (key, value) in kwargs.into_iter() {
171+
let key_str = match state.store.deref(&key).content {
172+
ObjectContent::String(ref s) => s,
173+
_ => panic!("Keyword names should be strings."),
174+
};
175+
if explicit_keywords.contains(key_str) {
176+
locals.insert(key_str.clone(), value);
177+
}
178+
else {
179+
remaining_kwargs.push((key, value))
180+
}
181+
}
182+
}
183+
184+
if let Some(starkwargs_name) = code.get_varkwargs_name() { // If it has a **kwargs argument
185+
let obj_ref = state.store.allocate(state.primitive_objects.new_dict(remaining_kwargs));
186+
locals.borrow_mut().insert(starkwargs_name.clone(), obj_ref);
187+
}
188+
else { // If it has no **kwargs argument
189+
if remaining_kwargs.len() != 0 {
190+
panic!(format!("Unknown keyword argument to function {} with no **kwargs", code.name))
191+
}
192+
}
193+
194+
// Bind positional arguments
159195
{
160196
let mut locals = locals.borrow_mut();
161197
for (argname, argvalue) in code.varnames.iter().zip(args) {
162198
locals.insert(argname.clone(), argvalue);
163199
};
164200
}
201+
165202
let new_frame = Frame::new(func_ref.clone(), *code, locals);
166203
call_stack.push(new_frame);
167204
}
168205
else {
169206
let exc = state.primitive_objects.processorerror.clone();
170-
let repr = func_ref.repr(&state.store);
207+
let repr = code_ref.repr(&state.store);
171208
raise(state, call_stack, exc, format!("Not a code object {}", repr));
172209
}
173210
},
@@ -460,13 +497,13 @@ fn run_code<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame>) ->
460497
let mut func;
461498
{
462499
let frame = call_stack.last_mut().unwrap();
463-
kwargs = py_unwrap!(state, frame.var_stack.pop_many(nb_kwargs*2), ProcessorError::StackTooSmall);
500+
kwargs = py_unwrap!(state, frame.var_stack.pop_n_pairs(nb_kwargs), ProcessorError::StackTooSmall);
464501
args = py_unwrap!(state, frame.var_stack.pop_many(nb_args), ProcessorError::StackTooSmall);
465502
func = pop_stack!(state, frame.var_stack);
466503
}
467504
call_function(state, call_stack, &func, args, kwargs)
468505
},
469-
Instruction::MakeFunction(0, 0, 0) => {
506+
Instruction::MakeFunction(0, nb_default_kwargs, 0) => {
470507
// TODO: consume default arguments and annotations
471508
let obj = {
472509
let frame = call_stack.last_mut().unwrap();
@@ -483,7 +520,16 @@ fn run_code<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame>) ->
483520
};
484521
let frame = call_stack.last_mut().unwrap();
485522
let code = pop_stack!(state, frame.var_stack);
486-
let func = state.primitive_objects.new_function(func_name, frame.object.module(&state.store), code);
523+
let raw_kwdefaults = py_unwrap!(state, frame.var_stack.pop_n_pairs(nb_default_kwargs), ProcessorError::StackTooSmall);
524+
let mut kwdefaults: HashMap<String, ObjectRef> = HashMap::new();
525+
kwdefaults.reserve(nb_default_kwargs);
526+
for (key, value) in raw_kwdefaults {
527+
match state.store.deref(&key).content {
528+
ObjectContent::String(ref s) => { kwdefaults.insert(s.clone(), value); },
529+
_ => panic!("Defaults' keys must be strings."),
530+
}
531+
}
532+
let func = state.primitive_objects.new_function(func_name, frame.object.module(&state.store), code, kwdefaults);
487533
frame.var_stack.push(state.store.allocate(func))
488534
},
489535
_ => panic!(format!("todo: instruction {:?}", instruction)),

src/varstack.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub trait VarStack : Debug {
88
fn pop_many(&mut self, count: usize) -> Option<Vec<Self::Item>>;
99
fn push(&mut self, value: Self::Item);
1010
fn pop_all_and_get_n_last(&mut self, nb: usize) -> Option<Vec<Self::Item>>;
11+
fn pop_n_pairs(&mut self, nb: usize) -> Option<Vec<(Self::Item, Self::Item)>>;
1112
}
1213

1314
#[derive(Debug)]
@@ -27,7 +28,7 @@ impl<Item> VectorVarStack<Item> {
2728
}
2829
}
2930

30-
impl<Item> VarStack for VectorVarStack<Item> where Item: Debug {
31+
impl<Item: Clone> VarStack for VectorVarStack<Item> where Item: Debug {
3132
type Item = Item;
3233

3334
fn top(&self) -> Option<&Self::Item> {
@@ -61,4 +62,17 @@ impl<Item> VarStack for VectorVarStack<Item> where Item: Debug {
6162
Some(self.vector.drain(..).collect())
6263
}
6364
}
65+
66+
fn pop_n_pairs(&mut self, nb: usize) -> Option<Vec<(Self::Item, Self::Item)>> {
67+
self.pop_many(nb*2).map(|values| {
68+
let mut pairs = Vec::<(Self::Item, Self::Item)>::new();
69+
pairs.reserve(nb);
70+
for chunk in values.chunks(2) {
71+
assert!(chunk.len() == 2);
72+
// TODO: remove clones. http://stackoverflow.com/q/37097395/539465
73+
pairs.push((chunk.get(0).unwrap().clone(), chunk.get(1).unwrap().clone()));
74+
}
75+
pairs
76+
})
77+
}
6478
}

0 commit comments

Comments
 (0)