diff --git a/src/eval/array.rs b/src/eval/array.rs index f62bda9f9..38876c960 100644 --- a/src/eval/array.rs +++ b/src/eval/array.rs @@ -73,11 +73,6 @@ impl Array { Rc::make_mut(&mut self.vec).push(value); } - /// Extend the array with the values from another array. - pub fn extend(&mut self, other: &Array) { - Rc::make_mut(&mut self.vec).extend(other.into_iter()) - } - /// Clear the array. pub fn clear(&mut self) { if Rc::strong_count(&mut self.vec) == 1 { @@ -90,19 +85,13 @@ impl Array { /// Repeat this array `n` times. pub fn repeat(&self, n: usize) -> Self { let len = self.len().checked_mul(n).expect("capacity overflow"); - self.into_iter().cycle().take(len).collect() + self.iter().cloned().cycle().take(len).collect() } /// Iterate over references to the contained values. pub fn iter(&self) -> std::slice::Iter { self.vec.iter() } - - /// Iterate over the contained values. - pub fn into_iter(&self) -> impl Iterator + Clone + '_ { - // TODO: Actually consume the vector if the ref-count is 1? - self.iter().cloned() - } } impl Default for Array { @@ -117,12 +106,48 @@ impl Debug for Array { } } +impl Add for Array { + type Output = Self; + + fn add(mut self, rhs: Array) -> Self::Output { + self += rhs; + self + } +} + +impl AddAssign for Array { + fn add_assign(&mut self, rhs: Array) { + match Rc::try_unwrap(rhs.vec) { + Ok(vec) => self.extend(vec), + Err(rc) => self.extend(rc.iter().cloned()), + } + } +} + impl FromIterator for Array { fn from_iter>(iter: T) -> Self { Array { vec: Rc::new(iter.into_iter().collect()) } } } +impl Extend for Array { + fn extend>(&mut self, iter: T) { + Rc::make_mut(&mut self.vec).extend(iter); + } +} + +impl IntoIterator for Array { + type Item = Value; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + match Rc::try_unwrap(self.vec) { + Ok(vec) => vec.into_iter(), + Err(rc) => (*rc).clone().into_iter(), + } + } +} + impl<'a> IntoIterator for &'a Array { type Item = &'a Value; type IntoIter = std::slice::Iter<'a, Value>; @@ -131,18 +156,3 @@ impl<'a> IntoIterator for &'a Array { self.iter() } } - -impl Add<&Array> for Array { - type Output = Self; - - fn add(mut self, rhs: &Array) -> Self::Output { - self.extend(rhs); - self - } -} - -impl AddAssign<&Array> for Array { - fn add_assign(&mut self, rhs: &Array) { - self.extend(rhs); - } -} diff --git a/src/eval/dict.rs b/src/eval/dict.rs index c1c3f5052..bc7df2b8f 100644 --- a/src/eval/dict.rs +++ b/src/eval/dict.rs @@ -60,11 +60,6 @@ impl Dict { Rc::make_mut(&mut self.map).insert(key, value); } - /// Extend the dictionary with the values from another dictionary. - pub fn extend(&mut self, other: &Dict) { - Rc::make_mut(&mut self.map).extend(other.into_iter()) - } - /// Clear the dictionary. pub fn clear(&mut self) { if Rc::strong_count(&mut self.map) == 1 { @@ -74,12 +69,6 @@ impl Dict { } } - /// Iterate over pairs of the contained keys and values. - pub fn into_iter(&self) -> impl Iterator + Clone + '_ { - // TODO: Actually consume the map if the ref-count is 1? - self.iter().map(|(k, v)| (k.clone(), v.clone())) - } - /// Iterate over pairs of references to the contained keys and values. pub fn iter(&self) -> std::collections::btree_map::Iter { self.map.iter() @@ -98,12 +87,48 @@ impl Debug for Dict { } } +impl Add for Dict { + type Output = Self; + + fn add(mut self, rhs: Dict) -> Self::Output { + self += rhs; + self + } +} + +impl AddAssign for Dict { + fn add_assign(&mut self, rhs: Dict) { + match Rc::try_unwrap(rhs.map) { + Ok(map) => self.extend(map), + Err(rc) => self.extend(rc.iter().map(|(k, v)| (k.clone(), v.clone()))), + } + } +} + impl FromIterator<(EcoString, Value)> for Dict { fn from_iter>(iter: T) -> Self { Dict { map: Rc::new(iter.into_iter().collect()) } } } +impl Extend<(EcoString, Value)> for Dict { + fn extend>(&mut self, iter: T) { + Rc::make_mut(&mut self.map).extend(iter); + } +} + +impl IntoIterator for Dict { + type Item = (EcoString, Value); + type IntoIter = std::collections::btree_map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + match Rc::try_unwrap(self.map) { + Ok(map) => map.into_iter(), + Err(rc) => (*rc).clone().into_iter(), + } + } +} + impl<'a> IntoIterator for &'a Dict { type Item = (&'a EcoString, &'a Value); type IntoIter = std::collections::btree_map::Iter<'a, EcoString, Value>; @@ -112,18 +137,3 @@ impl<'a> IntoIterator for &'a Dict { self.iter() } } - -impl Add<&Dict> for Dict { - type Output = Self; - - fn add(mut self, rhs: &Dict) -> Self::Output { - self.extend(rhs); - self - } -} - -impl AddAssign<&Dict> for Dict { - fn add_assign(&mut self, rhs: &Dict) { - self.extend(rhs); - } -} diff --git a/src/eval/ops.rs b/src/eval/ops.rs index 6408c6c35..df8babe26 100644 --- a/src/eval/ops.rs +++ b/src/eval/ops.rs @@ -11,10 +11,10 @@ pub fn join(lhs: Value, rhs: Value) -> Result { (a, None) => a, (None, b) => b, - (Str(a), Str(b)) => Str(a + &b), - (Array(a), Array(b)) => Array(a + &b), - (Dict(a), Dict(b)) => Dict(a + &b), - (Template(a), Template(b)) => Template(a + &b), + (Str(a), Str(b)) => Str(a + b), + (Array(a), Array(b)) => Array(a + b), + (Dict(a), Dict(b)) => Dict(a + b), + (Template(a), Template(b)) => Template(a + b), (Template(a), Str(b)) => Template(a + b), (Str(a), Template(b)) => Template(a + b), @@ -74,10 +74,10 @@ pub fn add(lhs: Value, rhs: Value) -> Value { (Fractional(a), Fractional(b)) => Fractional(a + b), - (Str(a), Str(b)) => Str(a + &b), - (Array(a), Array(b)) => Array(a + &b), - (Dict(a), Dict(b)) => Dict(a + &b), - (Template(a), Template(b)) => Template(a + &b), + (Str(a), Str(b)) => Str(a + b), + (Array(a), Array(b)) => Array(a + b), + (Dict(a), Dict(b)) => Dict(a + b), + (Template(a), Template(b)) => Template(a + b), (Template(a), Str(b)) => Template(a + b), (Str(a), Template(b)) => Template(a + b), diff --git a/src/eval/template.rs b/src/eval/template.rs index 0be2492be..33f2b8960 100644 --- a/src/eval/template.rs +++ b/src/eval/template.rs @@ -1,12 +1,12 @@ use std::collections::HashMap; use std::fmt::{self, Debug, Formatter}; -use std::ops::{Add, Deref}; +use std::ops::{Add, AddAssign, Deref}; use std::rc::Rc; use super::Value; -use crate::util::EcoString; use crate::exec::ExecContext; use crate::syntax::{Expr, SyntaxTree}; +use crate::util::EcoString; /// A template value: `[*Hi* there]`. #[derive(Default, Debug, Clone)] @@ -50,15 +50,25 @@ impl PartialEq for Template { } } -impl Add<&Template> for Template { +impl Add for Template { type Output = Self; - fn add(mut self, rhs: &Self) -> Self::Output { - Rc::make_mut(&mut self.nodes).extend(rhs.nodes.iter().cloned()); + fn add(mut self, rhs: Self) -> Self::Output { + self += rhs; self } } +impl AddAssign for Template { + fn add_assign(&mut self, rhs: Template) { + let sink = Rc::make_mut(&mut self.nodes); + match Rc::try_unwrap(rhs.nodes) { + Ok(source) => sink.extend(source), + Err(rc) => sink.extend(rc.iter().cloned()), + } + } +} + impl Add for Template { type Output = Self; diff --git a/src/util/eco.rs b/src/util/eco.rs index a52a163c4..7fe1ac9c7 100644 --- a/src/util/eco.rs +++ b/src/util/eco.rs @@ -274,14 +274,20 @@ impl PartialOrd for EcoString { } } -impl Add<&Self> for EcoString { +impl Add for EcoString { type Output = Self; - fn add(self, rhs: &Self) -> Self::Output { + fn add(self, rhs: Self) -> Self::Output { self + rhs.as_str() } } +impl AddAssign for EcoString { + fn add_assign(&mut self, rhs: EcoString) { + self.push_str(&rhs); + } +} + impl Add<&str> for EcoString { type Output = Self;