From ea1a7bb39525b0693cb77d36743fb61097fd6bf9 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sat, 2 Feb 2019 22:04:26 -0500 Subject: [PATCH] Add list.{__lt__, __gt__, __le__, __ge__} --- tests/snippets/list.py | 8 ++++ vm/src/obj/objlist.rs | 94 +++++++++++++++++++++++++++++++++++++- vm/src/obj/objsequence.rs | 95 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+), 1 deletion(-) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index 85010a755..52dd60f0c 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -20,3 +20,11 @@ except ValueError: pass else: assert False, "ValueError was not raised" + +x = [[1,2,3], 'a', 1] +y = [[3,1,1], 'z', 2] +assert x < y, "list __lt__ failed" + +x = [5, -3, 1] +y = [1, 10, 29] +assert x > y, "list __gt__ failed" \ No newline at end of file diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 641bb9aa6..b4f7eb5df 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -5,7 +5,7 @@ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; use super::objsequence::{ - get_elements, get_item, get_mut_elements, seq_equal, PySliceableSequence, + get_elements, get_item, get_mut_elements, seq_equal, seq_gt, seq_lt, seq_le, seq_ge, PySliceableSequence, }; use super::objstr; use super::objtype; @@ -73,6 +73,94 @@ fn list_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } +fn list_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.list_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_lt(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn list_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.list_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_gt(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn list_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.list_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_ge(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn list_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.list_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_le(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -281,6 +369,10 @@ pub fn init(context: &PyContext) { context.new_rustfunc(list_contains), ); context.set_attr(&list_type, "__eq__", context.new_rustfunc(list_eq)); + context.set_attr(&list_type, "__lt__", context.new_rustfunc(list_lt)); + context.set_attr(&list_type, "__gt__", context.new_rustfunc(list_gt)); + context.set_attr(&list_type, "__le__", context.new_rustfunc(list_le)); + context.set_attr(&list_type, "__ge__", context.new_rustfunc(list_ge)); context.set_attr( &list_type, "__getitem__", diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index cd26391a9..705bdb750 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -120,6 +120,101 @@ pub fn seq_equal( } } +pub fn seq_lt( + vm: &mut VirtualMachine, + zelf: &Vec, + other: &Vec, +) -> Result { + if zelf.len() == other.len() { + for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { + let lt = vm.call_method(&a.clone(), "__lt__", vec![b.clone()])?; + let value = objbool::boolval(vm, lt)?; + if !value { + return Ok(false); + } + } + Ok(true) + } else { + // This case is more complicated because it can still return true if + // `zelf` is the head of `other` e.g. [1,2,3] < [1,2,3,4] should return true + let mut head = true; // true if `zelf` is the head of `other` + + for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { + let lt = vm.call_method(&a.clone(), "__lt__", vec![b.clone()])?; + let eq = vm.call_method(&a.clone(), "__eq__", vec![b.clone()])?; + let lt_value = objbool::boolval(vm, lt)?; + let eq_value = objbool::boolval(vm, eq)?; + + if !lt_value && !eq_value { + return Ok(false); + } else if !eq_value { + head = false; + } + } + + if head { + Ok(zelf.len() < other.len()) + } else { + Ok(true) + } + } +} + +pub fn seq_gt( + vm: &mut VirtualMachine, + zelf: &Vec, + other: &Vec, +) -> Result { + if zelf.len() == other.len() { + for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { + let gt = vm.call_method(&a.clone(), "__gt__", vec![b.clone()])?; + let value = objbool::boolval(vm, gt)?; + if !value { + return Ok(false); + } + } + Ok(true) + } else { + let mut head = true; // true if `other` is the head of `zelf` + for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { + // This case is more complicated because it can still return true if + // `other` is the head of `zelf` e.g. [1,2,3,4] > [1,2,3] should return true + let gt = vm.call_method(&a.clone(), "__gt__", vec![b.clone()])?; + let eq = vm.call_method(&a.clone(), "__eq__", vec![b.clone()])?; + let gt_value = objbool::boolval(vm, gt)?; + let eq_value = objbool::boolval(vm, eq)?; + + if !gt_value && !eq_value { + return Ok(false); + } else if !eq_value { + head = false; + } + } + + if head { + Ok(zelf.len() > other.len()) + } else { + Ok(true) + } + } +} + +pub fn seq_ge( + vm: &mut VirtualMachine, + zelf: &Vec, + other: &Vec, +) -> Result { + Ok(seq_gt(vm, zelf, other)? || seq_equal(vm, zelf, other)?) +} + +pub fn seq_le( + vm: &mut VirtualMachine, + zelf: &Vec, + other: &Vec, +) -> Result { + Ok(seq_lt(vm, zelf, other)? || seq_equal(vm, zelf, other)?) +} + pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { Ref::map(obj.borrow(), |x| { if let PyObjectPayload::Sequence { ref elements } = x.payload {