-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
08895ae
commit b0002ce
Showing
14 changed files
with
255 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
use savvy::{savvy, EnvironmentSexp, Sexp}; | ||
|
||
#[savvy] | ||
fn get_var_in_env(name: &str, env: Option<EnvironmentSexp>) -> savvy::Result<Sexp> { | ||
let env = env.unwrap_or(EnvironmentSexp::global_env()); | ||
let obj = env.get(name)?; | ||
obj.ok_or("Not found".into()) | ||
} | ||
|
||
#[savvy] | ||
fn var_exists_in_env(name: &str, env: Option<EnvironmentSexp>) -> savvy::Result<Sexp> { | ||
let env = env.unwrap_or(EnvironmentSexp::global_env()); | ||
env.contains(name)?.try_into() | ||
} | ||
|
||
#[savvy] | ||
fn set_var_in_env(name: &str, value: Sexp, env: Option<EnvironmentSexp>) -> savvy::Result<()> { | ||
let env = env.unwrap_or(EnvironmentSexp::global_env()); | ||
env.set(name, value) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
test_that("environment", { | ||
e1 <- new.env(parent = emptyenv()) | ||
e1$a <- "foo" | ||
|
||
expect_true(var_exists_in_env("a", e1)) | ||
expect_false(var_exists_in_env("b", e1)) | ||
|
||
expect_equal(get_var_in_env("a", e1), "foo") | ||
expect_error(get_var_in_env("b", e1)) | ||
|
||
# doesn't climb up the parent environments | ||
e2 <- new.env(parent = e1) | ||
expect_false(var_exists_in_env("a", e2)) | ||
|
||
set_var_in_env("c", 100L, e1) | ||
expect_equal(e1$c, 100L) | ||
# overwrite | ||
set_var_in_env("c", 300L, e1) | ||
expect_equal(e1$c, 300L) | ||
|
||
# global env | ||
.GlobalEnv$global_obj <- "ABC" | ||
expect_equal(get_var_in_env("global_obj"), "ABC") | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
use std::ffi::CString; | ||
|
||
use savvy_ffi::{R_GlobalEnv, R_NilValue, R_UnboundValue, Rboolean_FALSE, Rboolean_TRUE, SEXP}; | ||
|
||
use crate::Sexp; | ||
|
||
use super::utils::str_to_symsxp; | ||
|
||
/// An environment. | ||
pub struct EnvironmentSexp(pub SEXP); | ||
|
||
impl EnvironmentSexp { | ||
/// Returns the raw SEXP. | ||
#[inline] | ||
pub fn inner(&self) -> savvy_ffi::SEXP { | ||
self.0 | ||
} | ||
|
||
/// Returns the SEXP bound to a variable of the specified name in the | ||
/// specified environment. | ||
/// | ||
/// The absense of an object with the specified name is represented as | ||
/// `None`. `Some(NilSexp)` means there's a variable whose value is `NULL`. | ||
/// | ||
/// # Protection | ||
/// | ||
/// The result `Sexp` is unprotected. In most of the cases, you don't need | ||
/// to worry about this because existing in an environment means it won't be | ||
/// GC-ed as long as the environment exists (it's possible the correspondig | ||
/// variable gets explicitly removed, but it should be rare). However, if | ||
/// the environment is a temporary one (e.g. an exectuion environment of a | ||
/// function call), it's your responsibility to protect the object. In other | ||
/// words, you should never use this if you don't understand how R's | ||
/// protection mechanism works. | ||
pub fn get<T: AsRef<str>>(&self, name: T) -> crate::error::Result<Option<crate::Sexp>> { | ||
let sym = str_to_symsxp(name)?.ok_or("name must not be empty")?; | ||
|
||
// Note: since this SEXP already belongs to an environment, this doesn't | ||
// need protection. | ||
let sexp = unsafe { | ||
crate::unwind_protect(|| savvy_ffi::Rf_findVarInFrame3(self.0, sym, Rboolean_TRUE))? | ||
}; | ||
|
||
if sexp == unsafe { R_UnboundValue } { | ||
Ok(None) | ||
} else { | ||
Ok(Some(Sexp(sexp))) | ||
} | ||
} | ||
|
||
/// Returns `true` the specified environment contains the specified | ||
/// variable. | ||
pub fn contains<T: AsRef<str>>(&self, name: T) -> crate::error::Result<bool> { | ||
let sym = str_to_symsxp(name)?.ok_or("name must not be empty")?; | ||
|
||
let res = unsafe { | ||
crate::unwind_protect(|| savvy_ffi::Rf_findVarInFrame3(self.0, sym, Rboolean_FALSE))? | ||
!= R_UnboundValue | ||
}; | ||
|
||
Ok(res) | ||
} | ||
|
||
/// Bind the SEXP to the specified environment as the specified name. | ||
pub fn set<T: AsRef<str>>(&self, name: T, value: Sexp) -> crate::error::Result<()> { | ||
let name_cstr = match CString::new(name.as_ref()) { | ||
Ok(cstr) => cstr, | ||
Err(e) => return Err(crate::error::Error::new(&e.to_string())), | ||
}; | ||
|
||
unsafe { | ||
crate::unwind_protect(|| { | ||
savvy_ffi::Rf_defineVar(savvy_ffi::Rf_install(name_cstr.as_ptr()), value.0, self.0); | ||
R_NilValue | ||
})? | ||
}; | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Return the global env. | ||
pub fn global_env() -> Self { | ||
Self(unsafe { R_GlobalEnv }) | ||
} | ||
} | ||
|
||
// conversions from/to EnvironmentSexp *************** | ||
|
||
impl TryFrom<Sexp> for EnvironmentSexp { | ||
type Error = crate::error::Error; | ||
|
||
fn try_from(value: Sexp) -> crate::error::Result<Self> { | ||
value.assert_environment()?; | ||
Ok(Self(value.0)) | ||
} | ||
} | ||
|
||
impl From<EnvironmentSexp> for Sexp { | ||
fn from(value: EnvironmentSexp) -> Self { | ||
Self(value.inner()) | ||
} | ||
} | ||
|
||
impl From<EnvironmentSexp> for crate::error::Result<Sexp> { | ||
fn from(value: EnvironmentSexp) -> Self { | ||
Ok(<Sexp>::from(value)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.