Skip to content

Instantly share code, notes, and snippets.

@DavidGamba
Created May 22, 2014 05:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save DavidGamba/b3287d40b019e498982c to your computer and use it in GitHub Desktop.
Save DavidGamba/b3287d40b019e498982c to your computer and use it in GitHub Desktop.
Scala OptionParser that doesn't worry about descriptions (we write man pages for that)
def main(args: Array[String]) {
// Required positional arguments by key in options
val required = List('arg1, 'arg2)
// Options with value
val optional = Map("--option|-o" -> 'option, "-t|--test" -> 'test)
// Flags
val flags = Map("--flag1|-f" -> 'flag1, "--flag2" -> 'flag2)
// Default options that are passed in
var defaultOptions = Map[Symbol, String]( 'test -> "hello world!")
// Parse options based on the command line args
val options = parseOptions(args.toList, required, optional, flags, defaultOptions)
println(options)
}
implicit class OptionMapImprovements(val m: Map[String, Symbol]) {
def match_key(opt: String): String = {
m.keys.find(_.matches(s""".*$opt(\\|.*)?""")).getOrElse("")
}
def match_get(opt: String): Option[Symbol] = {
m.get(m.match_key(opt))
}
def match_apply(opt: String): Symbol = {
m(m.match_key(opt))
}
}
type OptionMap = Map[Symbol, String]
type OptionMapBuilder = Map[String, Symbol]
def parseOptions(args: List[String],
required: List[Symbol],
optional: OptionMapBuilder,
flags: OptionMapBuilder,
options: OptionMap = Map[Symbol, String](),
strict: Boolean = false
): OptionMap = {
args match {
// Empty list
case Nil => options
// Options with values
case key :: value :: tail if optional.match_get(key) != None =>
parseOptions(tail, required, optional, flags, options ++ Map(optional.match_apply(key) -> value))
// Flags
case key :: tail if flags.match_get(key) != None =>
parseOptions(tail, required, optional, flags, options ++ Map(flags.match_apply(key) -> "true"))
// Positional arguments
case value :: tail if required != Nil =>
parseOptions(tail, required.tail, optional, flags, options ++ Map(required.head -> value))
// Generate symbols out of remaining arguments
case value :: tail if !strict => parseOptions(tail, required, optional, flags, options ++ Map(Symbol(value) -> value))
case _ if strict =>
printf("Unknown argument(s): %s\n", args.mkString(", "))
sys.exit(1)
}
}
@DavidGamba
Copy link
Author

Based on this: http://stackoverflow.com/a/19056645/1601989
Changes:

  • Only fail if strict is set
  • Make defaultOptions optional. Should do the same with every other argument.
  • If strict is not set, create symbols out of all extra cmd line arguments.
  • Allow use of flags and options with values.
  • Allow using short and long options. e.g. -t|--test.

@NeuroNex
Copy link

Hi David,

Thanks very much for your contribution. In parseOptions the recursive call to itself must also pass the strict parameter:

def parseOptions(args:     List[String],
                 required: List[Symbol],
                 optional: OptionMapBuilder,
                 flags:    OptionMapBuilder,
                 defaultOptions: OptionMap = Map[Symbol, String](),
                 strict:   Boolean = false
                ): OptionMap = {
  args match {
    // Empty list
    case Nil => defaultOptions

    // Options with values
    case key :: value :: tail if optional.match_get(key).isDefined =>
      parseOptions(tail, required, optional, flags,  defaultOptions ++ Map(optional.match_apply(key) -> value), strict)

    // Flags
    case key :: tail if flags.match_get(key).isDefined =>
      parseOptions(tail, required, optional, flags,  defaultOptions ++ Map(flags.match_apply(key) -> "true"), strict)

    // Positional arguments
    case value :: tail if required != Nil =>
      parseOptions(tail, required.tail, optional, flags,  defaultOptions ++ Map(required.head -> value), strict)

    // Generate symbols out of remaining arguments
    case value :: tail if !strict => parseOptions(tail, required, optional, flags,  defaultOptions ++ Map(Symbol(value) -> value), strict)

    case _ if strict =>
      printf("UNKNOWN ARGUMENT(s): %s\n", args.mkString(", "))
      sys.exit(1)
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment