Skip to content

Commit 999c625

Browse files
committed
Merge branch 'master' of https://github.com/RustPython/RustPython into objtyp
2 parents f9538af + 4132f33 commit 999c625

File tree

12 files changed

+252
-50
lines changed

12 files changed

+252
-50
lines changed

parser/src/ast.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ pub enum Expression {
137137
},
138138
Call {
139139
function: Box<Expression>,
140-
args: Vec<Expression>,
140+
// parameters are (None, value), kwargs are (keyword name, value)
141+
args: Vec<(Option<String>, Expression)>,
141142
},
142143
Number {
143144
value: Number,

parser/src/parser.rs

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,12 @@ mod tests {
9595
function: Box::new(ast::Expression::Identifier {
9696
name: String::from("print"),
9797
}),
98-
args: vec![ast::Expression::String {
99-
value: String::from("Hello world"),
100-
},],
98+
args: vec![(
99+
None,
100+
ast::Expression::String {
101+
value: String::from("Hello world"),
102+
}
103+
),],
101104
},
102105
},
103106
},],
@@ -120,12 +123,53 @@ mod tests {
120123
name: String::from("print"),
121124
}),
122125
args: vec![
123-
ast::Expression::String {
124-
value: String::from("Hello world"),
125-
},
126-
ast::Expression::Number {
127-
value: ast::Number::Integer { value: 2 },
128-
},
126+
(
127+
None,
128+
ast::Expression::String {
129+
value: String::from("Hello world"),
130+
}
131+
),
132+
(
133+
None,
134+
ast::Expression::Number {
135+
value: ast::Number::Integer { value: 2 },
136+
}
137+
),
138+
],
139+
},
140+
},
141+
},],
142+
}
143+
);
144+
}
145+
146+
#[test]
147+
fn test_parse_kwargs() {
148+
let source = String::from("my_func('positional', keyword=2)\n");
149+
let parse_ast = parse_program(&source).unwrap();
150+
assert_eq!(
151+
parse_ast,
152+
ast::Program {
153+
statements: vec![ast::LocatedStatement {
154+
location: ast::Location::new(1, 1),
155+
node: ast::Statement::Expression {
156+
expression: ast::Expression::Call {
157+
function: Box::new(ast::Expression::Identifier {
158+
name: String::from("my_func"),
159+
}),
160+
args: vec![
161+
(
162+
None,
163+
ast::Expression::String {
164+
value: String::from("positional"),
165+
}
166+
),
167+
(
168+
Some("keyword".to_string()),
169+
ast::Expression::Number {
170+
value: ast::Number::Integer { value: 2 },
171+
}
172+
),
129173
],
130174
},
131175
},

parser/src/python.lalrpop

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -510,8 +510,13 @@ TestList: Vec<ast::Expression> = {
510510
}
511511
};
512512

513-
FunctionArguments: Vec<ast::Expression> = {
514-
<e: Comma<Test>> => e,
513+
FunctionArguments: Vec<(Option<String>, ast::Expression)> = {
514+
<e: Comma<FunctionArgument>> => e,
515+
};
516+
517+
FunctionArgument: (Option<String>, ast::Expression) = {
518+
<e:Expression> => (None, e.clone()),
519+
<i:Identifier> "=" <e:Expression> => (Some(i.clone()), e.clone()),
515520
};
516521

517522
Comma<T>: Vec<T> = {

tests/snippets/func_defaults.py

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,19 @@ def no_args():
1010
else:
1111
assert False, 'no TypeError raised: 1 arg to no_args'
1212

13+
try:
14+
no_args(kw='should fail')
15+
except TypeError:
16+
pass
17+
else:
18+
assert False, 'no TypeError raised: kwarg to no_args'
19+
1320

1421
def one_arg(arg):
15-
pass
22+
return arg
1623

1724
one_arg('one_arg')
25+
assert "arg" == one_arg(arg="arg")
1826

1927
try:
2028
one_arg()
@@ -23,19 +31,41 @@ def one_arg(arg):
2331
else:
2432
assert False, 'no TypeError raised: no args to one_arg'
2533

34+
try:
35+
one_arg(wrong_arg='wont work')
36+
except TypeError:
37+
pass
38+
else:
39+
assert False, 'no TypeError raised: incorrect kwarg to one_arg'
40+
2641
try:
2742
one_arg('one_arg', 'two_arg')
2843
except TypeError:
2944
pass
3045
else:
3146
assert False, 'no TypeError raised: two args to one_arg'
3247

48+
try:
49+
one_arg('one_arg', extra_arg='wont work')
50+
except TypeError:
51+
pass
52+
else:
53+
assert False, 'no TypeError raised: extra kwarg to one_arg'
54+
55+
try:
56+
one_arg('one_arg', arg='duplicate')
57+
except TypeError:
58+
pass
59+
else:
60+
assert False, 'no TypeError raised: same pos and kwarg to one_arg'
61+
3362

3463
def one_default_arg(arg="default"):
3564
return arg
3665

3766
assert 'default' == one_default_arg()
3867
assert 'arg' == one_default_arg('arg')
68+
assert 'kwarg' == one_default_arg(arg='kwarg')
3969

4070
try:
4171
one_default_arg('one_arg', 'two_arg')
@@ -56,11 +86,26 @@ def one_normal_one_default_arg(pos, arg="default"):
5686
except TypeError:
5787
pass
5888
else:
59-
assert False, 'no TypeError raised: two args to one_default_arg'
89+
assert False, 'no TypeError raised: no args to one_normal_one_default_arg'
6090

6191
try:
6292
one_normal_one_default_arg('one', 'two', 'three')
6393
except TypeError:
6494
pass
6595
else:
66-
assert False, 'no TypeError raised: two args to one_default_arg'
96+
assert False, 'no TypeError raised: three args to one_normal_one_default_arg'
97+
98+
99+
def two_pos(a, b):
100+
return (a, b)
101+
102+
assert ('a', 'b') == two_pos('a', 'b')
103+
assert ('a', 'b') == two_pos(b='b', a='a')
104+
105+
106+
def kwargs_are_variable(x=[]):
107+
x.append(1)
108+
return x
109+
110+
assert [1] == kwargs_are_variable()
111+
assert [1, 1] == kwargs_are_variable()

tests/snippets/json_snippet.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def round_trip_test(obj):
3838
assert True == json.loads('true')
3939
assert False == json.loads('false')
4040
# TODO: uncomment once None comparison is implemented
41-
# assert None == json.loads('null')
41+
assert None == json.loads('null')
4242
assert [] == json.loads('[]')
4343
assert ['a'] == json.loads('["a"]')
4444
assert [['a'], 'b'] == json.loads('[["a"], "b"]')

vm/src/builtins.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py
420420
function,
421421
PyFuncArgs {
422422
args: vec![namespace.clone()],
423+
kwargs: vec![],
423424
},
424425
);
425426
objtype::new(metaclass, name, bases, namespace)

vm/src/bytecode.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ pub enum Instruction {
100100
CallFunction {
101101
count: usize,
102102
},
103+
CallFunctionKw {
104+
count: usize,
105+
},
103106
ForIter,
104107
ReturnValue,
105108
SetupLoop {

vm/src/compile.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -628,10 +628,27 @@ impl Compiler {
628628
ast::Expression::Call { function, args } => {
629629
self.compile_expression(&*function);
630630
let count = args.len();
631-
for arg in args {
632-
self.compile_expression(arg)
631+
let mut kwarg_names = vec![];
632+
for (kwarg, value) in args {
633+
if let Some(kwarg) = kwarg {
634+
kwarg_names.push(bytecode::Constant::String {
635+
value: kwarg.to_string(),
636+
});
637+
} else if kwarg_names.len() > 0 {
638+
panic!("positional argument follows keyword argument");
639+
};
640+
self.compile_expression(value);
641+
}
642+
if kwarg_names.len() > 0 {
643+
self.emit(Instruction::LoadConst {
644+
value: bytecode::Constant::Tuple {
645+
elements: kwarg_names,
646+
},
647+
});
648+
self.emit(Instruction::CallFunctionKw { count });
649+
} else {
650+
self.emit(Instruction::CallFunction { count });
633651
}
634-
self.emit(Instruction::CallFunction { count: count });
635652
}
636653
ast::Expression::BoolOp { .. } => {
637654
self.compile_test(expression, None, None, EvalContext::Expression)

vm/src/obj/objtype.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ pub fn get_attribute(vm: &mut VirtualMachine, obj: PyObjectRef, name: &String) -
131131
descriptor,
132132
PyFuncArgs {
133133
args: vec![attr, obj, cls],
134+
kwargs: vec![],
134135
},
135136
);
136137
}

vm/src/objbool.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result<bool, PyObje
1616
PyObjectKind::None { .. } => false,
1717
_ => {
1818
if let Ok(f) = objtype::get_attribute(vm, obj.clone(), &String::from("__bool__")) {
19-
match vm.invoke(f, PyFuncArgs::new()) {
19+
match vm.invoke(f, PyFuncArgs::default()) {
2020
Ok(result) => match result.borrow().kind {
2121
PyObjectKind::Boolean { value } => value,
2222
_ => return Err(vm.new_type_error(String::from("TypeError"))),

vm/src/pyobject.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,21 +521,30 @@ impl fmt::Debug for PyObject {
521521
#[derive(Debug, Default, Clone)]
522522
pub struct PyFuncArgs {
523523
pub args: Vec<PyObjectRef>,
524-
// TODO: add kwargs here
524+
pub kwargs: Vec<(String, PyObjectRef)>,
525525
}
526526

527527
impl PyFuncArgs {
528-
pub fn new() -> PyFuncArgs {
529-
PyFuncArgs { args: vec![] }
528+
pub fn new(mut args: Vec<PyObjectRef>, kwarg_names: Vec<String>) -> PyFuncArgs {
529+
let mut kwargs = vec![];
530+
for name in kwarg_names.iter().rev() {
531+
kwargs.push((name.clone(), args.pop().unwrap()));
532+
}
533+
PyFuncArgs {
534+
args: args,
535+
kwargs: kwargs,
536+
}
530537
}
531538

532539
pub fn insert(&self, item: PyObjectRef) -> PyFuncArgs {
533540
let mut args = PyFuncArgs {
534541
args: self.args.clone(),
542+
kwargs: self.kwargs.clone(),
535543
};
536544
args.args.insert(0, item);
537545
return args;
538546
}
547+
539548
pub fn shift(&mut self) -> PyObjectRef {
540549
self.args.remove(0)
541550
}
@@ -793,6 +802,7 @@ impl PartialEq for PyObject {
793802
}
794803
}
795804
(PyObjectKind::Boolean { value: a }, PyObjectKind::Boolean { value: b }) => a == b,
805+
(PyObjectKind::None, PyObjectKind::None) => true,
796806
_ => panic!(
797807
"TypeError in COMPARE_OP: can't compare {:?} with {:?}",
798808
self, other

0 commit comments

Comments
 (0)