# group source lines into complete expressions (using a brutal-force method)
group_src = function(code, context) {
if ((n <- length(code)) < 1) return(list(code))
i = i1 = i2 = 1
x = list()
while (i2 <= n) {
piece = code[i1:i2]
if (eval(parse(text=sprintf("%s$validate(piece)", context))) && eval(parse(text=sprintf('%s$eval(piece)!="undefined"', context)))) {
x[[i]] = piece; i = i + 1
i1 = i2 + 1 # start from the next line
}
i2 = i2 + 1
}
if (i1 <= n) parse(text = piece) # must be an error there
x
}
#
library(knitr)
knit_engines$set(V8 = function(options) {
require(V8)
context <- ifelse(!is.null(options$V8.context), options$V8.context, "ct")
if(!exists(context)){
assign(context, new_context(), envir = .GlobalEnv)
if(!is.null(options$V8.libraries)){
for(library in options$V8.libraries){
eval(parse(text=sprintf('%s$source("%s")', context, library)))
}
}
}
code <- as.character(c(options$code))
if(!eval(parse(text=sprintf("%s$validate(code)", context)))) stop("unvalid javascript code")
if(!is.element(options$results, c("hide", "last"))){
code <- group_src(code, context)
output <- sapply(code, function(code) eval(parse(text=sprintf("%s$eval(code)", context))) )
code <- sapply(code, function(code) knitr:::wrap.source(list(src= paste(code, collapse = "\n")), options))
}else{
if(options$results=="last") output <- eval(parse(text=sprintf("%s$eval(code)", context)))
code <- knitr:::wrap.source(list(src= paste(code, collapse = "\n")), options)
if(options$results=="hide") return(code)
return(c(code, knitr:::wrap.character(output, options)))
}
return(c(sapply(seq_along(code), function(i) c(code[i], knitr:::wrap.character(output[i], options)))))
})
Define x in Javascript context using the chunk option engine='V8':
var x=[]
for(i = 0; i<3; i++){
x[i]=i;
}
## 2
x.push([3,4])
## 4
JSON.stringify(x)
## [0,1,2,[3,4]]
In the previous chunk, the results option is set to its default value. You can use results='hide' to hide the output, or results='last' to show only the output generated by the last line:
var x=[]
x.push([1,2])
x.push([3,4])
JSON.stringify(x)
## [[1,2],[3,4]]
By default the Javascript context is a R variable named ct. You can set another name by doing opts_chunk$set(V8.context="mycontext"). Then using the context variable you can import a variable from the Javascript context to R or vice-versa (see ?V8::V8 for details) :
x in R :( x.in.R <- ct$get("x") )
## [,1] [,2]
## [1,] 1 2
## [2,] 3 4
x in R and assign in Javascript context :ct$assign("double_x", 2*x.in.R)
The Javascript JSON.stringify function is nice for displaying an object in a V8 chunk:
JSON.stringify(double_x);
## [[2,4],[6,8]]
You can load a Javascript library like this :
ct$source(system.file("js/underscore.js", package="V8"))
As you can see, it works:
JSON.stringify(_.flatten(x))
## [1,2,3,4]
Another way is to give the libraries as a character vector in the V8.libraries chunk option:
opts_chunk$set(V8.libraries=c(system.file("js/underscore.js", package="V8"), "http://coffeescript.org/extras/coffee-script.js"))
The libraries given by this way are loaded only once, at the moment the Javascript context is created.