Skip to content

Commit

Permalink
Merge pull request #19 from andrjohns/js-to-r-init
Browse files Browse the repository at this point in the history
Initial implementations for passing JS variables to R
  • Loading branch information
andrjohns authored May 13, 2024
2 parents 55ec408 + 7344b0f commit f47bfaa
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 5 deletions.
52 changes: 52 additions & 0 deletions inst/include/quickjsr/JSValue_to_Cpp.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#ifndef QUICKJSR_JSVALUE_TO_CPP_HPP
#define QUICKJSR_JSVALUE_TO_CPP_HPP

#include <quickjsr/type_traits.hpp>
#include <type_traits>
#include <string>
#include <vector>
#include <quickjs-libc.h>

namespace quickjsr {
template <typename T, std::enable_if_t<std::is_same<T, double>::value>* = nullptr>
T JSValue_to_Cpp(JSContext* ctx, JSValue val) {
double res;
JS_ToFloat64(ctx, &res, val);
return res;
}

template <typename T, std::enable_if_t<std::is_same<T, int32_t>::value>* = nullptr>
T JSValue_to_Cpp(JSContext* ctx, JSValue val) {
int32_t res;
JS_ToInt32(ctx, &res, val);
return res;
}

template <typename T, std::enable_if_t<std::is_same<T, bool>::value>* = nullptr>
T JSValue_to_Cpp(JSContext* ctx, JSValue val) {
return static_cast<bool>(JS_ToBool(ctx, val));
}

template <typename T, std::enable_if_t<std::is_same<T, std::string>::value>* = nullptr>
T JSValue_to_Cpp(JSContext* ctx, JSValue val) {
return JS_ToCString(ctx, val);
}

template <typename T, std::enable_if_t<is_std_vector<T>::value>* = nullptr>
T JSValue_to_Cpp(JSContext* ctx, JSValue val) {
T res;
uint32_t len;
JSValue arr_len = JS_GetPropertyStr(ctx, val, "length");
JS_ToUint32(ctx, &len, arr_len);
JS_FreeValue(ctx, arr_len);
for (uint32_t i = 0; i < len; i++) {
JSValue elem = JS_GetPropertyUint32(ctx, val, i);
res.push_back(JSValue_to_Cpp<value_type_t<T>>(ctx, elem));
JS_FreeValue(ctx, elem);
}
return res;
}

} // namespace quickjsr

#endif
30 changes: 25 additions & 5 deletions inst/include/quickjsr/JSValue_to_SEXP.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,45 @@

#include <cpp11.hpp>
#include <quickjs-libc.h>
#include <quickjsr/JSValue_to_Cpp.hpp>

namespace quickjsr {
// Forward declaration to allow for recursive calls
SEXP JSValue_to_SEXP(JSContext* ctx, JSValue val);

SEXP JSValue_to_SEXP_scalar(JSContext* ctx, JSValue val) {
if (JS_IsBool(val)) {
return cpp11::as_sexp(static_cast<bool>(JS_ToBool(ctx, val)));
return cpp11::as_sexp(JSValue_to_Cpp<bool>(ctx, val));
}
if (JS_IsNumber(val)) {
double res;
JS_ToFloat64(ctx, &res, val);
return cpp11::as_sexp(res);
return cpp11::as_sexp(JSValue_to_Cpp<double>(ctx, val));
}
if (JS_IsString(val)) {
return cpp11::as_sexp(JS_ToCString(ctx, val));
return cpp11::as_sexp(JSValue_to_Cpp<std::string>(ctx, val));
}
return cpp11::as_sexp("Unsupported type");
}

SEXP JSValue_to_SEXP_vector(JSContext* ctx, JSValue val) {
JSValue elem = JS_GetPropertyUint32(ctx, val, 0);
SEXP rtn;
if (JS_IsBool(elem)) {
rtn = cpp11::as_sexp(JSValue_to_Cpp<std::vector<bool>>(ctx, val));
} else if (JS_IsNumber(elem)) {
rtn = cpp11::as_sexp(JSValue_to_Cpp<std::vector<double>>(ctx, val));
} else if (JS_IsString(elem)) {
rtn = cpp11::as_sexp(JSValue_to_Cpp<std::vector<std::string>>(ctx, val));
} else {
rtn = cpp11::as_sexp("Unsupported type");
}
JS_FreeValue(ctx, elem);
return rtn;
}

SEXP JSValue_to_SEXP(JSContext* ctx, JSValue val) {
if (JS_IsArray(ctx, val)) {
return JSValue_to_SEXP_vector(ctx, val);
}
// TODO: Implement array and object conversion
return JSValue_to_SEXP_scalar(ctx, val);
}
Expand Down
26 changes: 26 additions & 0 deletions inst/include/quickjsr/type_traits.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef QUICKJSR_TYPE_TRAITS_HPP
#define QUICKJSR_TYPE_TRAITS_HPP

#include <type_traits>
#include <vector>

namespace quickjsr {
template <typename T>
struct is_std_vector : std::false_type {};
template <typename T>
struct is_std_vector<std::vector<T>> : std::true_type {};

template <typename T, typename = void>
struct value_type {
using type = typename std::decay_t<T>;
};

template <typename T>
struct value_type<T, std::enable_if_t<is_std_vector<T>::value>> {
using type = typename std::decay_t<T>::value_type;
};

template <typename T>
using value_type_t = typename value_type<T>::type;
} // namespace quickjsr
#endif
16 changes: 16 additions & 0 deletions inst/tinytest/test_data_conversion.R
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,19 @@ expect_eq_jsonlite(list(list(a = 1, b = 2), list(c = 3, d = 4)))
expect_eq_jsonlite(list(c("e", "f"), c("g", "h")))
expect_eq_jsonlite(list(list("e", "f"), list("g", "h")))
expect_eq_jsonlite(list(list(a = "e", b = "f"), list(c = "g", d = "h")))


# Test that the full round-trip conversion is consistent.
expect_eq_jsonlite_full <- function(x) {
expect_equal(qjs_passthrough(x, FALSE), jsonlite::fromJSON(jsonlite::toJSON(x)))
}
expect_eq_jsonlite_full(1)
expect_eq_jsonlite_full(1:3)
expect_eq_jsonlite_full(c(1.5, 2.5))

expect_eq_jsonlite_full("a")
expect_eq_jsonlite_full(c("a", "b", "c"))

expect_eq_jsonlite_full(TRUE)
expect_eq_jsonlite_full(FALSE)
expect_eq_jsonlite_full(c(TRUE, FALSE))

0 comments on commit f47bfaa

Please sign in to comment.