Skip to content

Commit e6fd9fb

Browse files
committed
Add double star arguments in function calls
1 parent 3f6c716 commit e6fd9fb

File tree

4 files changed

+71
-21
lines changed

4 files changed

+71
-21
lines changed

tests/snippets/function_args.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,6 @@ def va2(*args, **kwargs):
3939

4040
# va3(1, 2, 3, b=10)
4141

42+
x = {'f': 42, 'e': 1337}
43+
y = {'d': 1337}
44+
va(1, 22, 3, 4, **x, **y)

vm/src/bytecode.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ pub enum Instruction {
159159
},
160160
BuildMap {
161161
size: usize,
162+
unpack: bool,
162163
},
163164
BuildSlice {
164165
size: usize,

vm/src/compile.rs

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,10 @@ impl Compiler {
879879
self.compile_expression(key)?;
880880
self.compile_expression(value)?;
881881
}
882-
self.emit(Instruction::BuildMap { size: size });
882+
self.emit(Instruction::BuildMap {
883+
size: size,
884+
unpack: false,
885+
});
883886
}
884887
ast::Expression::Slice { elements } => {
885888
let size = elements.len();
@@ -982,14 +985,45 @@ impl Compiler {
982985

983986
// Normal arguments:
984987
let must_unpack = self.gather_elements(args)?;
988+
let has_double_star = keywords.iter().any(|k| k.name.is_none());
985989

986-
if must_unpack {
990+
if must_unpack || has_double_star {
991+
// Create a tuple with positional args:
987992
self.emit(Instruction::BuildTuple {
988993
size: args.len(),
989-
unpack: true,
994+
unpack: must_unpack,
990995
});
996+
997+
// Create an optional map with kw-args:
991998
if keywords.len() > 0 {
992-
unimplemented!()
999+
for keyword in keywords {
1000+
if let Some(name) = &keyword.name {
1001+
self.emit(Instruction::LoadConst {
1002+
value: bytecode::Constant::String {
1003+
value: name.to_string(),
1004+
},
1005+
});
1006+
self.compile_expression(&keyword.value)?;
1007+
if has_double_star {
1008+
self.emit(Instruction::BuildMap {
1009+
size: 1,
1010+
unpack: false,
1011+
});
1012+
}
1013+
} else {
1014+
// This means **kwargs!
1015+
self.compile_expression(&keyword.value)?;
1016+
}
1017+
}
1018+
1019+
self.emit(Instruction::BuildMap {
1020+
size: keywords.len(),
1021+
unpack: has_double_star,
1022+
});
1023+
1024+
self.emit(Instruction::CallFunction {
1025+
typ: CallType::Ex(true),
1026+
});
9931027
} else {
9941028
self.emit(Instruction::CallFunction {
9951029
typ: CallType::Ex(false),
@@ -1099,7 +1133,10 @@ impl Compiler {
10991133
});
11001134
}
11011135
ast::ComprehensionKind::Dict { .. } => {
1102-
self.emit(Instruction::BuildMap { size: 0 });
1136+
self.emit(Instruction::BuildMap {
1137+
size: 0,
1138+
unpack: false,
1139+
});
11031140
}
11041141
}
11051142

vm/src/frame.rs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use super::builtins;
99
use super::bytecode;
1010
use super::import::import;
1111
use super::obj::objbool;
12+
use super::obj::objdict;
1213
use super::obj::objiter;
1314
use super::obj::objlist;
1415
use super::obj::objstr;
@@ -245,21 +246,29 @@ impl Frame {
245246
self.push_value(list_obj);
246247
Ok(None)
247248
}
248-
bytecode::Instruction::BuildMap { size } => {
249+
bytecode::Instruction::BuildMap { size, unpack } => {
249250
let mut elements = HashMap::new();
250251
for _x in 0..*size {
251252
let obj = self.pop_value();
252-
// XXX: Currently, we only support String keys, so we have to unwrap the
253-
// PyObject (and ensure it is a String).
254-
let key_pyobj = self.pop_value();
255-
let key = match key_pyobj.borrow().kind {
256-
PyObjectKind::String { ref value } => value.clone(),
257-
ref kind => unimplemented!(
258-
"Only strings can be used as dict keys, we saw: {:?}",
259-
kind
260-
),
261-
};
262-
elements.insert(key, obj);
253+
if *unpack {
254+
// Take all key-value pairs from the dict:
255+
let dict_elements = objdict::get_elements(&obj);
256+
for (key, obj) in dict_elements {
257+
elements.insert(key, obj);
258+
}
259+
} else {
260+
// XXX: Currently, we only support String keys, so we have to unwrap the
261+
// PyObject (and ensure it is a String).
262+
let key_pyobj = self.pop_value();
263+
let key = match key_pyobj.borrow().kind {
264+
PyObjectKind::String { ref value } => value.clone(),
265+
ref kind => unimplemented!(
266+
"Only strings can be used as dict keys, we saw: {:?}",
267+
kind
268+
),
269+
};
270+
elements.insert(key, obj);
271+
}
263272
}
264273
let map_obj = PyObject::new(
265274
PyObjectKind::Dict { elements: elements },
@@ -466,15 +475,15 @@ impl Frame {
466475
PyFuncArgs::new(args, kwarg_names)
467476
}
468477
bytecode::CallType::Ex(has_kwargs) => {
469-
let kwarg_names = if *has_kwargs {
470-
self.pop_value();
471-
unimplemented!();
478+
let kwargs = if *has_kwargs {
479+
let kw_dict = self.pop_value();
480+
objdict::get_elements(&kw_dict).into_iter().collect()
472481
} else {
473482
vec![]
474483
};
475484
let args = self.pop_value();
476485
let args = self.extract_elements(vm, &args)?;
477-
PyFuncArgs::new(args, kwarg_names)
486+
PyFuncArgs { args, kwargs }
478487
}
479488
};
480489

0 commit comments

Comments
 (0)