experimental/lexopt

  Source   Edit

This module provides a user-driven command line lexer.

Unlike std/parseopt, this lexer parses command line tokens as requested by the caller.

Example:

import experimental/lexopt
import std/options

type
  Opts = object
    paths: seq[string]
    color: bool
    output: string

var lexer = initCmdLexer(["--color", "/", "-o", "outfile.txt"])
var (kind, option) = lexer.next()
var opts = Opts()
while kind != cmdEnd:
  if (kind == cmdLong and option == "color") or (kind == cmdShort and option == "c"):
    opts.color = true
  elif (kind == cmdLong and option == "output") or (kind == cmdShort and option == "o"):
    let value = lexer.value()
    if value.isNone:
      quit "error: '--output' must be followed by a value"
    opts.output = value.get()
  elif kind == cmdValue:
    opts.paths.add option

  (kind, option) = lexer.next()

doAssert opts.paths == ["/"]
doAssert opts.color == true
doAssert opts.output == "outfile.txt"

Types

CmdLexer = object
  cmdline: seq[string]       ## The command line to parse
  index: int                 ## The index of `cmdline` to be processed
  valueIdx: int ## For long options, the index of `ValueSeparators`.
                ## 
                ## For short options, the index of the next flag to be processed.
                ## 
                ## Otherwise, it is unused.
  
  Source   Edit
CmdlineKind = enum
  cmdEnd,                   ## End of command line
  cmdLong,                  ## A long option (i.e. `--opt`)
  cmdShort,                 ## A short option (i.e. `-o`)
  cmdValue                   ## A value that is not an option
  Source   Edit
LexError = object of CatchableError
  
Errors occurring during lexing.   Source   Edit
UnexpectedValueError = object of LexError
  opt*: string               ## The option that a value was provided to
  value*: string             ## The provided value
  
A value was provided but not consumed.   Source   Edit

Consts

ValueSeparators = {':', '='}
Characters that separate an option's name from its value   Source   Edit

Procs

proc initCmdLexer(args: openArray[string]): CmdLexer {....raises: [], tags: [].}
Creates a new CmdLexer. args should be the command line parameters as returned by commandLineParams.   Source   Edit
proc initCmdLexer(args: sink seq[string]): CmdLexer {....raises: [], tags: [].}
Creates a new CmdLexer. args should be the command line parameters as returned by commandLineParams.   Source   Edit
proc isLongOpt(s: string): bool {....raises: [], tags: [].}
Returns whether s can be parsed as a long option.   Source   Edit
proc isShortOpt(s: string): bool {....raises: [], tags: [].}
Returns whether s can be parsed as a short option.   Source   Edit
proc next(l: var CmdLexer): (CmdlineKind, string) {.
    ...raises: [UnexpectedValueError], tags: [].}

Consume the next option.

The following section describes how various syntaxes are handled:

  1. -- and - are considered values.
  2. --foo:bar and --foo=bar are treated as long options with key foo and bar is considered the value of foo.

    If this value is not consumed before the next call to next(), an error will be raised. See below for more details.

  3. --foo is treated as a long option with key foo.
  4. -a:foo and -a=foo are treated as short options with key a and value foo.
  5. -a is treated as a short option with key a.
  6. -abco is equivalent to -a, -b, -c, -o appearing in sequence. Four calls to next() are required to consume -abco.
  7. -abco:foo and -abco=foo are equivalent to -a, -b, -c, -o:foo appearing in sequence.

    If this value is not consumed before the next call to next(), an error will be raised. See below for more details

  8. Everything else is considered values.

If the previous option have an unconsumed value (e.g. value() was not called for --opt:foo), UnexpectedValueError will be raised.

  Source   Edit
proc prefix(kind: CmdlineKind): string {....raises: [], tags: [].}
Returns the prefix string for a given command line parameter kind.   Source   Edit
proc value(l: var CmdLexer; delimitedOnly = false): Option[string] {....raises: [],
    tags: [].}

Consume the value for the current option, if any.

The following section describes how various syntaxes are handled:

  1. --foo:bar or --foo=bar yields bar if called after next() returns foo.
  2. -abco:foo or -abco=foo yields foo if called after next() returns o.
  3. If delimitedOnly is true, syntaxes below will not be considered.
  4. -abcooutput.txt yields output.txt if called after next() returns o.
  5. The current parameter is treated as a value, regardless of whether it can be considered as an option (ie. --foo --bar will yield --bar if this procedure is called after --foo).
  Source   Edit

Iterators

iterator remaining(l: var CmdLexer): string {....raises: [UnexpectedValueError],
    tags: [].}

Consume the remaining unprocessed parameters.

If the previous option returned from next() have a value that was not consumed by value(), UnexpectedValueError will be raised.

As a special case, any short options within a bundle (ie. -abcde) that have not been processed by next() is considered the value of the last short option returned and will raise UnexpectedValueError.

  Source   Edit