diff --git a/compiler/commands.nim b/compiler/commands.nim index ca6ef5d94588..d367823414c6 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -309,7 +309,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool proc processPath(conf: ConfigRef; path: string, info: TLineInfo, notRelativeToProj = false): AbsoluteDir = - let p = if os.isAbsolute(path) or '$' in path: + let p = if os.isAbsolute(path) or path.shouldPathSubs: path elif notRelativeToProj: getCurrentDir() / path @@ -325,7 +325,7 @@ proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): AbsoluteDir let path = if path.len > 0 and path[0] == '"': strutils.unescape(path) else: path let basedir = toFullPath(conf, info).splitFile().dir - let p = if os.isAbsolute(path) or '$' in path: + let p = if os.isAbsolute(path) or path.shouldPathSubs: path else: basedir / path diff --git a/compiler/options.nim b/compiler/options.nim index c39ffa792b0f..781a4464e9b4 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -590,9 +590,15 @@ proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir = AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name & (if isDefined(conf, "release") or isDefined(conf, "danger"): "_r" else: "_d")) +const subs = {'$', '@'} + +proc shouldPathSubs*(input: string): bool = + input.contains(subs + {'~'}) + proc pathSubs*(conf: ConfigRef; p, config: string): string = let home = removeTrailingDirSep(os.getHomeDir()) - result = unixToNativePath(p % [ + result = "" + result.addfCustom(p, subs = subs, [ "nim", getPrefixDir(conf).string, "lib", conf.libpath.string, "home", home, @@ -600,14 +606,17 @@ proc pathSubs*(conf: ConfigRef; p, config: string): string = "projectname", conf.projectName, "projectpath", conf.projectPath.string, "projectdir", conf.projectPath.string, - "nimcache", getNimcacheDir(conf).string]).expandTilde + "nimcache", getNimcacheDir(conf).string]) + result = unixToNativePath(result).expandTilde iterator nimbleSubs*(conf: ConfigRef; p: string): string = let pl = p.toLowerAscii - if "$nimblepath" in pl or "$nimbledir" in pl: + if "$nimblepath" in pl or "$nimbledir" in pl or "@nimblepath" in pl or "@nimbledir" in pl: for i in countdown(conf.nimblePaths.len-1, 0): let nimblePath = removeTrailingDirSep(conf.nimblePaths[i].string) - yield p % ["nimblepath", nimblePath, "nimbledir", nimblePath] + var ret = "" + ret.addfCustom(p, subs = subs, ["nimblepath", nimblePath, "nimbledir", nimblePath]) + yield ret else: yield p diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index e9e720b1b71b..7171c65e3f39 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -175,7 +175,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; cbconf patchFile: let key = a.getString(0) & "_" & a.getString(1) var val = a.getString(2).addFileExt(NimExt) - if {'$', '~'} in val: + if val.shouldPathSubs: val = pathSubs(conf, val, vthisDir) elif not isAbsolute(val): val = vthisDir / val diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 535af2b318c6..4f4da14b1391 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -2623,69 +2623,80 @@ proc findNormalized(x: string, inArray: openArray[string]): int = proc invalidFormatString() {.noinline.} = raise newException(ValueError, "invalid format string") -proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {. - noSideEffect, rtl, extern: "nsuAddf".} = +proc addfCustom*(s: var string, formatstr: string, subs = {'$'}, a: varargs[string, `$`]) {. + noSideEffect, rtl, extern: "nsuAddf2".} = ## The same as ``add(s, formatstr % a)``, but more efficient. + runnableExamples: + var s = "start:" + s.addfCustom("$foo @bar $$ @@ $", subs = {'$', '@'}, ["foo", "FOO", "bar", "BAR"]) + doAssert s == "start:FOO BAR $ @ $" + const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'} var i = 0 var num = 0 while i < len(formatstr): - if formatstr[i] == '$' and i+1 < len(formatstr): - case formatstr[i+1] - of '#': - if num > a.high: invalidFormatString() - add s, a[num] - inc i, 2 - inc num - of '$': - add s, '$' + if formatstr[i] in subs and i+1 < len(formatstr): + if formatstr[i+1] == formatstr[i]: + add s, formatstr[i] inc(i, 2) - of '1'..'9', '-': - var j = 0 - inc(i) # skip $ - var negative = formatstr[i] == '-' - if negative: inc i - while i < formatstr.len and formatstr[i] in Digits: - j = j * 10 + ord(formatstr[i]) - ord('0') - inc(i) - let idx = if not negative: j-1 else: a.len-j - if idx < 0 or idx > a.high: invalidFormatString() - add s, a[idx] - of '{': - var j = i+2 - var k = 0 - var negative = formatstr[j] == '-' - if negative: inc j - var isNumber = 0 - while j < formatstr.len and formatstr[j] notin {'\0', '}'}: - if formatstr[j] in Digits: - k = k * 10 + ord(formatstr[j]) - ord('0') - if isNumber == 0: isNumber = 1 - else: - isNumber = -1 - inc(j) - if isNumber == 1: - let idx = if not negative: k-1 else: a.len-k + else: + case formatstr[i+1] + of '#': + if num > a.high: invalidFormatString() + add s, a[num] + inc i, 2 + inc num + of '1'..'9', '-': + var j = 0 + inc(i) # skip subs + var negative = formatstr[i] == '-' + if negative: inc i + while i < formatstr.len and formatstr[i] in Digits: + j = j * 10 + ord(formatstr[i]) - ord('0') + inc(i) + let idx = if not negative: j-1 else: a.len-j if idx < 0 or idx > a.high: invalidFormatString() add s, a[idx] - else: - var x = findNormalized(substr(formatstr, i+2, j-1), a) + of '{': + var j = i+2 + var k = 0 + var negative = formatstr[j] == '-' + if negative: inc j + var isNumber = 0 + while j < formatstr.len and formatstr[j] notin {'\0', '}'}: + if formatstr[j] in Digits: + k = k * 10 + ord(formatstr[j]) - ord('0') + if isNumber == 0: isNumber = 1 + else: + isNumber = -1 + inc(j) + if isNumber == 1: + let idx = if not negative: k-1 else: a.len-k + if idx < 0 or idx > a.high: invalidFormatString() + add s, a[idx] + else: + var x = findNormalized(substr(formatstr, i+2, j-1), a) + if x >= 0 and x < high(a): add s, a[x+1] + else: invalidFormatString() + i = j+1 + of 'a'..'z', 'A'..'Z', '\128'..'\255', '_': + var j = i+1 + while j < formatstr.len and formatstr[j] in PatternChars: inc(j) + var x = findNormalized(substr(formatstr, i+1, j-1), a) if x >= 0 and x < high(a): add s, a[x+1] else: invalidFormatString() - i = j+1 - of 'a'..'z', 'A'..'Z', '\128'..'\255', '_': - var j = i+1 - while j < formatstr.len and formatstr[j] in PatternChars: inc(j) - var x = findNormalized(substr(formatstr, i+1, j-1), a) - if x >= 0 and x < high(a): add s, a[x+1] - else: invalidFormatString() - i = j - else: - invalidFormatString() + i = j + else: + invalidFormatString() else: add s, formatstr[i] inc(i) +proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {. + noSideEffect, rtl, extern: "nsuAddf".} = + ## Wrapper around `addfCustom` with subs = {'$'} + addfCustom(s, formatstr, subs = {'$'}, a) + proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect, rtl, extern: "nsuFormatOpenArray".} = ## Interpolates a format string with the values from `a`.