Skip to content

Commit

Permalink
improve parsing of Description.qml
Browse files Browse the repository at this point in the history
  • Loading branch information
vandenman committed Oct 8, 2024
1 parent 58f14c9 commit b9680c6
Showing 1 changed file with 107 additions and 14 deletions.
121 changes: 107 additions & 14 deletions R/options.R
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ analysisOptionsFromJASPfile <- function(file) {
return(options)
}

parsePreloadDataFromDescriptionQml <- function(analysisName) {
parseDescriptionQmlFromAnalysisName <- function(analysisName) {

modulePath <- getModulePathFromRFunction(analysisName)
if (isBinaryPackage(modulePath)) {
Expand All @@ -188,23 +188,116 @@ parsePreloadDataFromDescriptionQml <- function(analysisName) {
return(TRUE)
}

# some more parsing of QML with regex, mostly to make Joris cry
raw <- trimws(readLines(pathToDescriptionQml))
idx <- grep(sprintf("\\s*func\\s*:\\s*\"%s\"", analysisName), raw)
return(parseDescriptionQmlFromPath(pathToDescriptionQml))
}

if (length(idx) == 0) {
warning("Analysis ", analysisName, " not found in Description.qml. Assuming the module preloads data.")
return(TRUE)
# some code to test the function below on all Description.qml files in jasp
# dirs <- list.dirs("~/github/jasp/jasp-desktop/Modules", recursive = FALSE)
# qmls <- file.path(dirs, "inst", "Description.qml")
# qmls <- Filter(file.exists, qmls)
# nms <- basename(dirname(dirname(qmls)))
# names(qmls) <- nms
# results <- vector("list", length(nms))
# names(results) <- nms
# for (nm in nms) {
# cat(nm, "\n")
# results[[nm]] <- jaspTools:::parseDescriptionQmlFromPath(qmls[[nm]])
# }
parseDescriptionQmlFromPath <- function(pathToDescriptionQml) {

raw <- trimws(readLines(pathToDescriptionQml))
raw <- raw[raw != ""]
# drop import statements
raw <- raw[!startsWith(raw, "import")]
# ensure that everything is on a newline
raw <- trimws(unlist(strsplit(raw, ";", fixed = TRUE), use.names = FALSE))
# remove any whitespace
raw <- gsub("\\s", "", raw)
# transform "Description{}" to c("Description", "{", "})
raw <- trimws(unlist(strsplit(raw, "(?=\\{)", perl = TRUE), use.names = FALSE))
raw <- trimws(unlist(strsplit(raw, "(?=\\})", perl = TRUE), use.names = FALSE))
# "qsTr(\"bla\")" -> "\"bla\""
raw <- gsub('qsTr\\("(.*)"\\)', "\\1", raw)

result <- list()
subResults <- NULL
subNames <- character(0L)
depth <- 0L
skipUntilClose <- FALSE
skipCount <- 0L

groupsToskip <- c("GroupTitle", "Separator", "Timer")
for (i in seq_along(raw)) {

if (!skipUntilClose) {

hasColon <- grepl(":", raw[i], fixed = TRUE)
if (!hasColon && grepl("{", raw[i + 1], fixed = TRUE)) {

if (any(vapply(groupsToskip, function(x) identical(raw[i], x), FUN.VALUE = logical(1L)))) {
skipUntilClose <- TRUE
next
}

depth <- depth + 1L
subNames[[depth]] <- raw[i]
subResult <- list()
subResults[[depth]] <- list()

} else if (hasColon) {

match <- regexec("([^:]+):(.*)", raw[i])
parts <- regmatches(raw[i], match)[[1]]
key <- parts[2]
value <- parts[3]
# remove quotes at the start and end of the string
value <- gsub("^[\"']|[\"']$", "", value)

subResults[[depth]][[key]] <- switch(
value,
"false" = FALSE,
"true" = TRUE,
value
)

} else if (grepl("}", raw[i], fixed = TRUE)) {

subName <- subNames[[depth]]
subResult <- subResults[[depth]]
if (!is.null(subResult) && length(subResult) > 0L) {

# rather than "Analysis", use the name of the R function
if (subName == "Analysis")
subName <- subResult[["func"]]

result[[subName]] <- subResult

}

result[[subName]] <- subResult
depth <- depth - 1L
}

} else if (grepl("{" , raw[i], fixed = TRUE)) {
skipCount <- skipCount + 1L
} else if (grepl("}", raw[i], fixed = TRUE)) {

skipCount <- skipCount - 1L
if (skipCount == 0)
skipUntilClose <- FALSE

}
}

previous_bracket <- grep("{", raw[1:idx], fixed = TRUE)
previous_bracket <- previous_bracket[length(previous_bracket)]
next_bracket <- grep("}", raw[idx:length(raw)], fixed = TRUE)
next_bracket <- next_bracket[1] + idx
idx_match <- grep("\\s*preloadData\\s*:\\s*(.*)[^;]{0,1}", raw[previous_bracket:next_bracket])
result <- gsub("\\s*preloadData\\s*:\\s*(.*)[^;]{0,1}", "\\1", raw[previous_bracket:next_bracket][idx_match])
preloadData <- identical(result, "true")
return(result)

}

parsePreloadDataFromDescriptionQml <- function(analysisName) {

description <- parseDescriptionQmlFromAnalysisName(analysisName)

preloadData <- isTRUE(description[["preloadData"]]) || isTRUE(description[[analysisName]][["preloadData"]])
if (!preloadData)
warning("Analysis ", analysisName, " does not preload data. Please update the code.")

Expand Down

0 comments on commit b9680c6

Please sign in to comment.