Skip to content

Commit

Permalink
new: nim -e:cmd to run a command directly; also fixes #15731 (#15687)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
timotheecour and Araq authored Nov 9, 2020
1 parent 53eca45 commit c1664f9
Show file tree
Hide file tree
Showing 13 changed files with 75 additions and 26 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

- 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.
- Added `nim --eval:cmd` to evaluate a command directly, see `nim --help`


## Tool changes
6 changes: 5 additions & 1 deletion compiler/cmdlinehelper.nim
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ proc initDefinesProg*(self: NimProg, conf: ConfigRef, name: string) =

proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) =
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)
elif conf.projectName != "":
try:
Expand All @@ -59,6 +61,8 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
graph: ModuleGraph): bool =
if self.suggestMode:
conf.command = "nimsuggest"
if conf.command == "e":
incl(conf.globalOptions, optWasNimscript)
loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files

if not self.suggestMode:
Expand Down
25 changes: 19 additions & 6 deletions compiler/commands.nim
Original file line number Diff line number Diff line change
Expand Up @@ -383,19 +383,33 @@ proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, in
expectArg(conf, switch, arg, pass, info)
options.inclDynlibOverride(conf, arg)

proc handleStdinInput*(conf: ConfigRef) =
conf.projectName = "stdinfile"
template handleStdinOrCmdInput =
conf.projectFull = conf.projectName.AbsoluteFile
conf.projectPath = AbsoluteDir getCurrentDir()
conf.projectIsStdin = true
if conf.outDir.isEmpty:
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;
conf: ConfigRef) =
var
key, val: string
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":
expectArg(conf, switch, arg, pass, info)
for path in nimbleSubs(conf, arg):
Expand Down Expand Up @@ -781,9 +795,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "def":
expectNoArg(conf, switch, arg, pass, info)
conf.ideCmd = ideDef
of "eval":
expectArg(conf, switch, arg, pass, info)
conf.evalExpr = arg
of "context":
expectNoArg(conf, switch, arg, pass, info)
conf.ideCmd = ideCon
Expand Down Expand Up @@ -936,6 +947,8 @@ proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) =

proc processArgument*(pass: TCmdLinePass; p: OptParser;
argsCount: var int; config: ConfigRef): bool =
if argsCount == 0 and config.implicitCmd:
argsCount.inc
if argsCount == 0:
# nim filename.nims is the same as "nim e filename.nims":
if p.key.endsWith(".nims"):
Expand Down
11 changes: 11 additions & 0 deletions compiler/extccomp.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,10 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) =

lit ",\L\"stdinInput\": "
lit $(%* conf.projectIsStdin)
lit ",\L\"projectIsCmd\": "
lit $(%* conf.projectIsCmd)
lit ",\L\"cmdInput\": "
lit $(%* conf.cmdInput)

if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"):
lit ",\L\"cmdline\": "
Expand Down Expand Up @@ -1051,11 +1055,18 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; projectfile: Absol
return true
if not data.hasKey("stdinInput"): return true
let stdinInput = data["stdinInput"].getBool
let projectIsCmd = data["projectIsCmd"].getBool
if conf.projectIsStdin or stdinInput:
# could optimize by returning false if stdin input was the same,
# but I'm not sure how to get full stding input
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"]
doAssert depfilesPairs.kind == JArray
for p in depfilesPairs:
Expand Down
5 changes: 3 additions & 2 deletions compiler/main.nim
Original file line number Diff line number Diff line change
Expand Up @@ -348,11 +348,12 @@ proc mainCommand*(graph: ModuleGraph) =
conf.cmd = cmdInteractive
commandInteractive(graph)
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)
elif not conf.projectFull.string.endsWith(".nims"):
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":
# prevent the "success" message:
conf.cmd = cmdDump
Expand Down
13 changes: 9 additions & 4 deletions compiler/modules.nim
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P
var flags = flags
if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
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:
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
result = loadModuleSym(graph, fileIdx, filename)
Expand All @@ -91,15 +98,13 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P
registerModule(graph, result)
else:
partialInitModule(result, graph, fileIdx, filename)
discard processModule(graph, result, idGeneratorFromModule(result),
if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil)
processModuleAux()
elif graph.isDirty(result):
result.flags.excl sfDirty
# reset module fields:
initStrTable(result.tab)
result.ast = nil
discard processModule(graph, result, idGeneratorFromModule(result),
if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil)
processModuleAux()
graph.markClientsDirty(fileIdx)

proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
Expand Down
1 change: 1 addition & 0 deletions compiler/msgs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
let s = if isRaw: arg else: getMessageStr(msg, arg)
if not ignoreMsg:
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: ""
if conf.structuredErrorHook != nil:
conf.structuredErrorHook(conf, info, s & kindmsg, sev)
Expand Down
3 changes: 2 additions & 1 deletion compiler/nim.nim
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =

self.processCmdLineAndProjectPath(conf)
var graph = newModuleGraph(cache, conf)
if not self.loadConfigsAndRunMainCommand(cache, conf, graph): return
if not self.loadConfigsAndRunMainCommand(cache, conf, graph):
return
mainCommand(graph)
if conf.hasHint(hintGCStats): echo(GC_getStatistics())
#echo(GC_getStatistics())
Expand Down
21 changes: 13 additions & 8 deletions compiler/nimconf.nim
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,16 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen:
if readConfigFile(configPath, cache, conf):
configFiles.add(configPath)

template runNimScriptIfExists(path: AbsoluteFile) =
template runNimScriptIfExists(path: AbsoluteFile, isMain = false) =
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)
runNimScript(cache, p, idgen, freshDefines = false, conf)
runNimScript(cache, p, idgen, freshDefines = false, conf, s)

if optSkipSystemConfigFile notin conf.globalOptions:
readConfigFile(getSystemConfigPath(conf, cfg))
Expand Down Expand Up @@ -288,19 +293,19 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen:
runNimScriptIfExists(pd / DefaultConfigNims)

let scriptFile = conf.projectFull.changeFileExt("nims")
let isMain = scriptFile == conf.projectFull
let scriptIsProj = scriptFile == conf.projectFull
template showHintConf =
for filename in configFiles:
# delayed to here so that `hintConf` is honored
rawMessage(conf, hintConf, filename.string)
if isMain:
if scriptIsProj:
showHintConf()
configFiles.setLen 0
if conf.command != "nimsuggest":
runNimScriptIfExists(scriptFile)
runNimScriptIfExists(scriptFile, isMain = true)
else:
if not isMain:
runNimScriptIfExists(scriptFile)
if not scriptIsProj:
runNimScriptIfExists(scriptFile, isMain = true)
else:
# 'nimsuggest foo.nims' means to just auto-complete the NimScript file
discard
Expand Down
5 changes: 3 additions & 2 deletions compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,13 @@ type
evalMacroCounter*: int
exitcode*: int8
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 (+)
exc*: ExceptionSystem
verbosity*: int # how verbose the compiler is
numberOfProcessors*: int # number of processors
evalExpr*: string # expression for idetools --eval
lastCmdTime*: float # when caas is enabled, we measure each command
symbolFiles*: SymbolFilesOption

Expand Down Expand Up @@ -418,7 +420,6 @@ proc newConfigRef*(): ConfigRef =
macrosToExpand: newStringTable(modeStyleInsensitive),
arcToExpand: newStringTable(modeStyleInsensitive),
m: initMsgConfig(),
evalExpr: "",
cppDefines: initHashSet[string](),
headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
hintQuitCalled, hintExecuting},
Expand Down
4 changes: 2 additions & 2 deletions compiler/scriptconfig.nim
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;

proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
idgen: IdGenerator;
freshDefines=true; conf: ConfigRef) =
freshDefines=true; conf: ConfigRef, stream: PLLStream) =
let oldSymbolFiles = conf.symbolFiles
conf.symbolFiles = disabledSf

Expand All @@ -226,7 +226,7 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
graph.vm = vm

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
# to remember this:
Expand Down
3 changes: 3 additions & 0 deletions compiler/vm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2114,6 +2114,9 @@ proc evalStmt*(c: PCtx, n: PNode) =
discard execute(c, start)

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 start = genExpr(c, n)
assert c.code[start].opcode != opcEof
Expand Down
3 changes: 3 additions & 0 deletions doc/basicopt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ Options:
--app:console|gui|lib|staticlib
generate a console app|GUI app|DLL|static library
-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
-h, --help show this help
-v, --version show detailed version information
Expand Down

0 comments on commit c1664f9

Please sign in to comment.