new: nim -e:cmd to run a command directly; also fixes #15731 (#15687)

* new: `nim -i cmd`
* rename -i to -e (for eval); consistent with majority of other programing languages
* `nim e -e:cmd` now works; bugfix: `echo cmd | nim e -` now works
* honor --betterRun
* address comments
* --eval alias for -e (replaces undocumented --eval which was a noop)
* --eval now defaults to e (nimscript) instead of r
* address comment: remove -e, only keep --eval
* address comment
* fixup
* Update compiler/nimconf.nim

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
This commit is contained in:
Timothee Cour 2020-11-09 09:07:22 -06:00 • committed by GitHub
commit c1664f93b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 75 additions and 26 deletions

View file

@ -33,6 +33,7 @@
- Added `--declaredlocs` to show symbol declaration location in messages. - Added `--declaredlocs` to show symbol declaration location in messages.
- Source+Edit links now appear on top of every docgen'd page when `nim doc --git.url:url ...` is given. - Source+Edit links now appear on top of every docgen'd page when `nim doc --git.url:url ...` is given.
- Added `nim --eval:cmd` to evaluate a command directly, see `nim --help`
## Tool changes ## Tool changes

View file

@ -41,7 +41,9 @@ proc initDefinesProg*(self: NimProg, conf: ConfigRef, name: string) =
proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) = proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) =
self.processCmdLine(passCmd1, "", conf) self.processCmdLine(passCmd1, "", conf)
if self.supportsStdinFile and conf.projectName == "-": if conf.projectIsCmd and conf.projectName in ["-", ""]:
handleCmdInput(conf)
elif self.supportsStdinFile and conf.projectName == "-":
handleStdinInput(conf) handleStdinInput(conf)
elif conf.projectName != "": elif conf.projectName != "":
try: try:
@ -59,6 +61,8 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
graph: ModuleGraph): bool = graph: ModuleGraph): bool =
if self.suggestMode: if self.suggestMode:
conf.command = "nimsuggest" conf.command = "nimsuggest"
if conf.command == "e":
incl(conf.globalOptions, optWasNimscript)
loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files
if not self.suggestMode: if not self.suggestMode:

View file

@ -383,19 +383,33 @@ proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, in
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
options.inclDynlibOverride(conf, arg) options.inclDynlibOverride(conf, arg)
proc handleStdinInput*(conf: ConfigRef) = template handleStdinOrCmdInput =
conf.projectName = "stdinfile"
conf.projectFull = conf.projectName.AbsoluteFile conf.projectFull = conf.projectName.AbsoluteFile
conf.projectPath = AbsoluteDir getCurrentDir() conf.projectPath = AbsoluteDir getCurrentDir()
conf.projectIsStdin = true
if conf.outDir.isEmpty: if conf.outDir.isEmpty:
conf.outDir = getNimcacheDir(conf) conf.outDir = getNimcacheDir(conf)
proc handleStdinInput*(conf: ConfigRef) =
conf.projectName = "stdinfile"
conf.projectIsStdin = true
handleStdinOrCmdInput()
proc handleCmdInput*(conf: ConfigRef) =
conf.projectName = "cmdfile"
handleStdinOrCmdInput()
proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
conf: ConfigRef) = conf: ConfigRef) =
var var
key, val: string key, val: string
case switch.normalize case switch.normalize
of "eval":
expectArg(conf, switch, arg, pass, info)
conf.projectIsCmd = true
conf.cmdInput = arg # can be empty (a nim file with empty content is valid too)
if conf.command == "":
conf.command = "e" # better than "r" as a default
conf.implicitCmd = true
of "path", "p": of "path", "p":
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
for path in nimbleSubs(conf, arg): for path in nimbleSubs(conf, arg):
@ -781,9 +795,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "def": of "def":
expectNoArg(conf, switch, arg, pass, info) expectNoArg(conf, switch, arg, pass, info)
conf.ideCmd = ideDef conf.ideCmd = ideDef
of "eval":
expectArg(conf, switch, arg, pass, info)
conf.evalExpr = arg
of "context": of "context":
expectNoArg(conf, switch, arg, pass, info) expectNoArg(conf, switch, arg, pass, info)
conf.ideCmd = ideCon conf.ideCmd = ideCon
@ -936,6 +947,8 @@ proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) =
proc processArgument*(pass: TCmdLinePass; p: OptParser; proc processArgument*(pass: TCmdLinePass; p: OptParser;
argsCount: var int; config: ConfigRef): bool = argsCount: var int; config: ConfigRef): bool =
if argsCount == 0 and config.implicitCmd:
argsCount.inc
if argsCount == 0: if argsCount == 0:
# nim filename.nims is the same as "nim e filename.nims": # nim filename.nims is the same as "nim e filename.nims":
if p.key.endsWith(".nims"): if p.key.endsWith(".nims"):

