I've had enough of copy and pasting output from my R session into my email editor, blog, etc. I need something like Sweave for plain text files. In particular, I want the result of parsing
<<echo=TRUE>>= f <- function(x) { x + 1 } f(1) @
with Sweave, but without the latex markup. For example, the Sweave output of this code chunk looks like this
\begin{Schunk} \begin{Sinput} > f <- function(x) { + x + 1 + } > f(1) \end{Sinput} \begin{Soutput} [1] 2 \end{Soutput} \end{Schunk}
But I want something like
> f <- function(x) { + x + 1 + } > f(1) [1] 2
From my latest reading (today) of the Sweave manual, Sweave does not have a documented option for this. I decided not to explore the code base of Sweave just yet, due to its size (src/library/utils/R/Sweave.R is nearly 1000 lines, and half are related to the RweaveLatex driver). I think it's probably overkill to write an Sweave driver for my purpose.
The brew package, by Jeff Horner is a nice little package that does something similar to Sweave for text files. The source code for brew is still manageable in size, so I decided to start there. I had originally thought that the brew package incorporated the type of functionality I mentioned above, but it does not. However, brew allows the user to provide their own 'template' parsing function. Essentially, when brew encounters the delimiters '<%%' and '%%>' in a text file, the text within the delimiters is passed to the user-supplied template parsing function, which should return a character vector to be printed in place of the delimited text.
In order to duplicate a the desired portion of Sweave functionality in brew , I wrote the following template parsing function.
brew.weave <- function(code) { out <- '' exr <- try(parse(text=code),TRUE) if(inherits(exr, "try-error")) return(exr) env <- new.env(parent=sys.frame(sys.parent())) for(i in 1:length(exr)) { dep <- deparse(exr[[i]]) for(j in 1:length(dep)) { pmt <- ifelse(j > 1, getOption("continue"), getOption("prompt")) out <- paste(out, pmt, dep[j], '\n', sep='') } res <- try(capture.output(eval(exr[i], env)),TRUE) if(length(res)>0) { res <- paste(res, collapse='\n', sep='') if(!grepl("^.*\n$", res)) res <- paste(res, '\n') out <- paste(out, res, sep='') } } return(out) }
The first block below is a file (featurefull.brew) I modified from the brew source package that demonstrates all of the brew functionality, and the extended functionality provided by brew.weave. The second block shows the default output of brew for this file. The third block shows the output using the brew.weave template function.
-
You won't see this R output, but it will run. <% foo <- 'bar' %> Now foo is <%=foo%> and today is <%=format(Sys.time(),'%B %d, %Y')%>. <%# Comment -- ignored -- useful in testing. Also notice the dash-percent-gt. It chops off the trailing newline. You can add it to any percent-gt. -%> How about generating a template from a template? <%% foo <- 'fee fi fo fum' bar <- function(x) { x + 1 } bar(1) %%> foo is still <%=foo%>.
-
> brew::brew("featurefull.brew") You won't see this R output, but it will run. Now foo is bar and today is July 26, 2010. How about generating a template from a template? <% foo <- 'fee fi fo fum' bar <- function(x) { x + 1 } bar(1) %> foo is still bar.
-
> brew::brew("featurefull.brew", tplParser=brew.weave) You won't see this R output, but it will run. Now foo is bar and today is July 26, 2010. How about generating a template from a template? > foo <- "fee fi fo fum" > bar <- function(x) { + x + 1 + } > bar(1) [1] 2 foo is still bar.
There are (of course) some limitations. This extension applies only to output that can be captured with the capture.output function in the utils package. Also, brew does not provide a mechanism to propagate changes to the R environment made by template (<%% %%>) code. Also, I might like to simultaneously HTML escape the output for easy pasting into hypertext documents. This could be done with additional template parsers in brew. However, you can only use one template parser at a time.
Another interesting possibility would be to use the evaluate function and others in Hadley Wickham's new package evaluate, as the intended purpose of this package seems to be in support of brew and Sweave-like functionality. I have a potential extension/solution to the issues above (and potential package if there is interest) that I'll discuss in a later post.