# parseopt

Dark Mode

- Manual
- Standard library
- Index
- Compiler docs
- Fusion docs

Search:

This module provides the standard Nim command line parser. It supports one convenience iterator over all command line options and some lower-level features.

# Supported Syntax

The following syntax is supported when arguments for the `shortNoVal` and `longNoVal` parameters, which are described later, are not provided:

1. Short options: `-abcd`, `-e:5`, `-e=5`
2. Long options: `--foo:bar`, `--foo=bar`, `--foo`
3. Arguments: everything that does not start with a `-`

These three kinds of tokens are enumerated in the CmdLineKind enum.

When option values begin with ':' or '=', they need to be doubled up (as in `--delim::`) or alternated (as in `--delim=:`).

The `--` option, commonly used to denote that every token that follows is an argument, is interpreted as a long option, and its name is the empty string.

# Parsing

Use an OptParser to parse command line options. It can be created with initOptParser, and next advances the parser by one token.

For each token, the parser's `kind`, `key`, and `val` fields give information about that token. If the token is a long or short option, `key` is the option's name, and `val` is either the option's value, if provided, or the empty string. For arguments, the `key` field contains the argument itself, and `val` is unused. To check if the end of the command line has been reached, check if `kind` is equal to `cmdEnd`.

Here is an example:

    import parseopt

    var p = initOptParser("-ab -e:5 --foo --bar=20 file.txt")
    while true:
      p.next()
      case p.kind
      of cmdEnd: break
      of cmdShortOption, cmdLongOption:
        if p.val == "":
          echo "Option: ", p.key
        else:
          echo "Option and value: ", p.key, ", ", p.val
      of cmdArgument:
        echo "Argument: ", p.key

    # Output:
    # Option: a
    # Option: b
    # Option and value: e, 5
    # Option: foo
    # Option and value: bar, 20
    # Argument: file.txt

The getopt iterator, which is provided for convenience, can be used to iterate through all command line options as well.

# `shortNoVal` and `longNoVal`

The optional `shortNoVal` and `longNoVal` parameters present in initOptParser are for specifying which short and long options do not accept values.

When `shortNoVal` is non-empty, users are not required to separate short options and their values with a ':' or '=' since the parser knows which options accept values and which ones do not. This behavior also applies for long options if `longNoVal` is non-empty. For short options, `-j4` becomes supported syntax, and for long options, `--foo bar` becomes supported. This is in addition to the previously mentioned syntax. Users can still separate options and their values with ':' or '=', but that becomes optional.

As more options which do not accept values are added to your program, remember to amend `shortNoVal` and `longNoVal` accordingly.

The following example illustrates the difference between having an empty `shortNoVal` and `longNoVal`, which is the default, and providing arguments for those two parameters:

    import parseopt

    proc printToken(kind: CmdLineKind, key: string, val: string) =
      case kind
      of cmdEnd: doAssert(false)  # Doesn't happen with getopt()
      of cmdShortOption, cmdLongOption:
        if val == "":
          echo "Option: ", key
        else:
          echo "Option and value: ", key, ", ", val
      of cmdArgument:
        echo "Argument: ", key

    let cmdLine = "-j4 --first bar"

    var emptyNoVal = initOptParser(cmdLine)
    for kind, key, val in emptyNoVal.getopt():
      printToken(kind, key, val)

    # Output:
    # Option: j
    # Option: 4
    # Option: first
    # Argument: bar

    var withNoVal = initOptParser(cmdLine, shortNoVal = {'c'},
                                  longNoVal = @["second"])
    for kind, key, val in withNoVal.getopt():
      printToken(kind, key, val)

    # Output:
    # Option and value: j, 4
    # Option and value: first, bar

# See also

- os module for lower-level command line parsing procs
- parseutils module for helpers that parse tokens, numbers, identifiers, etc.
- strutils module for common string handling operations
- json module for a JSON parser
- parsecfg module for a configuration file parser
- parsecsv module for a simple CSV (comma separated value) parser
- parsexml module for a XML / HTML parser
- other parsers for more parsers

# Imports

os

# Types

    CmdLineKind = enum
      cmdEnd,                   ## End of command line reached
      cmdArgument,              ## An argument such as a filename
      cmdLongOption,            ## A long option such as --option
      cmdShortOption             ## A short option such as -c

  The detected command line token. Source Edit

    OptParser = object of RootObj
      pos*: int
      inShortState: bool
      allowWhitespaceAfterColon: bool
      shortNoVal: set[char]
      longNoVal: seq[string]
      cmds: seq[string]
      idx: int
      kind*: CmdLineKind         ## The detected command line token
      key*, val*: TaintedString  ## Key and value pair; the key is the option
                                 ## or the argument, and the value is not "" if
                                 ## the option was given a value

  Implementation of the command line parser.

  To initialize it, use the initOptParser proc.

  Source Edit