View file

@ -1022,6 +1022,10 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) =
lit ",\L\"stdinInput\": " lit ",\L\"stdinInput\": "
lit $(%* conf.projectIsStdin) lit $(%* conf.projectIsStdin)
lit ",\L\"projectIsCmd\": "
lit $(%* conf.projectIsCmd)
lit ",\L\"cmdInput\": "
lit $(%* conf.cmdInput)
if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"): if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"):
lit ",\L\"cmdline\": " lit ",\L\"cmdline\": "
@ -1051,11 +1055,18 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; projectfile: Absol
return true return true
if not data.hasKey("stdinInput"): return true if not data.hasKey("stdinInput"): return true
let stdinInput = data["stdinInput"].getBool let stdinInput = data["stdinInput"].getBool
let projectIsCmd = data["projectIsCmd"].getBool
if conf.projectIsStdin or stdinInput: if conf.projectIsStdin or stdinInput:
# could optimize by returning false if stdin input was the same, # could optimize by returning false if stdin input was the same,
# but I'm not sure how to get full stding input # but I'm not sure how to get full stding input
return true return true
if conf.projectIsCmd or projectIsCmd:
if not (conf.projectIsCmd and projectIsCmd): return true
if not data.hasKey("cmdInput"): return true
let cmdInput = data["cmdInput"].getStr
if cmdInput != conf.cmdInput: return true
let depfilesPairs = data["depfiles"] let depfilesPairs = data["depfiles"]
doAssert depfilesPairs.kind == JArray doAssert depfilesPairs.kind == JArray
for p in depfilesPairs: for p in depfilesPairs:

View file

@ -348,11 +348,12 @@ proc mainCommand*(graph: ModuleGraph) =
conf.cmd = cmdInteractive conf.cmd = cmdInteractive
commandInteractive(graph) commandInteractive(graph)
of "e": of "e":
if not fileExists(conf.projectFull): if conf.projectIsCmd or conf.projectIsStdin: discard
elif not fileExists(conf.projectFull):
rawMessage(conf, errGenerated, "NimScript file does not exist: " & conf.projectFull.string) rawMessage(conf, errGenerated, "NimScript file does not exist: " & conf.projectFull.string)
elif not conf.projectFull.string.endsWith(".nims"): elif not conf.projectFull.string.endsWith(".nims"):
rawMessage(conf, errGenerated, "not a NimScript file: " & conf.projectFull.string) rawMessage(conf, errGenerated, "not a NimScript file: " & conf.projectFull.string)
# main NimScript logic handled in cmdlinehelper.nim. # main NimScript logic handled in `loadConfigs`.
of "nop", "help": of "nop", "help":
# prevent the "success" message: # prevent the "success" message:
conf.cmd = cmdDump conf.cmd = cmdDump

View file

@ -82,6 +82,13 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P
var flags = flags var flags = flags
if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
result = graph.getModule(fileIdx) result = graph.getModule(fileIdx)
template processModuleAux =
var s: PLLStream
if sfMainModule in flags:
if graph.config.projectIsStdin: s = stdin.llStreamOpen
elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
discard processModule(graph, result, idGeneratorFromModule(result), s)
if result == nil: if result == nil:
let filename = AbsoluteFile toFullPath(graph.config, fileIdx) let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
result = loadModuleSym(graph, fileIdx, filename) result = loadModuleSym(graph, fileIdx, filename)
@ -91,15 +98,13 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P
registerModule(graph, result) registerModule(graph, result)
else: else:
partialInitModule(result, graph, fileIdx, filename) partialInitModule(result, graph, fileIdx, filename)
discard processModule(graph, result, idGeneratorFromModule(result), processModuleAux()
if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil)
elif graph.isDirty(result): elif graph.isDirty(result):
result.flags.excl sfDirty result.flags.excl sfDirty
# reset module fields: # reset module fields:
initStrTable(result.tab) initStrTable(result.tab)
result.ast = nil result.ast = nil
discard processModule(graph, result, idGeneratorFromModule(result), processModuleAux()
if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil)
graph.markClientsDirty(fileIdx) graph.markClientsDirty(fileIdx)
proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym = proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =

