-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18 from andrjohns/sexp-to-jsvalue
Tidy functionality and add tests for R to JS conversions
- Loading branch information
Showing
13 changed files
with
222 additions
and
65 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,4 @@ | |
^src/quickjs/\.github$ | ||
^src/quickjs/\.git$ | ||
semicolon_delimited_script | ||
Makefile |
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,2 @@ | ||
clean: | ||
$(MAKE) -C src -f Makevars clean |
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,8 @@ | ||
#ifndef QUICKJSR_HPP | ||
#define QUICKJSR_HPP | ||
|
||
#include <quickjsr/SEXP_to_JSValue.hpp> | ||
#include <quickjsr/JSValue_to_SEXP.hpp> | ||
#include <quickjsr/JSValue_to_JSON.hpp> | ||
|
||
#endif |
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,33 @@ | ||
#ifndef QUICKJSR_JSVALUE_TO_JSON_HPP | ||
#define QUICKJSR_JSVALUE_TO_JSON_HPP | ||
|
||
#include <cpp11.hpp> | ||
#include <quickjs-libc.h> | ||
|
||
namespace quickjsr { | ||
|
||
std::string JS_ValToJSON(JSContext* ctx, JSValue* val) { | ||
JSValue global = JS_GetGlobalObject(ctx); | ||
JSValue json = JS_GetPropertyStr(ctx, global, "JSON"); | ||
JSValue stringify = JS_GetPropertyStr(ctx, json, "stringify"); | ||
|
||
JSValue result_js = JS_Call(ctx, stringify, global, 1, val); | ||
std::string result; | ||
if (JS_IsException(result_js)) { | ||
js_std_dump_error(ctx); | ||
result = "Error!"; | ||
} else { | ||
result = JS_ToCString(ctx, result_js); | ||
} | ||
|
||
JS_FreeValue(ctx, result_js); | ||
JS_FreeValue(ctx, stringify); | ||
JS_FreeValue(ctx, json); | ||
JS_FreeValue(ctx, global); | ||
|
||
return result; | ||
} | ||
|
||
} // namespace quickjsr | ||
|
||
#endif |
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,31 @@ | ||
#ifndef QUICKJSR_JSVALUE_TO_SEXP_HPP | ||
#define QUICKJSR_JSVALUE_TO_SEXP_HPP | ||
|
||
#include <cpp11.hpp> | ||
#include <quickjs-libc.h> | ||
|
||
namespace quickjsr { | ||
|
||
SEXP JSValue_to_SEXP_scalar(JSContext* ctx, JSValue val) { | ||
if (JS_IsBool(val)) { | ||
return cpp11::as_sexp(static_cast<bool>(JS_ToBool(ctx, val))); | ||
} | ||
if (JS_IsNumber(val)) { | ||
double res; | ||
JS_ToFloat64(ctx, &res, val); | ||
return cpp11::as_sexp(res); | ||
} | ||
if (JS_IsString(val)) { | ||
return cpp11::as_sexp(JS_ToCString(ctx, val)); | ||
} | ||
return cpp11::as_sexp("Unsupported type"); | ||
} | ||
|
||
SEXP JSValue_to_SEXP(JSContext* ctx, JSValue val) { | ||
// TODO: Implement array and object conversion | ||
return JSValue_to_SEXP_scalar(ctx, val); | ||
} | ||
|
||
} // namespace quickjsr | ||
|
||
#endif |
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,66 @@ | ||
#ifndef QUICKJSR_SEXP_TO_JSVALUE_HPP | ||
#define QUICKJSR_SEXP_TO_JSVALUE_HPP | ||
|
||
#include <cpp11.hpp> | ||
#include <quickjs-libc.h> | ||
|
||
namespace quickjsr { | ||
// Forward declaration to allow for recursive calls | ||
JSValue SEXP_to_JSValue(JSContext* ctx, SEXP x, bool auto_unbox); | ||
|
||
JSValue SEXP_to_JSValue_elem(JSContext* ctx, SEXP x, int i, bool auto_unbox) { | ||
switch(TYPEOF(x)) { | ||
case REALSXP: | ||
return JS_NewFloat64(ctx, REAL(x)[i]); | ||
case INTSXP: | ||
return JS_NewInt32(ctx, INTEGER(x)[i]); | ||
case LGLSXP: | ||
return JS_NewBool(ctx, LOGICAL(x)[i]); | ||
case STRSXP: | ||
return JS_NewString(ctx, CHAR(STRING_ELT(x, i))); | ||
case VECSXP: | ||
return SEXP_to_JSValue(ctx, VECTOR_ELT(x, i), auto_unbox); | ||
default: | ||
return JS_UNDEFINED; | ||
} | ||
} | ||
|
||
JSValue SEXP_to_JSValue_array(JSContext* ctx, SEXP x, bool auto_unbox) { | ||
JSValue arr = JS_NewArray(ctx); | ||
for (int i = 0; i < Rf_length(x); i++) { | ||
JSValue val = SEXP_to_JSValue_elem(ctx, x, i, auto_unbox); | ||
JS_SetPropertyUint32(ctx, arr, i, val); | ||
} | ||
return arr; | ||
} | ||
|
||
JSValue SEXP_to_JSValue_object(JSContext* ctx, SEXP x, bool auto_unbox) { | ||
JSValue obj = JS_NewObject(ctx); | ||
for (int i = 0; i < Rf_length(x); i++) { | ||
SEXP name = STRING_ELT(Rf_getAttrib(x, R_NamesSymbol), i); | ||
JSValue val = SEXP_to_JSValue_elem(ctx, x, i, auto_unbox); | ||
JS_SetPropertyStr(ctx, obj, CHAR(name), val); | ||
} | ||
return obj; | ||
} | ||
|
||
JSValue SEXP_to_JSValue(JSContext* ctx, SEXP x, bool auto_unbox = false) { | ||
// Following jsonlite conventions: | ||
// - R list with names is an object, otherwise an array | ||
if (TYPEOF(x) == VECSXP) { | ||
if (Rf_getAttrib(x, R_NamesSymbol) != R_NilValue) { | ||
return SEXP_to_JSValue_object(ctx, x, auto_unbox); | ||
} else { | ||
return SEXP_to_JSValue_array(ctx, x, auto_unbox); | ||
} | ||
} | ||
if (Rf_length(x) == 1 && auto_unbox) { | ||
return SEXP_to_JSValue_elem(ctx, x, 0, true); | ||
} else { | ||
return SEXP_to_JSValue_array(ctx, x, true); | ||
} | ||
} | ||
|
||
} // namespace quickjsr | ||
|
||
#endif |
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,29 @@ | ||
# Check conversions from R types to JS types are consistent with jsonlite. | ||
# - The inputs are directly converted from R to JS types using the QuickJS API. | ||
# - The outputs are returned as JSON strings and parsed back to R using jsonlite. | ||
# - If the conversion is consistent, the output should be the same as the input. | ||
expect_eq_jsonlite <- function(x) { | ||
expect_equal(qjs_passthrough(x), jsonlite::fromJSON(jsonlite::toJSON(x))) | ||
} | ||
expect_eq_jsonlite(1) | ||
expect_eq_jsonlite(1:3) | ||
expect_eq_jsonlite(c(1.5, 2.5)) | ||
|
||
expect_eq_jsonlite("a") | ||
expect_eq_jsonlite(c("a", "b", "c")) | ||
|
||
expect_eq_jsonlite(TRUE) | ||
expect_eq_jsonlite(FALSE) | ||
expect_eq_jsonlite(c(TRUE, FALSE)) | ||
|
||
expect_eq_jsonlite(list(1, 2, 3)) | ||
expect_eq_jsonlite(list(a = 1, b = 2, c = 3)) | ||
expect_eq_jsonlite(list(a = "d", b = "e", c = "f")) | ||
|
||
expect_eq_jsonlite(list(c(1, 2), c(3, 4))) | ||
expect_eq_jsonlite(list(list(1, 2), list(3, 4))) | ||
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"))) |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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