com.loopfor

scalop

package scalop

A simple option parser.

Overview

An option parser is an ordered collection of option name and option processor pairs that matches on a sequence of arguments, yielding a map of option names to option values. As the parser recognizes option names in the argument sequence, it applies the corresponding processor function to obtain the option value. This process is repeated until the argument sequence is exhausted or the parser encounters an error.

An option name can be expressed in both long form (a string with at least two characters) and short form (a single character). For any given sequence of arguments, long options are detected by the presence of a -- prefix and short options with a - prefix. Examples include --verbose and -v, respectively.

An option processor is an arbitrary function whose purpose is to return a value that gets assigned to the option name. A processor may consider and absorb subsequent arguments in computing the value, such as --timeout that might expect the next argument to be an integer.

Parser Construction

The construction of a parser is best done using the DSL, which is available using the following import statement.

import com.loopfor.scalop._

The following is a simple parser that demonstrates how the DSL is used to pair an option name with a corresponding processor.

val parser = ("verbose", 'v') ~> set(true)

The ~> method implicitly converts the tuple ("verbose", 'v') into an option, recognizing both long and short forms, and associates the processor function set(true) with that option.

Invoking parser with an empty sequence yields the following map of option names to option values. The @ option is a special name containing all non-option values trailing the last option in the argument sequence. This is discussed in more detail below.

"@" -> Seq()

Invoking parser with the sequence ("--verbose") or ("-v") produces the following map. Notice that the value is associated with both long and short forms of the option name regardless of which form is specified in the argument sequence.

"verbose" -> true
"v" -> true
"@" -> Seq()

The following is a more advanced parser that demonstrates additional features of the DSL.

val parser =
  ("file", 'f') ~> as { (arg, _) => new File(arg) } ++
  "timeout" ~> asInt ~~ 0 ++
  '?' ~> enable ~~ false

The ~~ operator following a processor function associates a default value, which is assigned to the option in the absence of being specified in the argument sequence.

The ++ operator concatenates options, producing a sequence whose order of traversal also defines the order of evaluation.

Note that, in addition to being defined in both long and short form, an option may also be specified using only one form. For example, the expression "timeout" ~> {...} creates an option with the long name "timeout", and similarly, the expression 't' ~> {...} creates an option with the short name "t".

Parser Behavior

Given a sequence of arguments, such as those provided by a shell when supplying arguments to a command line program, an option parser behaves in the following manner.

First, default values for each option, if specified, are assigned to the option value map. The parser then recursively applies the following algorithm as it traverses the argument sequence.

If the next argument is equal to "--", the sequence of all subsequent arguments is assigned to the special option name "@" and the parser terminates, returning the option value map. By convention, the -- option is used to explicitly terminate options so that remaining arguments, which might be prefixed with -- or -, are not treated as options.

If the next argument is either a long or short option name recognized by the parser, the corresponding processor function is applied to the remaining arguments, yielding a value, which is then associated with the option name, both long and short, in the option value map. However, if the argument happens to be an option that is not recognized by the parser, then an exception is thrown. Otherwise, the parser is recursively applied to the remaining sequence of arguments.

If the next argument is not an option, implying that it contains neither a -- nor a - prefix, then the remaining argument sequence is assigned to the "@" option and the parser terminates.

Option Processors

An option processor is a function accepting as input:

and returning as output a tuple containing:

The argument sequence provided as input are those that follow the recognized option. For example, given the following argument sequence:

--verbose -F foo.out --timeout 10

If -F was recognized by the parser, then the sequence provided to the associated processor function would be:

foo.out --timeout 10

Since a processor may expect additional arguments following the option, as is the case with -F, the resulting sequence should be the arguments that follow those absorbed by the processor, which in this case, would be:

--timeout 10

In cases where a processor requires additional arguments, it is often necessary to perform some degree of validation or transformation, both of which may fail. Exceptions that propagate beyond the processor function are caught by the parser and converted to OptException. Additionally, the yell() methods are provided as a convenience for processor implementations to throw instances of OptException.

Predefined Processors

A handful of predefined processors are made available to simplify the construction of options.

For standalone options with no additional arguments, such as --verbose, set can be used to explicitly assign a value.

("verbose", 'v') ~> set(true)

Since many standalone options need only convey a boolean value, enable and disable are shorthand for set(true) and set(false), respectively.

The as method constructs a processor in cases where an option requires only a single additional argument, thereby allowing the signature of the function to be simplified. The simplification comes from removing explicit handling of the argument sequence.

("timeout", 't') ~> as { (arg, opts) => arg.toShort }

In addition, most of the primitive types have a prebuilt processor that performs the necessary conversion:

Linear Supertypes
AnyRef, Any
Ordering
  1. Alphabetic
  2. By inheritance
Inherited
  1. scalop
  2. AnyRef
  3. Any
  1. Hide All
  2. Show all
Learn more about member selection
Visibility
  1. Public
  2. All

Type Members

  1. implicit class LongOptName extends OptName

  2. implicit class LongShortOptName extends OptName

  3. trait Opt[+A] extends AnyRef

    An option definition.

  4. class OptException extends Exception

    An exception thrown by OptParser.

  5. abstract class OptName extends AnyRef

  6. trait OptParser extends AnyRef

    An option parser defined with a sequence of option definitions.

  7. type OptProcessor[+A] = (Seq[String], Map[String, Any]) ⇒ (Seq[String], A)

  8. trait OptResult extends AnyRef

    The result of parsing an argument sequence.

  9. implicit class ShortOptName extends OptName