View file

@ -523,6 +523,7 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
let s = if isRaw: arg else: getMessageStr(msg, arg) let s = if isRaw: arg else: getMessageStr(msg, arg)
if not ignoreMsg: if not ignoreMsg:
let loc = if info != unknownLineInfo: conf.toFileLineCol(info) & " " else: "" let loc = if info != unknownLineInfo: conf.toFileLineCol(info) & " " else: ""
# we could also show `conf.cmdInput` here for `projectIsCmd`
var kindmsg = if kind.len > 0: KindFormat % kind else: "" var kindmsg = if kind.len > 0: KindFormat % kind else: ""
if conf.structuredErrorHook != nil: if conf.structuredErrorHook != nil:
conf.structuredErrorHook(conf, info, s & kindmsg, sev) conf.structuredErrorHook(conf, info, s & kindmsg, sev)

View file

@ -79,7 +79,8 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
self.processCmdLineAndProjectPath(conf) self.processCmdLineAndProjectPath(conf)
var graph = newModuleGraph(cache, conf) var graph = newModuleGraph(cache, conf)
if not self.loadConfigsAndRunMainCommand(cache, conf, graph): return if not self.loadConfigsAndRunMainCommand(cache, conf, graph):
return
mainCommand(graph) mainCommand(graph)
if conf.hasHint(hintGCStats): echo(GC_getStatistics()) if conf.hasHint(hintGCStats): echo(GC_getStatistics())
#echo(GC_getStatistics()) #echo(GC_getStatistics())

View file

@ -248,11 +248,16 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen:
if readConfigFile(configPath, cache, conf): if readConfigFile(configPath, cache, conf):
configFiles.add(configPath) configFiles.add(configPath)
template runNimScriptIfExists(path: AbsoluteFile) = template runNimScriptIfExists(path: AbsoluteFile, isMain = false) =
let p = path # eval once let p = path # eval once
if fileExists(p): var s: PLLStream
if isMain and optWasNimscript in conf.globalOptions:
if conf.projectIsStdin: s = stdin.llStreamOpen
elif conf.projectIsCmd: s = llStreamOpen(conf.cmdInput)
if s == nil and fileExists(p): s = llStreamOpen(p, fmRead)
if s != nil:
configFiles.add(p) configFiles.add(p)
runNimScript(cache, p, idgen, freshDefines = false, conf) runNimScript(cache, p, idgen, freshDefines = false, conf, s)
if optSkipSystemConfigFile notin conf.globalOptions: if optSkipSystemConfigFile notin conf.globalOptions:
readConfigFile(getSystemConfigPath(conf, cfg)) readConfigFile(getSystemConfigPath(conf, cfg))
@ -288,19 +293,19 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen:
runNimScriptIfExists(pd / DefaultConfigNims) runNimScriptIfExists(pd / DefaultConfigNims)
let scriptFile = conf.projectFull.changeFileExt("nims") let scriptFile = conf.projectFull.changeFileExt("nims")
let isMain = scriptFile == conf.projectFull let scriptIsProj = scriptFile == conf.projectFull
template showHintConf = template showHintConf =
for filename in configFiles: for filename in configFiles:
# delayed to here so that `hintConf` is honored # delayed to here so that `hintConf` is honored
rawMessage(conf, hintConf, filename.string) rawMessage(conf, hintConf, filename.string)
if isMain: if scriptIsProj:
showHintConf() showHintConf()
configFiles.setLen 0 configFiles.setLen 0
if conf.command != "nimsuggest": if conf.command != "nimsuggest":
runNimScriptIfExists(scriptFile) runNimScriptIfExists(scriptFile, isMain = true)
else: else:
if not isMain: if not scriptIsProj:
runNimScriptIfExists(scriptFile) runNimScriptIfExists(scriptFile, isMain = true)
else: else:
# 'nimsuggest foo.nims' means to just auto-complete the NimScript file # 'nimsuggest foo.nims' means to just auto-complete the NimScript file
discard discard

