typed_sexp/
lib.rs

1//! # Typed-SEXP
2//!
3//! Type-safe manipulation of R's SEXP objects without bells and whistles.
4//!
5//! ## Objectives
6//!
7//! R internal API is very confusing and not type-safe.
8//!
9//! However many times I do not want to pull in huge proc macros just to write my R extension.
10//! The main reasons are:
11//!   - Macros in general are hard to debug.
12//!   - Macros mess up static analysis tools.
13//!   - Macros hide the actual code that is being generated.
14//!
15//! This library is an attempt to provide a type-safe interface to R's SEXP objects without any user-exposed macros.
16//! However it is not a goal to provide a high-level abstractions over R's API like `Rcpp` or `extendr`, the most I will do is type-safe indexing, attributes, calling functions, etc.
17//! Users are expected to have a general understanding of R internals.
18//! Here is a quick [tutorial](https://github.com/hadley/r-internals).
19//!
20//! ## Features
21//!
22//! - Typed SEXP objects that are ABI-compatible with R's SEXP.
23//! - Type-safe (mutable) indexing of vectors and matrices.
24//! - Typed protection of SEXP objects with a debug mode that sanity-checks the protection order.
25//! - Stack RAII-based auto un-protection of SEXP objects.
26//! - Downcasting of SEXP objects in a method chain with a single call (even if the object is wrapped in another abstraction).
27//! - Namespace and function objects that are environment-aware.
28//! - Call R functions with type-safe arguments and return values.
29//! - (TODO): Dynamically create R functions.
30#![warn(missing_docs)]
31
32use libR_sys::{SEXPTYPE::*, *};
33
34use protect::{BoxProtected, Protected};
35
36pub use libR_sys;
37pub use libR_sys::SEXP;
38use sexp::{
39    env::{Env, Symbol},
40    function::{Builtin, Closure, Function},
41    lang::{Lang, PairlistBuilder},
42    matrix::Matrix,
43    vector::{CharacterVector, IntegerVector, List, LogicalVector, RealVector},
44};
45
46pub(crate) mod debug;
47#[cfg(feature = "embedded")]
48pub mod embedded;
49pub mod message;
50pub mod prelude;
51pub mod protect;
52pub mod sexp;
53
54/// Any supported SEXP type.
55#[allow(missing_docs)]
56#[derive(Debug)]
57pub enum AnySexp<T: JustSEXP> {
58    Nil(T),
59    Logical(LogicalVector<T>),
60    LogicalMatrix(Matrix<LogicalVector<T>>),
61    Real(RealVector<T>),
62    RealMatrix(Matrix<RealVector<T>>),
63    Integer(IntegerVector<T>),
64    IntegerMatrix(Matrix<IntegerVector<T>>),
65    Character(CharacterVector<T>),
66    CharacterMatrix(Matrix<CharacterVector<T>>),
67    List(List<T>),
68    Symbol(Symbol<T>),
69    Lang(Lang<T>),
70    Function(Function<T>),
71    Builtin(Builtin<T>),
72    Closure(Closure<T>),
73    Environment(Env<T>),
74    Other(T),
75}
76
77impl<T: JustSEXP> From<T> for AnySexp<T> {
78    fn from(value: T) -> Self {
79        let sexp = value.get_sexp();
80        match unsafe { TYPEOF(sexp) } {
81            NILSXP => AnySexp::Nil(value),
82            LGLSXP => Matrix::wrap_sexp(sexp)
83                .map(AnySexp::LogicalMatrix)
84                .unwrap_or_else(|| {
85                    AnySexp::Logical(unsafe { LogicalVector::wrap_sexp_unchecked(sexp) })
86                }),
87            REALSXP => Matrix::wrap_sexp(sexp)
88                .map(AnySexp::RealMatrix)
89                .unwrap_or_else(|| AnySexp::Real(unsafe { RealVector::wrap_sexp_unchecked(sexp) })),
90            INTSXP => Matrix::wrap_sexp(sexp)
91                .map(AnySexp::IntegerMatrix)
92                .unwrap_or_else(|| {
93                    AnySexp::Integer(unsafe { IntegerVector::wrap_sexp_unchecked(sexp) })
94                }),
95            STRSXP => Matrix::wrap_sexp(sexp)
96                .map(AnySexp::CharacterMatrix)
97                .unwrap_or_else(|| {
98                    AnySexp::Character(unsafe { CharacterVector::wrap_sexp_unchecked(sexp) })
99                }),
100            SYMSXP => AnySexp::Symbol(unsafe { Symbol::wrap_sexp_unchecked(sexp) }),
101            LANGSXP => AnySexp::Lang(unsafe { Lang::wrap_sexp_unchecked(sexp) }),
102            ENVSXP => AnySexp::Environment(unsafe { Env::wrap_sexp_unchecked(sexp) }),
103            FUNSXP => AnySexp::Function(unsafe { Function::wrap_sexp_unchecked(sexp) }),
104            VECSXP => AnySexp::List(unsafe { List::wrap_sexp_unchecked(sexp) }),
105            _ => {
106                if let Some(builtin) = Builtin::<T>::wrap_sexp(sexp) {
107                    AnySexp::Builtin(builtin)
108                } else if let Some(closure) = Closure::<T>::wrap_sexp(sexp) {
109                    AnySexp::Closure(closure)
110                } else {
111                    AnySexp::Other(value)
112                }
113            }
114        }
115    }
116}
117
118impl<T: JustSEXP> AnySexp<T> {
119    /// Get the inner value.
120    pub fn into_inner(self) -> T {
121        match self {
122            AnySexp::Nil(value) => value,
123            AnySexp::Logical(value) => value.upcast(),
124            AnySexp::LogicalMatrix(value) => value.upcast(),
125            AnySexp::Real(value) => value.upcast(),
126            AnySexp::RealMatrix(value) => value.upcast(),
127            AnySexp::Integer(value) => value.upcast(),
128            AnySexp::IntegerMatrix(value) => value.upcast(),
129            AnySexp::Character(value) => value.upcast(),
130            AnySexp::CharacterMatrix(value) => value.upcast(),
131            AnySexp::Symbol(value) => value.upcast(),
132            AnySexp::Lang(value) => value.upcast(),
133            AnySexp::Function(value) => value.upcast(),
134            AnySexp::Builtin(value) => value.upcast(),
135            AnySexp::Closure(value) => value.upcast(),
136            AnySexp::Environment(value) => value.upcast(),
137            AnySexp::List(value) => value.upcast(),
138            AnySexp::Other(value) => value,
139        }
140    }
141    /// Get a reference to the inner value.
142    pub fn inner_ref(&self) -> &T {
143        match self {
144            AnySexp::Nil(value) => value,
145            AnySexp::Logical(value) => value.inner_ref(),
146            AnySexp::LogicalMatrix(value) => value.inner_ref(),
147            AnySexp::Real(value) => value.inner_ref(),
148            AnySexp::RealMatrix(value) => value.inner_ref(),
149            AnySexp::Integer(value) => value.inner_ref(),
150            AnySexp::IntegerMatrix(value) => value.inner_ref(),
151            AnySexp::Character(value) => value.inner_ref(),
152            AnySexp::CharacterMatrix(value) => value.inner_ref(),
153            AnySexp::Symbol(value) => value.inner_ref(),
154            AnySexp::Lang(value) => value.inner_ref(),
155            AnySexp::Function(value) => value.inner_ref(),
156            AnySexp::Builtin(value) => value.inner_ref(),
157            AnySexp::Closure(value) => value.inner_ref(),
158            AnySexp::Environment(value) => value.inner_ref(),
159            AnySexp::List(value) => value.inner_ref(),
160            AnySexp::Other(value) => value,
161        }
162    }
163}
164impl<T: JustSEXP> HasSEXP for AnySexp<T> {
165    fn get_sexp(&self) -> SEXP {
166        match self {
167            AnySexp::Nil(value) => value.get_sexp(),
168            AnySexp::Logical(value) => value.get_sexp(),
169            AnySexp::LogicalMatrix(value) => value.get_sexp(),
170            AnySexp::Real(value) => value.get_sexp(),
171            AnySexp::RealMatrix(value) => value.get_sexp(),
172            AnySexp::Integer(value) => value.get_sexp(),
173            AnySexp::IntegerMatrix(value) => value.get_sexp(),
174            AnySexp::Character(value) => value.get_sexp(),
175            AnySexp::CharacterMatrix(value) => value.get_sexp(),
176            AnySexp::Symbol(value) => value.get_sexp(),
177            AnySexp::Lang(value) => value.get_sexp(),
178            AnySexp::Function(value) => value.get_sexp(),
179            AnySexp::Builtin(value) => value.get_sexp(),
180            AnySexp::Closure(value) => value.get_sexp(),
181            AnySexp::Environment(value) => value.get_sexp(),
182            AnySexp::List(value) => value.get_sexp(),
183            AnySexp::Other(value) => value.get_sexp(),
184        }
185    }
186}
187
188/// The null SEXP.
189pub fn null() -> SEXP {
190    unsafe { R_NilValue }
191}
192
193/// A trait for objects that have an static underlying [`SEXPTYPE`].
194pub trait TypedSEXP: HasSEXP {
195    /// The type of the SEXP.
196    const SEXP_TYPE: SEXPTYPE;
197}
198
199/// A trait for objects that have an underlying [`SEXP`].
200pub trait HasSEXP {
201    /// Get the underlying SEXP.
202    fn get_sexp(&self) -> SEXP;
203
204    /// Check if the SEXP is null.
205    fn is_sexp_null(&self) -> bool {
206        unsafe { self.get_sexp() == libR_sys::R_NilValue }
207    }
208
209    /// Get the type of the SEXP.
210    fn sexp_type(&self) -> SEXPTYPE {
211        unsafe { TYPEOF(self.get_sexp()) }
212    }
213
214    /// Print the value to the R console.
215    fn r_print(&self) {
216        unsafe {
217            Rf_PrintValue(self.get_sexp());
218        }
219    }
220
221    /// Get the attribute of the SEXP.
222    fn attrib(&self, tag: SEXP) -> SEXP {
223        unsafe { Rf_getAttrib(self.get_sexp(), tag) }
224    }
225
226    /// Coerce the underlying SEXP to the given type.
227    fn coerce(&self, sexp_type: SEXPTYPE) -> SEXP {
228        unsafe { Rf_coerceVector(self.get_sexp(), sexp_type) }
229    }
230
231    /// Protect the object in the protection stack.
232    fn protect(self) -> Protected<Self>
233    where
234        Self: Sized,
235    {
236        Protected::new(self)
237    }
238
239    /// Protect the object in the protection heap.
240    fn protect_box(self) -> BoxProtected<Self>
241    where
242        Self: Sized,
243    {
244        BoxProtected::new(self)
245    }
246
247    /// Shorthand for `DowncastSEXP::downcast`.
248    fn downcast_to<T: HasSEXP>(self) -> Option<T>
249    where
250        Self: DowncastSEXP<T>,
251    {
252        self.downcast()
253    }
254}
255
256impl HasSEXP for SEXP {
257    fn get_sexp(&self) -> SEXP {
258        *self
259    }
260}
261
262/// A [`SEXP`] wrapper that can be indexed.
263pub trait IndexableSEXP: HasSEXP {
264    /// The type of the index.
265    type Index;
266    /// The type of the output.
267    type Output;
268    #[must_use]
269    /// The scalar length of the object.
270    fn len(&self) -> usize;
271    /// Check if the index is inbound.
272    fn check_inbound(&self, index: usize) {
273        if index >= self.len() {
274            panic!(
275                "index out of bounds: the len is {} but the index is {}",
276                self.len(),
277                index
278            );
279        }
280    }
281
282    /// Get the element at the given index.
283    fn get_elt(&self, index: Self::Index) -> Self::Output;
284    /// Set the element at the given index.
285    fn set_elt(&mut self, index: Self::Index, value: impl Into<Self::Output>);
286}
287
288/// Trivial, ABI-compatible wrapper types around [`SEXP`].
289///
290/// Marking the wrong thing is ridiculously unsafe and should be used with caution.
291///
292/// It is deliberately not possible to get the inner SEXP from this type without destroying it.
293/// Because the underlying assumption implementors have may be broken.
294///
295/// # Safety
296///
297/// - Both types should have the same size and alignment as [`SEXP`], and should just be a wrapper around it.
298/// - [`Drop`] implementation for this type may or may not be called.
299pub unsafe trait JustSEXP: HasSEXP + Sized {
300    /// The inner type that this type wraps, of course it has to be also [`JustSEXP`].
301    type Inner: JustSEXP;
302    /// Transmute this type to another type that wraps the same [`SEXP`].
303    ///
304    /// # Safety
305    ///
306    /// Although this is memory safe, be sure that the target type is expecting the same type of SEXP.
307    unsafe fn transmute_to<U: JustSEXP>(self) -> U {
308        let sexp = self.get_sexp();
309        std::mem::forget(self);
310        U::wrap_sexp_unchecked(sexp)
311    }
312
313    /// Create a new object by wrapping a SEXP.
314    fn wrap_sexp(sexp: SEXP) -> Option<Self> {
315        Some(unsafe { Self::wrap_sexp_unchecked(sexp) })
316    }
317
318    /// Create a new object by wrapping a SEXP without checking the type.
319    unsafe fn wrap_sexp_unchecked(sexp: SEXP) -> Self;
320
321    /// Upcast this type to the inner type.
322    fn upcast(self) -> Self::Inner;
323
324    /// Get a reference to the inner type.
325    fn inner_ref(&self) -> &Self::Inner;
326}
327
328unsafe impl JustSEXP for SEXP {
329    type Inner = SEXP;
330
331    fn upcast(self) -> Self::Inner {
332        self
333    }
334
335    unsafe fn wrap_sexp_unchecked(sexp: SEXP) -> Self {
336        sexp
337    }
338
339    fn inner_ref(&self) -> &Self::Inner {
340        self
341    }
342}
343
344/// Trait for types whose [`SEXP`] is protected.
345pub unsafe trait ProtectedSEXP: HasSEXP {
346    /// The inner type that this type wraps.
347    type Inner: HasSEXP;
348
349    /// Forget the object and return it.
350    fn forget(self) -> Self::Inner;
351
352    /// Unprotect the object and return it.
353    fn unprotect(self) -> Self::Inner;
354
355    /// Start building a pairlist with this object.
356    fn build_pairlist(self) -> PairlistBuilder<Self>
357    where
358        Self: Sized,
359        Self::Inner: JustSEXP,
360    {
361        PairlistBuilder::new(self)
362    }
363}
364
365/// Type refinement of another [`SEXP`]-wrapping type.
366///
367/// The [`HasSEXP::downcast_to`] method enables this trait to be used within a method chain.
368///
369pub trait DowncastSEXP<T: HasSEXP>: HasSEXP + Sized {
370    /// Attempt to downcast the type to another type.
371    fn downcast(self) -> Option<T>;
372}
373
374impl<T: HasSEXP> DowncastSEXP<T> for T {
375    fn downcast(self) -> Option<T> {
376        Some(self)
377    }
378}