Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Swift as custom engine in knitr and including all previous content

Backrgound

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)
})
```

Challenge

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.

Question

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 ?

Example (ideal)

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)
```
like image 480
Konrad Avatar asked Oct 17 '25 13:10

Konrad


1 Answers

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)
```

enter image description here

Reprex files hosted with on GitHub

like image 177
the-mad-statter Avatar answered Oct 19 '25 03:10

the-mad-statter



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!