View file

@ -246,11 +246,13 @@ type
evalMacroCounter*: int evalMacroCounter*: int
exitcode*: int8 exitcode*: int8
cmd*: TCommands # the command cmd*: TCommands # the command
cmdInput*: string # input command
projectIsCmd*: bool # whether we're compiling from a command input
implicitCmd*: bool # whether some flag triggered an implicit `command`
selectedGC*: TGCMode # the selected GC (+) selectedGC*: TGCMode # the selected GC (+)
exc*: ExceptionSystem exc*: ExceptionSystem
verbosity*: int # how verbose the compiler is verbosity*: int # how verbose the compiler is
numberOfProcessors*: int # number of processors numberOfProcessors*: int # number of processors
evalExpr*: string # expression for idetools --eval
lastCmdTime*: float # when caas is enabled, we measure each command lastCmdTime*: float # when caas is enabled, we measure each command
symbolFiles*: SymbolFilesOption symbolFiles*: SymbolFilesOption
@ -418,7 +420,6 @@ proc newConfigRef*(): ConfigRef =
macrosToExpand: newStringTable(modeStyleInsensitive), macrosToExpand: newStringTable(modeStyleInsensitive),
arcToExpand: newStringTable(modeStyleInsensitive), arcToExpand: newStringTable(modeStyleInsensitive),
m: initMsgConfig(), m: initMsgConfig(),
evalExpr: "",
cppDefines: initHashSet[string](), cppDefines: initHashSet[string](),
headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic, headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
hintQuitCalled, hintExecuting}, hintQuitCalled, hintExecuting},

View file

@ -199,7 +199,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
idgen: IdGenerator; idgen: IdGenerator;
freshDefines=true; conf: ConfigRef) = freshDefines=true; conf: ConfigRef, stream: PLLStream) =
let oldSymbolFiles = conf.symbolFiles let oldSymbolFiles = conf.symbolFiles
conf.symbolFiles = disabledSf conf.symbolFiles = disabledSf
@ -226,7 +226,7 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
graph.vm = vm graph.vm = vm
graph.compileSystemModule() graph.compileSystemModule()
discard graph.processModule(m, vm.idgen, llStreamOpen(scriptName, fmRead)) discard graph.processModule(m, vm.idgen, stream)
# watch out, "newruntime" can be set within NimScript itself and then we need # watch out, "newruntime" can be set within NimScript itself and then we need
# to remember this: # to remember this:

View file

@ -2114,6 +2114,9 @@ proc evalStmt*(c: PCtx, n: PNode) =
discard execute(c, start) discard execute(c, start)
proc evalExpr*(c: PCtx, n: PNode): PNode = proc evalExpr*(c: PCtx, n: PNode): PNode =
# deadcode
# `nim --eval:"expr"` might've used it at some point for idetools; could
# be revived for nimsuggest
let n = transformExpr(c.graph, c.idgen, c.module, n) let n = transformExpr(c.graph, c.idgen, c.module, n)
let start = genExpr(c, n) let start = genExpr(c, n)
assert c.code[start].opcode != opcEof assert c.code[start].opcode != opcEof

View file

@ -31,6 +31,9 @@ Options:
--app:console|gui|lib|staticlib --app:console|gui|lib|staticlib
generate a console app|GUI app|DLL|static library generate a console app|GUI app|DLL|static library
-r, --run run the compiled program with given arguments -r, --run run the compiled program with given arguments
--eval:cmd evaluates nim code directly; e.g.: `nim --eval:"echo 1"`
defaults to `e` (nimscript) but customizable:
`nim r --eval:'for a in stdin.lines: echo a'`
--fullhelp show all command line switches --fullhelp show all command line switches
-h, --help show this help -h, --help show this help
-v, --version show detailed version information -v, --version show detailed version information