Value Members

  1. object Opt

    Constructs Opt values.

  2. object OptParser

    Constructs OptParser values.

  3. object OptResult

    Constructs OptResult values.

  4. def as[A](fn: (String, Map[String, Any]) ⇒ A): (Seq[String], Map[String, Any]) ⇒ (Seq[String], A)

    Returns a processor that transforms a single argument.

    Returns a processor that transforms a single argument.

    Example

    val parser = ("level", 'L') ~> as { (arg, opts) => arg.toInt } ~~ 0
    A

    the option type

    fn

    a function that transforms an argument to an instance of A

  5. def asBoolean: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Boolean)

    Return a processor that converts an argument to a Boolean.

    Return a processor that converts an argument to a Boolean.

    The resulting processor throws an OptException if the argument cannot be converted.

  6. def asByte: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Byte)

    Return a processor that converts an argument to a Byte.

    Return a processor that converts an argument to a Byte.

    The resulting processor throws an OptException if the argument cannot be converted.

  7. def asCharset: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Charset)

    Return a processor that converts an argument to a Charset.

    Return a processor that converts an argument to a Charset.

    Example

    val parser = ("encoding", 'E') ~> asCharset ~~ Charset.forName("UTF-8")
    

    The resulting processor throws an OptException if the argument refers to an unknown character set.

  8. def asDouble: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Double)

    Return a processor that converts an argument to a Double.

    Return a processor that converts an argument to a Double.

    The resulting processor throws an OptException if the argument cannot be converted.

  9. def asFloat: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Float)

    Return a processor that converts an argument to a Float.

    Return a processor that converts an argument to a Float.

    The resulting processor throws an OptException if the argument cannot be converted.

  10. def asInt: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Int)

    Return a processor that converts an argument to a Int.

    Return a processor that converts an argument to a Int.

    The resulting processor throws an OptException if the argument cannot be converted.

  11. def asLong: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Long)

    Return a processor that converts an argument to a Long.

    Return a processor that converts an argument to a Long.

    The resulting processor throws an OptException if the argument cannot be converted.

  12. def asShort: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Short)

    Return a processor that converts an argument to a Short.

    Return a processor that converts an argument to a Short.

    The resulting processor throws an OptException if the argument cannot be converted.

  13. def asSomeBoolean: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Option[Boolean])

    Return a processor that converts an argument to a Some[Boolean].

    Return a processor that converts an argument to a Some[Boolean].

    The resulting processor throws an OptException if the argument cannot be converted.

  14. def asSomeByte: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Option[Byte])

    Return a processor that converts an argument to a Some[Byte].

    Return a processor that converts an argument to a Some[Byte].

    The resulting processor throws an OptException if the argument cannot be converted.

  15. def asSomeCharset: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Option[Charset])

    Return a processor that converts an argument to a Some[Charset].

    Return a processor that converts an argument to a Some[Charset].

    Example

    val parser = ("encoding", 'E') ~> asSomeCharset ~~ None

    The resulting processor throws an OptException if the argument refers to an unknown character set.

  16. def asSomeDouble: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Option[Double])

    Return a processor that converts an argument to a Some[Double].

    Return a processor that converts an argument to a Some[Double].

    The resulting processor throws an OptException if the argument cannot be converted.

  17. def asSomeFloat: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Option[Float])

    Return a processor that converts an argument to a Some[Float].

    Return a processor that converts an argument to a Some[Float].

    The resulting processor throws an OptException if the argument cannot be converted.

  18. def asSomeInt: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Option[Int])

    Return a processor that converts an argument to a Some[Int].

    Return a processor that converts an argument to a Some[Int].

    The resulting processor throws an OptException if the argument cannot be converted.

  19. def asSomeLong: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Option[Long])

    Return a processor that converts an argument to a Some[Long].

    Return a processor that converts an argument to a Some[Long].

    The resulting processor throws an OptException if the argument cannot be converted.

  20. def asSomeShort: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Option[Short])

    Return a processor that converts an argument to a Some[Short].

    Return a processor that converts an argument to a Some[Short].

    The resulting processor throws an OptException if the argument cannot be converted.

  21. def asSomeString: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Option[String])

    Return a processor that simply returns an argument as a Some[String].

  22. def asString: (Seq[String], Map[String, Any]) ⇒ (Seq[String], String)

    Return a processor that simply returns an argument as a String.

  23. def disable: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Boolean)

    Return a processor whose value is false.

    Return a processor whose value is false.

    This is shorthand for set(false).

  24. def enable: (Seq[String], Map[String, Any]) ⇒ (Seq[String], Boolean)

    Return a processor whose value is true.

    Return a processor whose value is true.

    This is shorthand for set(true).

  25. implicit def optToParser(opt: Opt[_]): OptParser

  26. def set[A](fn: ⇒ A): (Seq[String], Map[String, Any]) ⇒ (Seq[String], A)

    Returns a processor whose value does not depend on additional arguments.

    Returns a processor whose value does not depend on additional arguments.

    Example

    val parser = ("verbose", 'v') ~> set(true) ~~ false
    A

    the option type

    fn

    a function that returns an instance of A

  27. def yell(message: String, cause: Throwable): Nothing

    A convenience method that throws OptException.

  28. def yell(message: String): Nothing

    A convenience method that throws OptException.

Inherited from AnyRef

Inherited from Any

Ungrouped