# Procs

    proc initOptParser(cmdline = ""; shortNoVal: set[char] = {};
                       longNoVal: seq[string] = @[];
                       allowWhitespaceAfterColon = true): OptParser {...}{.raises: [],
        tags: [ReadIOEffect].}

  Initializes the command line parser.

  If `cmdline == ""`, the real command line as provided by the `os` module is retrieved instead.

  `shortNoVal` and `longNoVal` are used to specify which options do not take values. See the documentation about these parameters for more information on how this affects parsing.

  See also:

  - getopt iterator

  Example:

      var p = initOptParser()
      p = initOptParser("--left --debug:3 -l -r:2")
      p = initOptParser("--left --debug:3 -l -r:2",
                        shortNoVal = {'l'}, longNoVal = @["left"])

  Source Edit

    proc initOptParser(cmdline: seq[TaintedString]; shortNoVal: set[char] = {};
                       longNoVal: seq[string] = @[];
                       allowWhitespaceAfterColon = true): OptParser {...}{.raises: [],
        tags: [ReadIOEffect].}

  Initializes the command line parser.

  If `cmdline.len == 0`, the real command line as provided by the `os` module is retrieved instead. Behavior of the other parameters remains the same as in initOptParser(string, ...).

  See also:

  - getopt iterator

  Example:

      var p = initOptParser()
      p = initOptParser(@["--left", "--debug:3", "-l", "-r:2"])
      p = initOptParser(@["--left", "--debug:3", "-l", "-r:2"],
                        shortNoVal = {'l'}, longNoVal = @["left"])

  Source Edit

    proc next(p: var OptParser) {...}{.gcsafe, extern: "npo$1", raises: [], tags: [].}

  Parses the next token.

  `p.kind` describes what kind of token has been parsed. `p.key` and `p.val` are set accordingly.

  Example:

      var p = initOptParser("--left -r:2 file.txt")
      p.next()
      doAssert p.kind == cmdLongOption and p.key == "left"
      p.next()
      doAssert p.kind == cmdShortOption and p.key == "r" and p.val == "2"
      p.next()
      doAssert p.kind == cmdArgument and p.key == "file.txt"
      p.next()
      doAssert p.kind == cmdEnd

  Source Edit

    proc cmdLineRest(p: OptParser): TaintedString {...}{.gcsafe, extern: "npo$1",
        raises: [], tags: [].}

  Retrieves the rest of the command line that has not been parsed yet.

  See also:

  - remainingArgs proc

  Examples:

      var p = initOptParser("--left -r:2 -- foo.txt bar.txt")
      while true:
        p.next()
        if p.kind == cmdLongOption and p.key == "":  # Look for "--"
          break
        else: continue
      doAssert p.cmdLineRest == "foo.txt bar.txt"

  Source Edit

    proc remainingArgs(p: OptParser): seq[TaintedString] {...}{.gcsafe, extern: "npo$1",
        raises: [], tags: [].}

  Retrieves a sequence of the arguments that have not been parsed yet.

  See also:

  - cmdLineRest proc

  Examples:

      var p = initOptParser("--left -r:2 -- foo.txt bar.txt")
      while true:
        p.next()
        if p.kind == cmdLongOption and p.key == "":  # Look for "--"
          break
        else: continue
      doAssert p.remainingArgs == @["foo.txt", "bar.txt"]

  Source Edit

# Iterators

    iterator getopt(p: var OptParser): tuple[kind: CmdLineKind,
        key, val: TaintedString] {...}{.raises: [], tags: [].}

  Convenience iterator for iterating over the given OptParser.

  There is no need to check for `cmdEnd` while iterating.

  See also:

  - initOptParser proc

  Examples:

      # these are placeholders, of course
      proc writeHelp() = discard
      proc writeVersion() = discard

      var filename: string
      var p = initOptParser("--left --debug:3 -l -r:2")

      for kind, key, val in p.getopt():
        case kind
        of cmdArgument:
          filename = key
        of cmdLongOption, cmdShortOption:
          case key
          of "help", "h": writeHelp()
          of "version", "v": writeVersion()
        of cmdEnd: assert(false) # cannot happen
      if filename == "":
        # no filename has been given, so we show the help
        writeHelp()

  Source Edit

    iterator getopt(cmdline: seq[TaintedString] = commandLineParams();
                    shortNoVal: set[char] = {}; longNoVal: seq[string] = @[]): tuple[
        kind: CmdLineKind, key, val: TaintedString] {...}{.raises: [],
        tags: [ReadIOEffect].}

  Convenience iterator for iterating over command line arguments.

  This creates a new OptParser. If no command line arguments are provided, the real command line as provided by the `os` module is retrieved instead.

  `shortNoVal` and `longNoVal` are used to specify which options do not take values. See the documentation about these parameters for more information on how this affects parsing.

  There is no need to check for `cmdEnd` while iterating.

  See also:

  - initOptParser proc

  Examples:

      # these are placeholders, of course
      proc writeHelp() = discard
      proc writeVersion() = discard

      var filename: string
      let params = @["--left", "--debug:3", "-l", "-r:2"]

      for kind, key, val in getopt(params):
        case kind
        of cmdArgument:
          filename = key
        of cmdLongOption, cmdShortOption:
          case key
          of "help", "h": writeHelp()
          of "version", "v": writeVersion()
        of cmdEnd: assert(false) # cannot happen
      if filename == "":
        # no filename has been written, so we show the help
        writeHelp()

  Source Edit