diff --git a/lib/src/prelude.ms b/lib/src/prelude.ms index 5e33a76..a754d54 100644 --- a/lib/src/prelude.ms +++ b/lib/src/prelude.ms @@ -1,5 +1,6 @@ module Core.Prelude +import Core.Protos.Append import Core.Protos.Display import Core.Protos.Order @@ -35,9 +36,10 @@ extern type Str impl Order, Display end end -extern type List +extern type List impl Append extern def size end extern def inspect end + extern def append(item) end def first self[0] diff --git a/lib/src/protos.ms b/lib/src/protos.ms index 367d1af..74792e9 100644 --- a/lib/src/protos.ms +++ b/lib/src/protos.ms @@ -21,3 +21,7 @@ proto Order when order(other) < 0 then other else self end end end + +proto Append + def append(item) end +end diff --git a/src/rt/eval.rs b/src/rt/eval.rs index 2200641..e4e6446 100644 --- a/src/rt/eval.rs +++ b/src/rt/eval.rs @@ -901,34 +901,26 @@ impl<'syms, 'descs, 'rt> Interpreter<'syms, 'descs, 'rt> { } } BinaryOp::Append => { - let Val::Obj(obj) = lhs.clone() else { + if self + .rt + .conforms(lhs.type_id(), self.rt.behaviors.append.proto) + { + let span = chunk.get_expr_span(expr_id); + let member = self.rt.behaviors.append.method; + self.call_member(span, lhs.clone(), member, vec![(None, rhs)]) + } else { let loc = self .rt .pkgs .desc(self.pkg_id) .loc(chunk.get_expr_span(expr_id)); - return Err(Signal::raise( + Err(Signal::raise( Raised::Native(Error::ProtocolError( ProtocolError::NotAppendable(lhs.type_id()), )), vec![(String::new(), loc)], - )); - }; - let Obj::List(list) = &mut *obj.borrow_mut() else { - let loc = self - .rt - .pkgs - .desc(self.pkg_id) - .loc(chunk.get_expr_span(expr_id)); - return Err(Signal::raise( - Raised::Native(Error::ProtocolError( - ProtocolError::NotAppendable(obj.borrow().type_id()), - )), - vec![(String::new(), loc)], - )); - }; - list.items.push(rhs); - Ok(lhs) + )) + } } BinaryOp::Add => match (lhs, rhs) { (Val::Num(lhs), Val::Num(rhs)) => Ok(Val::Num(Num(lhs.0 + rhs.0))), diff --git a/src/rt/types.rs b/src/rt/types.rs index 634bf70..10c2970 100644 --- a/src/rt/types.rs +++ b/src/rt/types.rs @@ -179,7 +179,18 @@ pub const CORE_TYPES: &[NativeTypeSpec] = &[ NativeTypeSpec { name: "List", new: Some(List::new), - methods: &[("size", &[], List::size), ("inspect", &[], List::inspect)], + methods: &[ + ("size", &[], List::size), + ("inspect", &[], List::inspect), + ( + "append", + &[NativeParam { + name: "item", + default: None, + }], + List::append, + ), + ], statics: &[], }, NativeTypeSpec { diff --git a/src/rt/val.rs b/src/rt/val.rs index c968140..0a35d59 100644 --- a/src/rt/val.rs +++ b/src/rt/val.rs @@ -296,6 +296,15 @@ impl List { } Ok(Str::of(&print::print_list(&printed))) } + + pub fn append(_interp: &mut Interpreter, val: &Val, args: Vec) -> Result { + let Val::Obj(obj) = val else { panic!() }; + let Obj::List(list) = &mut *obj.borrow_mut() else { + panic!() + }; + list.items.push(args[0].clone()); + Ok(val.clone()) + } } #[derive(Debug)] diff --git a/src/sem/protos.rs b/src/sem/protos.rs index d409dda..7743ed8 100644 --- a/src/sem/protos.rs +++ b/src/sem/protos.rs @@ -62,6 +62,7 @@ pub struct Behaviors { pub order: Behavior, pub display: Behavior, pub inspect: Behavior, + pub append: Behavior, } impl Behaviors { @@ -72,6 +73,7 @@ impl Behaviors { // Automatic: every type has this method without opting in, so the type pass // seeds it long before the protocols can be resolved. pub const INSPECT: (&str, &str) = ("Inspect", "inspect"); + pub const APPEND: (&str, &str) = ("Append", "append"); } // Resolve the protocols of 'Core.Protos' governing built-in behavior, and the @@ -86,6 +88,7 @@ pub fn build_behaviors(syms: &mut Interner, pkgs: &Packages, stdlib: PackageId) order: build_behavior(syms, pkgs, stdlib, module, Behaviors::ORDER), display: build_behavior(syms, pkgs, stdlib, module, Behaviors::DISPLAY), inspect: build_behavior(syms, pkgs, stdlib, module, Behaviors::INSPECT), + append: build_behavior(syms, pkgs, stdlib, module, Behaviors::APPEND), } } diff --git a/tests/proto.ms b/tests/proto.ms index b5602e5..2517c71 100644 --- a/tests/proto.ms +++ b/tests/proto.ms @@ -210,6 +210,33 @@ $print(Task(1) >= Task(5)) #--- +import Core.Protos.Append + +type Collection(items: []) + impl Append + + def append(item) + items << item + self + end +end + +#> Collection(items: ["Item"]) +$print(Collection() << "Item") +#> Collection(items: ["a", "b"]) +$print(Collection() << "a" << "b") + +#--- + +#! runtime error: type Task is not appendable + +type Task(priority) +end + +Task(1) << 1 + +#--- + import Core.Protos.Display type Task(priority)