CLI Package

The CLI package provides enhanced Posix+GNU command line parsing with the feature of commands that can be specified in a hierarchy.

See RFC-0038 for more background.

The general EBNF of a command line is:

  command_line ::= root_command (option | command)* (option | arg)*
  command ::= alphanum_word
  alphanum_word ::= alphachar(alphachar | numchar | '_' | '-')*
  option ::= longoption | shortoptionset
  longoption ::= '--'alphanum_word['='arg | ' 'arg]
  shortoptionset := '-'alphachar[alphachar]...['='arg | ' 'arg]
  arg := boolarg | intarg | floatarg | stringarg
  boolarg := 'true' | 'false'
  intarg> := ['-'] numchar...
  floatarg ::= ['-'] numchar... ['.' numchar...]
  stringarg ::= anychar

Some examples:

  usage: ls [<options>] [<args> ...]
  usage: make [<options>] <command> [<options>] [<args> ...]
  usage: chat [<options>] <command>  <subcommand> [<options>] [<args> ...]

Usage

The types in the cli package are broken down into three groups:

Specs

Pony programs use constructors to create the spec objects to specify their command line syntax. Many aspects of the spec are checked for correctness at compile time, and the result represents everything the parser needs to know when parsing a command line or forming syntax help messages.

Option and Arg value types

Options and Args parse values from the command line as one of four Pony types: Bool, String, I64 and F64. Values of each of these types can then be retrieved using the corresponding accessor funtions.

In addition, there is a string_seq type that accepts string values from the command line and collects them into a sequence which can then be retrieved as a ReadSeq[String] using the string_seq() accessor function.

Some specific details:

  • bool Options: have a default value of 'true' if no value is given. That is, -f is equivalent to -f=true.

  • string_seq Options: the option prefix has to be used each time, like: --file=f1 --file=f2 --file=f3 with the results being collected into a single sequence.

  • string_seq Args: there is no way to indicate termination, so a string_seq Arg should be the last arg for a command, and will consume all remaining command line arguments.

Parser

Programs then use the CommandSpec they've built to instantiate a parser to parse any given command line. This is often env.args(), but could also be commands from files or other input sources. The result of a parse is either a parsed command, a command help, or a syntax error object.

Commands

Programs then match the object returned by the parser to determine what kind it is. Errors and help requests typically print messages and exit. For commands, the fullname can be matched and the effective values for the command's options and arguments can be retrieved.

Example program

This program echos its command line arguments with the option of uppercasing them.

use "cli"

actor Main
  new create(env: Env) =>
    let cs =
      try
        CommandSpec.leaf("echo", "A sample echo program", [
          OptionSpec.bool("upper", "Uppercase words"
            where short' = 'U', default' = false)
        ], [
          ArgSpec.string_seq("words", "The words to echo")
        ])? .> add_help()?
      else
        env.exitcode(-1)  // some kind of coding error
        return
      end

    let cmd =
      match CommandParser(cs).parse(env.args, env.vars())
      | let c: Command => c
      | let ch: CommandHelp =>
          ch.print_help(env.out)
          env.exitcode(0)
          return
      | let se: SyntaxError =>
          env.out.print(se.string())
          env.exitcode(1)
          return
      end

    let upper = cmd.option("upper").bool()
    let words = cmd.arg("words").string_seq()
    for word in words.values() do
      env.out.write(if upper then word.upper() else word end + " ")
    end
    env.out.print("")

Public Types

Private Types