diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 6d0463db6..da49ad887 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -198,11 +198,14 @@ pub enum Expression { * In cpython this is called arguments, but we choose parameters to * distuingish between function parameters and actual call arguments. */ -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Default)] pub struct Parameters { pub args: Vec, + pub kwonlyargs: Vec, pub vararg: Option, + pub kwarg: Option, pub defaults: Vec, + pub kw_defaults: Vec>, } #[derive(Debug, PartialEq)] diff --git a/parser/src/parser.rs b/parser/src/parser.rs index af7016712..22ba9a730 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -228,8 +228,11 @@ mod tests { expression: ast::Expression::Lambda { args: ast::Parameters { args: vec![String::from("x"), String::from("y")], + kwonlyargs: vec![], vararg: None, + kwarg: None, defaults: vec![], + kw_defaults: vec![], }, body: Box::new(ast::Expression::Binop { a: Box::new(ast::Expression::Identifier { @@ -305,8 +308,11 @@ mod tests { name: String::from("__init__"), args: ast::Parameters { args: vec![String::from("self")], + kwonlyargs: vec![], vararg: None, + kwarg: None, defaults: vec![], + kw_defaults: vec![], }, body: vec![ast::LocatedStatement { location: ast::Location::new(3, 3), @@ -321,10 +327,13 @@ mod tests { name: String::from("method_with_default"), args: ast::Parameters { args: vec![String::from("self"), String::from("arg"),], + kwonlyargs: vec![], vararg: None, + kwarg: None, defaults: vec![ast::Expression::String { value: "default".to_string() - }] + }], + kw_defaults: vec![], }, body: vec![ast::LocatedStatement { location: ast::Location::new(5, 3), diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop index 459b690dd..f1bb14624 100644 --- a/parser/src/python.lalrpop +++ b/parser/src/python.lalrpop @@ -407,13 +407,22 @@ FuncDef: ast::LocatedStatement = { }; Parameters: ast::Parameters = { - "(" ")" => a, + "(" ")" => { + match a { + Some(a) => a, + None => Default::default(), + } + }, }; // parameters are (String, None), kwargs are (String, Some(Test)) where Test is // the default TypedArgsList: ast::Parameters = { - > => { + => { + // Combine first parameters: + let mut args = vec![param1]; + args.extend(param2.into_iter().map(|x| x.1)); + let mut names = vec![]; let mut default_elements = vec![]; @@ -433,17 +442,71 @@ TypedArgsList: ast::Parameters = { } } + // Now gather rest of parameters: + let (vararg, kwonlyargs, kw_defaults, kwarg) = match args2 { + Some((_, x)) => x, + None => (None, vec![], vec![], None), + }; + ast::Parameters { args: names, - vararg: None, + kwonlyargs: kwonlyargs, + vararg: vararg, + kwarg: kwarg, defaults: default_elements, + kw_defaults: kw_defaults, } - } + }, + => { + let (vararg, kwonlyargs, kw_defaults, kwarg) = params; + ast::Parameters { + args: vec![], + kwonlyargs: kwonlyargs, + vararg: vararg, + kwarg: kwarg, + defaults: vec![], + kw_defaults: kw_defaults, + } + }, + "**" => { + ast::Parameters { + args: vec![], + kwonlyargs: vec![], + vararg: None, + kwarg: Some(kw), + defaults: vec![], + kw_defaults: vec![], + } + }, }; -Parameter: (String, Option) = { - => (i.clone(), None), - "=" => (i.clone(), Some(e)), +TypedParameterDef: (String, Option) = { + => (i, None), + "=" => (i, Some(e)), +}; + +// TODO: add type annotations here: +TypedParameter: String = { + Identifier, +}; + +ParameterListStarArgs: (Option, Vec, Vec>, Option) = { + "*" => { + // Extract keyword arguments: + let mut kwonlyargs = vec![]; + let mut kw_defaults = vec![]; + for (name, value) in kw.into_iter().map(|x| x.1) { + kwonlyargs.push(name); + kw_defaults.push(value); + } + + let kwarg = match kwarg { + Some((_, _, name)) => Some(name), + None => None, + }; + + (Some(va), kwonlyargs, kw_defaults, kwarg) + } }; ClassDef: ast::LocatedStatement = { @@ -505,9 +568,9 @@ Test: ast::Expression = { }; LambdaDef: ast::Expression = { - "lambda" ":" => + "lambda" ":" => ast::Expression::Lambda { - args:p, + args: p.unwrap_or(Default::default()), body:Box::new(b) } } diff --git a/tests/snippets/delete.py b/tests/snippets/delete.py index e83b42768..078b6dcd9 100644 --- a/tests/snippets/delete.py +++ b/tests/snippets/delete.py @@ -2,3 +2,11 @@ a = 1 del a +class MyObject: pass +foo = MyObject() +foo.bar = 2 +assert hasattr(foo, 'bar') +del foo.bar + +assert not hasattr(foo, 'bar') + diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index ffecc61e1..dea6bc508 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -49,6 +49,28 @@ fn object_ne(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { objbool::not(vm, &eq) } +// TODO: is object the right place for delattr? +fn object_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (zelf, Some(vm.ctx.object())), + (attr, Some(vm.ctx.str_type())) + ] + ); + + // Get dict: + let dict = match zelf.borrow().kind { + PyObjectKind::Class { ref dict, .. } => dict.clone(), + PyObjectKind::Instance { ref dict, .. } => dict.clone(), + _ => return Err(vm.new_type_error("TypeError: no dictionary.".to_string())), + }; + + // Delete attr from dict: + vm.call_method(&dict, "__delitem__", vec![attr.clone()]) +} + fn object_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.object()))]); vm.call_method(zelf, "__repr__", vec![]) @@ -67,6 +89,7 @@ pub fn init(context: &PyContext) { object.set_attr("__init__", context.new_rustfunc(object_init)); object.set_attr("__eq__", context.new_rustfunc(object_eq)); object.set_attr("__ne__", context.new_rustfunc(object_ne)); + object.set_attr("__delattr__", context.new_rustfunc(object_delattr)); object.set_attr("__dict__", context.new_member_descriptor(object_dict)); object.set_attr("__str__", context.new_rustfunc(object_str)); object.set_attr("__repr__", context.new_rustfunc(object_repr));