extendr_api/robj/
operators.rs

1use crate as extendr_api;
2use crate::*;
3use extendr_ffi::Rf_lcons;
4use std::ops::{Add, Div, Mul, Sub};
5///////////////////////////////////////////////////////////////
6/// The following impls add operators to Robj.
7///
8pub trait Operators: Rinternals {
9    /// Do the equivalent of x$y
10    /// ```
11    /// use extendr_api::prelude::*;
12    /// test! {
13    /// let env = Environment::from_pairs(global_env(),
14    ///    vec![("a".to_string(), r!(1)), ("b".to_string(), r!(2))]);
15    /// assert_eq!(env.dollar("a").unwrap(), r!(1));
16    /// assert_eq!(env.dollar("b").unwrap(), r!(2));
17    /// }
18    /// ```
19    fn dollar<T>(&self, symbol: T) -> Result<Robj>
20    where
21        T: AsRef<str>,
22    {
23        let symbol: Symbol = Symbol::from_string(symbol.as_ref());
24        call!("`$`", self.as_robj(), symbol)
25    }
26
27    /// Do the equivalent of `x[y]`
28    /// ```
29    /// use extendr_api::prelude::*;
30    /// test! {
31    /// let vec = r!([10, 20, 30]);
32    /// assert_eq!(vec.slice(2).unwrap(), r!(20));
33    /// assert_eq!(vec.slice(2..=3).unwrap(), r!([20, 30]));
34    /// }
35    /// ```
36    fn slice<T>(&self, rhs: T) -> Result<Robj>
37    where
38        T: Into<Robj>,
39    {
40        call!("`[`", self.as_robj(), rhs.into())
41    }
42
43    /// Do the equivalent of `x[[y]]`
44    /// ```
45    /// use extendr_api::prelude::*;
46    /// test! {
47    /// let vec = r!([10, 20, 30]);
48    /// assert_eq!(vec.index(2).unwrap(), r!(20));
49    /// assert_eq!(vec.index(2..=3).is_err(), true);
50    /// }
51    /// ```
52    fn index<T>(&self, rhs: T) -> Result<Robj>
53    where
54        T: Into<Robj>,
55    {
56        call!("`[[`", self.as_robj(), rhs.into())
57    }
58
59    /// Do the equivalent of x ~ y
60    /// ```
61    /// use extendr_api::prelude::*;
62    /// test! {
63    ///     let x = r!(Symbol::from_string("x"));
64    ///     let y = r!(Symbol::from_string("y"));
65    ///     let tilde = x.tilde(y).unwrap();
66    ///     assert_eq!(tilde.inherits("formula"), true);
67    /// }
68    /// ```
69    fn tilde<T>(&self, rhs: T) -> Result<Robj>
70    where
71        T: Into<Robj>,
72    {
73        call!("`~`", self.as_robj(), rhs.into())
74    }
75
76    /// Do the equivalent of x :: y
77    /// ```
78    /// use extendr_api::prelude::*;
79    /// test! {
80    ///     let base = r!(Symbol::from_string("base"));
81    ///     let list = r!(Symbol::from_string("list"));
82    ///     let base_list = base.double_colon(list).unwrap();
83    ///     assert_eq!(base_list.is_function(), true);
84    /// }
85    /// ```
86    fn double_colon<T>(&self, rhs: T) -> Result<Robj>
87    where
88        T: Into<Robj>,
89    {
90        call!("`::`", self.as_robj(), rhs.into())
91    }
92
93    /// Do the equivalent of x(a, b, c)
94    /// ```
95    /// use extendr_api::prelude::*;
96    /// test! {
97    ///     let function = R!("function(a, b) a + b").unwrap();
98    ///     assert_eq!(function.is_function(), true);
99    ///     assert_eq!(function.call(pairlist!(a=1, b=2)).unwrap(), r!(3));
100    /// }
101    /// ```
102    fn call(&self, args: Pairlist) -> Result<Robj> {
103        if self.is_function() {
104            single_threaded(|| unsafe {
105                let call = Robj::from_sexp(Rf_lcons(self.get(), args.get()));
106                call.eval()
107            })
108        } else {
109            Err(Error::ExpectedFunction(self.as_robj().clone()))
110        }
111    }
112}
113
114impl<Rhs> Add<Rhs> for Robj
115where
116    Rhs: Into<Robj>,
117{
118    type Output = Robj;
119
120    /// Add two R objects, consuming the left hand side.
121    /// panics on error.
122    /// ```
123    /// use extendr_api::prelude::*;
124    /// test! {
125    ///
126    /// // lhs and rhs get dropped here
127    /// let lhs = r!([1, 2]);
128    /// let rhs = r!([10, 20]);
129    /// assert_eq!(lhs + rhs, r!([11, 22]));
130    ///
131    /// // lhs gets dropped and rhs is a temporary object.
132    /// let lhs = r!([1, 2]);
133    /// assert_eq!(lhs + 1000, r!([1001, 1002]));
134    ///
135    /// // Only lhs gets dropped.
136    /// let lhs = r!([1, 2]);
137    /// let rhs = r!([10, 20]);
138    /// assert_eq!(lhs + &rhs, r!([11, 22]));
139    /// }
140    /// ```
141    fn add(self, rhs: Rhs) -> Self::Output {
142        call!("`+`", self, rhs.into()).expect("Robj add failed")
143    }
144}
145
146impl<Rhs> Sub<Rhs> for Robj
147where
148    Rhs: Into<Robj>,
149{
150    type Output = Robj;
151
152    /// Subtract two R objects, consuming the left hand side.
153    /// panics on error.
154    /// ```
155    /// use extendr_api::prelude::*;
156    /// test! {
157    ///
158    /// // lhs and rhs get dropped here
159    /// let lhs = r!([10, 20]);
160    /// let rhs = r!([1, 2]);
161    /// assert_eq!(lhs - rhs, r!([9, 18]));
162    ///
163    /// // lhs gets dropped and rhs is a temporary object.
164    /// let lhs = r!([1000, 2000]);
165    /// assert_eq!(lhs - 1, r!([999, 1999]));
166    ///
167    /// // Only lhs gets dropped.
168    /// let lhs = r!([10, 20]);
169    /// let rhs = r!([1, 2]);
170    /// assert_eq!(lhs - &rhs, r!([9, 18]));
171    /// }
172    /// ```
173    fn sub(self, rhs: Rhs) -> Self::Output {
174        call!("`-`", self, rhs.into()).expect("Robj subtract failed")
175    }
176}
177
178impl<Rhs> Mul<Rhs> for Robj
179where
180    Rhs: Into<Robj>,
181{
182    type Output = Robj;
183
184    /// Multiply two R objects, consuming the left hand side.
185    /// panics on error.
186    /// ```
187    /// use extendr_api::prelude::*;
188    /// test! {
189    ///
190    /// // lhs and rhs get dropped here
191    /// let lhs = r!([10.0, 20.0]);
192    /// let rhs = r!([1.0, 2.0]);
193    /// assert_eq!(lhs * rhs, r!([10.0, 40.0]));
194    ///
195    /// // lhs gets dropped and rhs is a temporary object.
196    /// let lhs = r!([1.0, 2.0]);
197    /// assert_eq!(lhs * 10.0, r!([10.0, 20.0]));
198    ///
199    /// // Only lhs gets dropped.
200    /// let lhs = r!([10.0, 20.0]);
201    /// let rhs = r!([1.0, 2.0]);
202    /// assert_eq!(lhs * &rhs, r!([10.0, 40.0]));
203    /// }
204    /// ```
205    fn mul(self, rhs: Rhs) -> Self::Output {
206        call!("`*`", self, rhs.into()).expect("Robj multiply failed")
207    }
208}
209
210impl<Rhs> Div<Rhs> for Robj
211where
212    Rhs: Into<Robj>,
213{
214    type Output = Robj;
215
216    /// Divide two R objects, consuming the left hand side.
217    /// panics on error.
218    /// ```
219    /// use extendr_api::prelude::*;
220    /// test! {
221    ///
222    /// // lhs and rhs get dropped here
223    /// let lhs = r!([10.0, 20.0]);
224    /// let rhs = r!([1.0, 2.0]);
225    /// assert_eq!(lhs / rhs, r!([10.0, 10.0]));
226    ///
227    /// // lhs gets dropped and rhs is a temporary object.
228    /// let lhs = r!([10.0, 30.0]);
229    /// assert_eq!(lhs / 10.0, r!([1.0, 3.0]));
230    ///
231    /// // Only lhs gets dropped.
232    /// let lhs = r!([10.0, 20.0]);
233    /// let rhs = r!([1.0, 2.0]);
234    /// assert_eq!(lhs / &rhs, r!([10.0, 10.0]));
235    /// }
236    /// ```
237    fn div(self, rhs: Rhs) -> Self::Output {
238        call!("`/`", self, rhs.into()).expect("Robj divide failed")
239    }
240}
241
242impl Operators for Robj {}
243
244// Calls are still experimental.
245//
246// impl<Args> Fn(Args) for Robj
247// {
248//     extern "rust-call" fn call(&self, args: Args) -> Self::Output {
249
250//     }
251// }
252
253/* list of primitives in base.
254> b[sapply(b, function(b) is.primitive(get(b, baseenv())))]
255  [1] "-"               ":"               "!"               "!="
256  [5] "("               "["               "[["              "[[<-"
257  [9] "[<-"             "{"               "@"               "@<-"
258 [13] "*"               "/"               "&"               "&&"
259 [17] "%*%"             "%/%"             "%%"              "^"
260 [21] "+"               "<"               "<-"              "<<-"
261 [25] "<="              "="               "=="              ">"
262 [29] ">="              "|"               "||"              "~"
263 [33] "$"               "$<-"             "abs"             "acos"
264 [37] "acosh"           "all"             "any"             "anyNA"
265 [41] "Arg"             "as.call"         "as.character"    "as.complex"
266 [45] "as.double"       "as.environment"  "as.integer"      "as.logical"
267 [49] "as.numeric"      "as.raw"          "asin"            "asinh"
268 [53] "atan"            "atanh"           "attr"            "attr<-"
269 [57] "attributes"      "attributes<-"    "baseenv"         "break"
270 [61] "browser"         "c"               "call"            "ceiling"
271 [65] "class"           "class<-"         "Conj"            "cos"
272 [69] "cosh"            "cospi"           "cummax"          "cummin"
273 [73] "cumprod"         "cumsum"          "digamma"         "dim"
274 [77] "dim<-"           "dimnames"        "dimnames<-"      "emptyenv"
275 [81] "enc2native"      "enc2utf8"        "environment<-"   "exp"
276 [85] "expm1"           "expression"      "floor"           "for"
277 [89] "forceAndCall"    "function"        "gamma"           "gc.time"
278 [93] "globalenv"       "if"              "Im"              "interactive"
279 [97] "invisible"       "is.array"        "is.atomic"       "is.call"
280[101] "is.character"    "is.complex"      "is.double"       "is.environment"
281[105] "is.expression"   "is.finite"       "is.function"     "is.infinite"
282[109] "is.integer"      "is.language"     "is.list"         "is.logical"
283[113] "is.matrix"       "is.na"           "is.name"         "is.nan"
284[117] "is.null"         "is.numeric"      "is.object"       "is.pairlist"
285[121] "is.raw"          "is.recursive"    "is.single"       "is.symbol"
286[125] "isS4"            "lazyLoadDBfetch" "length"          "length<-"
287[129] "levels<-"        "lgamma"          "list"            "log"
288[133] "log10"           "log1p"           "log2"            "max"
289[137] "min"             "missing"         "Mod"             "names"
290[141] "names<-"         "nargs"           "next"            "nzchar"
291[145] "oldClass"        "oldClass<-"      "on.exit"         "pos.to.env"
292[149] "proc.time"       "prod"            "quote"           "range"
293[153] "Re"              "rep"             "repeat"          "retracemem"
294[157] "return"          "round"           "seq_along"       "seq_len"
295[161] "seq.int"         "sign"            "signif"          "sin"
296[165] "sinh"            "sinpi"           "sqrt"            "standardGeneric"
297[169] "storage.mode<-"  "substitute"      "sum"             "switch"
298[173] "tan"             "tanh"            "tanpi"           "tracemem"
299[177] "trigamma"        "trunc"           "unclass"         "untracemem"
300[181] "UseMethod"       "while"           "xtfrm"
301*/