Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ errors.

- Adds a new warning `--warning:ImplicitRangeConversion` that detects downsizing implicit conversions to range types (e.g., `int -> range[0..255]` or `range[1..256] -> range[0..255]`) that could cause runtime panics. Safe conversions like `range[0..255] -> range[0..65535]` and explicit casts do not trigger warnings. `int` to `Natural` and `Positive` conversions do not trigger warnings, which can be enabled with `--warning:systemRangeConversion`.


- `std/parseopt`: `initOptParser` and `getopt` no longer fall back to `commandLineParams()` when given empty input (`""` or `@[]`). An explicit empty string or sequence now produces an empty parser. Code that relied on `""` or `@[]` values to implicitly re-parse the OS command line must be updated to call `initOptParser()` with the `cmdline` argument omitted instead.
The `cmdline = ""` default was removed from the string overload of `initOptParser`. The no-argument form now unambiguously resolves to the `seq[string]` overload with `commandLineParams()` as its default.


- `std/cmdline.commandLineParams()` for NimScript now consistently returns only the script arguments (excluding the `nim` executable and its flags), fixing inconsistency between `std/cmdline` and `std/parseopt`.


## Standard library additions and changes

[//]: # "Additions:"
Expand Down
3 changes: 2 additions & 1 deletion compiler/nim.nim
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ proc nimbleLockExists(config: ConfigRef): bool =
return fileExists(pd.string / nimbleLock)

proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
var p = parseopt.initOptParser(cmd)
var p = if cmd.len == 0: parseopt.initOptParser()
else: parseopt.initOptParser(cmd)
var argsCount = 0

config.commandLine.setLen 0
Expand Down
3 changes: 2 additions & 1 deletion drnim/drnim.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1208,7 +1208,8 @@ proc mainCommand(graph: ModuleGraph) =
genSuccessX(graph.config)

proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
var p = parseopt.initOptParser(cmd)
var p = if cmd.len == 0: parseopt.initOptParser()
else: parseopt.initOptParser(cmd)
var argsCount = 1

config.commandLine.setLen 0
Expand Down
64 changes: 24 additions & 40 deletions lib/pure/parseopt.nim
Original file line number Diff line number Diff line change
Expand Up @@ -450,39 +450,18 @@ proc initOptParser(cmdline: openArray[string];
)
if prSepAllowEq in rules: result.separators.incl('=')
if prSepAllowColon in rules: result.separators.incl(':')
if cmdline.len == 0:
when declared(paramCount):
when defined(nimscript):
var ctr = 0
var firstNimsFound = false
for i in countup(0, paramCount()):
if firstNimsFound:
result.cmds[ctr] = paramStr(i)
inc ctr, 1
if paramStr(i).toLowerAscii().endsWith(".nims") and not firstNimsFound:
firstNimsFound = true
result.cmds = newSeq[string](paramCount()-i)
else:
result.cmds = newSeq[string](paramCount())
for i in countup(1, paramCount()):
result.cmds[i-1] = paramStr(i)
else:
# we cannot provide this for NimRtl creation on Posix, because we can't
# access the command line arguments then!
raiseAssert "empty command line given but" &
" real command line is not accessible"

proc initOptParser*(cmdline: seq[string];
proc initOptParser*(cmdline: seq[string] = commandLineParams();
shortNoVal: set[char] = {};
longNoVal: seq[string] = @[];
mode: CliMode = NimMode): OptParser =
## Initializes the command line parser.
## Initializes the command line parser from a sequence of arguments.
##
## **Parameters:**
##
## - `cmdline`: Sequence of command line arguments to parse. If empty, the
## real command line as provided by the `os` module is retrieved instead.
## If the command line is not available, an assertion will be raised.
## - `cmdline`: Sequence of arguments to parse. Defaults to
## `os.commandLineParams()`, which retrieves the actual command line arguments
## at the call site. Empty value means no arguments and has no side-effects.
## - `shortNoVal`: Set of short option characters that do not accept values.
## See `shortNoVal and longNoVal<#nimshortnoval-and-nimlongnoval>`_ for details.
## - `longNoVal`: Sequence of long option names that do not accept values.
Expand All @@ -499,8 +478,8 @@ proc initOptParser*(cmdline: seq[string];
shortNoVal = {'l'}, longNoVal = @["left"])
initOptParser(cmdline, shortNoVal, longNoVal, toRules(mode))

proc initOptParser*(cmdline: seq[string],
shortNoVal: set[char] = {},
proc initOptParser*(cmdline: seq[string] = commandLineParams();
shortNoVal: set[char] = {};
longNoVal: seq[string] = @[];
allowWhitespaceAfterColon: bool): OptParser {.deprecated:
"`allowWhitespaceAfterColon` is deprecated, use parser modes instead".} =
Expand All @@ -517,19 +496,22 @@ proc initOptParser*(cmdline: seq[string],
if allowWhitespaceAfterColon == false: nimrules.excl prSepAllowDelimAfter
initOptParser(cmdline, shortNoVal, longNoVal, nimrules)

proc initOptParser*(cmdline = "";
proc initOptParser*(cmdline: string;
shortNoVal: set[char] = {};
longNoVal: seq[string] = @[];
mode: CliMode = NimMode): OptParser =
## Initializes the command line parser from a command line string.
##
## The `cmdline` string is parsed into tokens using shell-like quoting rules.
## The `cmdline` string is split into tokens with `parseCmdLine proc`_
## using shell-like quoting rules.
##
## **Parameters:**
##
## - `cmdline`: Command line string to parse. If empty, the real command line
## as provided by the `os` module is retrieved instead. If the command line
## is not available, an assertion will be raised.
## - `cmdline`: Command line string to parse. Empty value means no arguments
## and has no side-effects. To parse the actual OS command line arguments,
## use the `initOptParser(seq[string], seq[char], seq[string], NimMode)`,
## or just omit this parameter, which the compiler will resolve to that
## overload automatically.
## - `shortNoVal`: Set of short option characters that do not accept values.
## See `shortNoVal and longNoVal<#nimshortnoval-and-nimlongnoval>`_ for details.
## - `longNoVal`: Sequence of long option names that do not accept values.
Expand All @@ -548,7 +530,7 @@ proc initOptParser*(cmdline = "";
shortNoVal = {'l'}, longNoVal = @["left"])
initOptParser(parseCmdLine(cmdline), shortNoVal, longNoVal, toRules(mode))

proc initOptParser*(cmdline = "";
proc initOptParser*(cmdline: string;
shortNoVal: set[char] = {};
longNoVal: seq[string] = @[];
allowWhitespaceAfterColon: bool): OptParser {.deprecated:
Expand Down Expand Up @@ -585,10 +567,10 @@ proc handleShortOption(p: var OptParser; cmd: string) =
p.inShortState = false
p.pos = 0
inc p.idx, n

template next(): untyped = p.cmds[p.idx + 1]

let canTakeVal = card(p.shortNoVal) > 0 and p.key[0] notin p.shortNoVal
let canTakeVal = card(p.shortNoVal) > 0 and p.key[0] notin p.shortNoVal
if i < cmd.len and cmd[i] in p.separators:
# separator case
if prShortAllowSep in p.rules:
Expand Down Expand Up @@ -779,16 +761,18 @@ iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key,
if p.kind == cmdEnd: break
yield (p.kind, p.key, p.val)

iterator getopt*(cmdline: seq[string] = @[];
iterator getopt*(cmdline: seq[string] = commandLineParams();
shortNoVal: set[char] = {};
longNoVal: seq[string] = @[];
mode: CliMode = NimMode):
tuple[kind: CmdLineKind, key, val: string] =
## Convenience iterator for iterating over command line arguments.
##
## This creates a new `OptParser<#OptParser>`_. If no command line
## arguments are provided, the real command line as provided by the
## `os` module is retrieved instead.
## This creates a new `OptParser<#OptParser>`_.
##
## `cmdline` is a sequence of arguments to parse. Defaults to
## `os.commandLineParams()`, which retrieves the actual command line arguments
## at the call site. Empty value means no arguments and has no side-effects.
##
## `shortNoVal` and `longNoVal` are used to specify which options
## do not take values. See the `documentation about these
Expand Down
26 changes: 23 additions & 3 deletions lib/std/cmdline.nim
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,14 @@ when declared(paramCount) or defined(nimdoc):
proc commandLineParams*(): seq[string] =
## Convenience proc which returns the command line parameters.
##
## This returns **only** the parameters. If you want to get the application
## Unlike `paramStr proc`_, returns **only** the parameters.
## If you want to get the application
## executable filename, call `getAppFilename() <os.html#getAppFilename>`_.
##
## When used from NimScript, arguments preceding and including the `.nims`
## file are also excluded, returning only the arguments intended for the
## script.
##
## **Availability**: On Posix there is no portable way to get the command
## line from a DLL and thus the proc isn't defined in this environment. You
## can test for its availability with `declared()
Expand All @@ -303,8 +308,23 @@ when declared(paramCount) or defined(nimdoc):
## # Do something else!
## ```
result = @[]
for i in 1..paramCount():
result.add(paramStr(i))
when defined(nimscript):
func isNimScriptExt(s: openArray[char]): bool =
let L = s.len
(L >= 5 and s[L-5] == '.' and (
(s[L-4] in {'n', 'N'}) and
(s[L-3] in {'i', 'I'}) and
(s[L-2] in {'m', 'M'}) and
(s[L-1] in {'s', 'S'})))
var firstNimsFound = false
for i in 0..paramCount(): # nimscript needs to check index 0
if firstNimsFound:
result.add(paramStr(i))
elif isNimScriptExt(paramStr(i)):
firstNimsFound = true
else:
for i in 1..paramCount():
result.add(paramStr(i))
else:
proc commandLineParams*(): seq[string] {.error:
"commandLineParams() unsupported by dynamic libraries".} =
Expand Down
3 changes: 2 additions & 1 deletion nimsuggest/nimsuggest.nim
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,8 @@ proc mainCommand(graph: ModuleGraph) =
close(results)

proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
var p = parseopt.initOptParser(cmd)
var p = if cmd.len == 0: parseopt.initOptParser()
else: parseopt.initOptParser(cmd)
var findProject = false
while true:
parseopt.next(p)
Expand Down