diff --git a/src/obj/list.rs b/src/obj/list.rs index 062b13d..1f0ce5d 100644 --- a/src/obj/list.rs +++ b/src/obj/list.rs @@ -125,7 +125,7 @@ impl List { if index == this.list().len() { // if this is the last item in the list, then we're done let new_str = format!("{}{}]", build_str.borrow(), repr_str.borrow()); - FunctionResult::ReturnPush(Str::create(new_str)) + Str::create(new_str).into() } else { // otherwise, continue building the string and calling to_repr let new_str = format!("{}{}, ", build_str.borrow(), repr_str.borrow()); @@ -145,6 +145,13 @@ impl List { } } + pub(crate) fn to_list(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + // create a clone of this list + let this = vm.frame_stack()[0].clone(); + let list_items = with_obj_downcast(this, |list: &List| list.list().clone()); + List::create(list_items).into() + } + impl_do_call!(to_list); pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { @@ -182,7 +189,7 @@ impl List { } }); - FunctionResult::ReturnPush(item) + item.into() } pub(crate) fn len(vm: &mut Vm, _state: FunctionState) -> FunctionResult { @@ -195,7 +202,7 @@ impl List { let this = vm.frame_stack()[0].clone(); let arg = vm.frame_stack()[1].clone(); with_obj_downcast_mut(this, |list: &mut List| list.list_mut().push(arg)); - FunctionResult::ReturnPush(Nil::create()) + Nil::create().into() } pub(crate) fn pop(vm: &mut Vm, _state: FunctionState) -> FunctionResult { @@ -210,6 +217,27 @@ impl List { todo!("throw an exception when the list is empty and there is nothing to pop") }; - FunctionResult::ReturnPush(last) + last.into() + } + + pub(crate) fn extend(vm: &mut Vm, _state: FunctionState) -> FunctionResult { + let this = vm.frame_stack()[0].clone(); + let arg = vm.frame_stack()[1].clone(); + + // we have to clone here, in case this and arg are the same list. otherwise, we couldn't + // borrow `this` mutably while using `list` immutably. + let list_extension = if let Some(list) = arg.borrow().as_any().downcast_ref::() { + list.list().clone() + } else { + // TODO List::extend - throw an exception when the argument is not a list + // BLOCKED-ON: exceptions + todo!("throw an exception when the List.extend argument is not a list") + }; + + with_obj_downcast_mut(this, |list: &mut List| { + list.list_mut().extend(list_extension); + }); + + Nil::create().into() } } diff --git a/src/obj/ty.rs b/src/obj/ty.rs index 7dcba81..eecade5 100644 --- a/src/obj/ty.rs +++ b/src/obj/ty.rs @@ -190,6 +190,7 @@ pub fn init_types() { List { // Conversion methods to_repr => BuiltinFunction::create("to_repr", List::to_repr, 1), + to_list => BuiltinFunction::create("to_list", List::to_list, 1), // Constructor __call__ => BuiltinFunction::create("__call__", List::do_call, 2), @@ -203,6 +204,7 @@ pub fn init_types() { push => BuiltinFunction::create("push", List::push, 2), pop => BuiltinFunction::create("pop", List::pop, 1), + extend => BuiltinFunction::create("extend", List::extend, 2), }, Str { // Conversion methods diff --git a/tests/list.npp b/tests/list.npp new file mode 100644 index 0000000..b4ebae4 --- /dev/null +++ b/tests/list.npp @@ -0,0 +1,51 @@ +# List type operator and function tests + +a = [] + +println("to_str") +println(a.to_str()) +println([1, 2, 3]) +println(["a", "b", "c"]) +println(["a\nb\nc"]) + +println("push and pop") +a.push(1234) +a.push(56) +println(a) +println(a.pop()) +println(a) +a.push(99) +a.push(100) +println(a) +a.pop() +a.pop() +a.push('99') +a.push('100') +println(a) + +println("len") +println(a.len()) +a.pop() +println(a.len()) +a.pop() +println(a.len()) +a.pop() +println(a.len()) + +println("extend") +a.extend([1, 2, 3]) +println(a) +a.extend([1, 2, 3]) +println(a) +a.extend(['a', 's', 'd', 'f']) +println(a) + +println("constructor") +println(List("asdf")) +println(List([1, 2, 3])) +# ensure that creating a new list actually clones it +b = [1, 2, 3] +c = List(b) +c.pop() +println(b) +println(c) diff --git a/tests/list.npp.expect b/tests/list.npp.expect new file mode 100644 index 0000000..e6e0f12 --- /dev/null +++ b/tests/list.npp.expect @@ -0,0 +1,25 @@ +to_str +[] +[1, 2, 3] +['a', 'b', 'c'] +['a\nb\nc'] +push and pop +[1234, 56] +56 +[1234] +[1234, 99, 100] +[1234, '99', '100'] +len +3 +2 +1 +0 +extend +[1, 2, 3] +[1, 2, 3, 1, 2, 3] +[1, 2, 3, 1, 2, 3, 'a', 's', 'd', 'f'] +constructor +['a', 's', 'd', 'f'] +[1, 2, 3] +[1, 2, 3] +[1, 2]