Code to convert base**exp to pow(base,exp) - metrumresearchgroup/mrgsolve GitHub Wiki
convert_pow <- function(code) {
# Process line by line
lines <- strsplit(code, "\n")[1](/metrumresearchgroup/mrgsolve/wiki/1)
converted <- sapply(lines, convert_line)
paste(converted, collapse = "\n")
}
convert_line <- function(line) {
# Repeatedly apply conversion until no more ** remain
while (grepl("\\*\\*", line)) {
line <- convert_one_pow(line)
}
line
}
convert_one_pow <- function(line) {
# Find the position of the FIRST ** operator
pos <- regexpr("\\*\\*", line)
if (pos == -1) return(line)
op_start <- as.integer(pos) # position of first *
op_end <- op_start + 1L # position of second *
# --- Extract BASE (scan LEFT from op_start - 1) ---
base_end <- op_start - 1L
base_start <- scan_left(line, base_end)
# --- Extract EXPONENT (scan RIGHT from op_end + 1) ---
exp_start <- op_end + 1L
exp_end <- scan_right(line, exp_start)
base <- substr(line, base_start, base_end)
exp <- substr(line, exp_start, exp_end)
# Rebuild the line
before <- substr(line, 1L, base_start - 1L)
after <- substr(line, exp_end + 1L, nchar(line))
paste0(before, "pow(", trimws(base), ", ", trimws(exp), ")", after)
}
# Scan leftward from `pos` to find the start of an expression token.
# Handles: parenthesised groups, identifiers, numbers (with dots/signs).
scan_left <- function(line, pos) {
chars <- strsplit(line, "")[1](/metrumresearchgroup/mrgsolve/wiki/1)
i <- pos
# Skip leading whitespace
while (i >= 1 && chars[i] == " ") i <- i - 1L
if (i < 1) return(1L)
# If we end on ')' scan back to matching '('
if (chars[i] == ")") {
depth <- 0L
while (i >= 1) {
if (chars[i] == ")") depth <- depth + 1L
if (chars[i] == "(") depth <- depth - 1L
if (depth == 0L) break
i <- i - 1L
}
# Now include the function name before '(' if present
i <- i - 1L
while (i >= 1 && (grepl("[A-Za-z0-9_]", chars[i]))) i <- i - 1L
return(i + 1L)
}
# Otherwise scan back over identifier/number characters
# Allow unary minus/plus as part of a standalone numeric literal only
# when preceded by nothing or an operator
while (i >= 1 && grepl("[A-Za-z0-9_.]", chars[i])) i <- i - 1L
# Absorb a leading unary sign if what's before it is an operator / start
if (i >= 1 && chars[i] %in% c("+", "-")) {
prev <- i - 1L
while (prev >= 1 && chars[prev] == " ") prev <- prev - 1L
if (prev < 1 || chars[prev] %in% c("(", ",", "=", "+", "-", "*", "/")) {
i <- i - 1L
}
}
i + 1L
}
# Scan rightward from `pos` to find the end of an expression token.
scan_right <- function(line, pos) {
chars <- strsplit(line, "")[1](/metrumresearchgroup/mrgsolve/wiki/1)
n <- length(chars)
i <- pos
# Skip leading whitespace
while (i <= n && chars[i] == " ") i <- i + 1L
if (i > n) return(n)
# Absorb optional unary sign
if (chars[i] %in% c("+", "-")) i <- i + 1L
# Skip whitespace after sign
while (i <= n && chars[i] == " ") i <- i + 1L
# If we start on '(' scan forward to matching ')'
if (i <= n && chars[i] == "(") {
depth <- 0L
while (i <= n) {
if (chars[i] == "(") depth <- depth + 1L
if (chars[i] == ")") depth <- depth - 1L
if (depth == 0L) break
i <- i + 1L
}
return(i)
}
# Otherwise scan forward over identifier/number characters (incl. function calls)
while (i <= n && grepl("[A-Za-z0-9_.]", chars[i])) i <- i + 1L
# If followed by '(' it's a function call — consume through matching ')'
if (i <= n && chars[i] == "(") {
depth <- 0L
while (i <= n) {
if (chars[i] == "(") depth <- depth + 1L
if (chars[i] == ")") depth <- depth - 1L
if (depth == 0L) break
i <- i + 1L
}
}
i - 1L
}