I'm working on a R Mardkown document which I'm renderring using the knitr
package. The document uses Swift as a custom enginge. I'm defining the Swift engine as follows:
```{r, setup, eval=TRUE, include=FALSE}
knitr::knit_engines$set(swift = function(options) {
code <- paste(options$code, collapse = '\n')
out <- system2(
command = "swift",
args = "repl",
input = code,
stdout = TRUE,
stderr = TRUE
)
knitr::engine_output(options, code, out)
})
```
I would like for the chunks to evaluate and include the code from the past chunks when evaluating. For instance in a first chunk I would provide:
```{swift, hello, eval=TRUE, include=TRUE}
import Foundation
let helloText: String = "Hello from Swift REPL"
print(helloText)
```
This would evaluate correctly to:
## helloText: String = "Hello from Swift REPL"
## Hello from Swift REPL
In the second chunk I would provide:
```{swift, hello_two, eval=TRUE, include=TRUE, dependson=knitr::all_labels()}
let punctuationMark: String = "!"
let helloTwo:String = helloText + punctuationMark
print(helloTwo)
```
This would return error
## punctuationMark: String = "!"
## error: repl.swift:2:23: cannot find 'helloText' in scope
## let helloTwo:String = helloText + punctuationMark
## ^~~~~~~~~
##
##
## error: repl.swift:2:7: cannot find 'helloTwo' in scope
## print(helloTwo)
## ^~~~~~~~
Understandably the object helloText
defined in the first chunk is not visible when Swift REPL is called again via previously defined swift
function.
How can I redefined the knit_engines$set(swift = ...)
call so it would automatically include code from the previous chunks, or better, maintain the object space ?
In my second chunk where the code is defined as:
```{swift, hello_two, eval=TRUE, include=TRUE, dependson=knitr::all_labels()}
let punctuationMark: String = "!"
let helloTwo:String = helloText + punctuationMark
print(helloTwo)
```
The code that should be evaluated would be:
```{swift, hello_two, eval=TRUE, include=TRUE, dependson=knitr::all_labels()}
let punctuationMark: String = "!"
let helloTwo:String = helloText + punctuationMark
let punctuationMark: String = "!"
let helloTwo:String = helloText + punctuationMark
print(helloTwo)
```
Note: This is including only one print statement. I reckon this may difficult so I would also accept scenario where evaluated code in second chunk is:
```{swift, hello_two, eval=TRUE, include=TRUE, dependson=knitr::all_labels()}
let punctuationMark: String = "!"
let helloTwo:String = helloText + punctuationMark
print(helloText)
let punctuationMark: String = "!"
let helloTwo:String = helloText + punctuationMark
print(helloTwo)
```
This contains two print statements (code chunks are merged together). I want for this process to happen automatically, in terms of the textual content, the second chunk should only contain:
```{swift, hello_two, eval=TRUE, include=TRUE, dependson=knitr::all_labels()}
let punctuationMark: String = "!"
let helloTwo:String = helloText + punctuationMark
print(helloTwo)
```
Here is one approach near your ideal.
The central idea is that for each Swift chunk the engine will collate all previous Swift chunk code in addition to the current chunk code and then run the collated code through the Swift REPL.
This allows previously defined objects to be available in each new Swift REPL. The accumulated code can be modified such as stripping previous print statements, and options and current chunk code can pass through unmodified.
---
title: "Untitled"
output: html_document
date: "2025-03-04"
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
knitr::knit_engines$set(swift = function(options) {
# collapsed code from the chunk currently being processed
current_code <- paste(options$code, collapse = '\n')
# all chunk names with engine set to swift
swift_chunk_names <- knitr::all_labels(engine == "swift")
# code accumulation location
cummulative_code <- NULL
# for each swift chunk in order of appearance
for(swift_chunk_name in swift_chunk_names) {
# get a collapsed copy of the code
prior_code <- paste(knitr::knit_code$get(swift_chunk_name), collapse = '\n')
# if the prior code is identical to the current chunk's code
if(current_code == prior_code) {
# just append the code to our accumulator
cummulative_code <- c(cummulative_code, prior_code)
# and break out of the for loop
break
}
else {
# else strip print statements from the code before appending
cummulative_code <- c(cummulative_code, gsub("print\\(.+?\\)", "", prior_code))
}
}
# collapse the accumulated code
code <- paste(cummulative_code, collapse = '\n')
# run the code through swift
out <- system2(
command = "swift",
args = "repl",
input = code,
stdout = TRUE,
stderr = TRUE
)
# output, allowing options and original code to pass through
knitr::engine_output(options, options$code, out)
})
```
```{swift}
import Foundation
let helloText: String = "Hello from Swift REPL"
print(helloText)
```
```{swift}
let punctuationMark: String = "!"
let helloTwo:String = helloText + punctuationMark
print(helloTwo)
```
Reprex files hosted with on GitHub