chickadee » chibi-app » run-application

run-application spec #!rest oprocedure

The high-level interface. Parses a command-line with optional and/or positional arguments, with arbitrarily nested subcommands (optionally having their own arguments), and calls the corresponding main procedure on the parsed config.

Given an application spec spec, parses the given command-line arguments args into a config object (from (chibi config)), prepended to the existing object config if given. Then runs the corresponding command (or sub-command) procedure from spec on the following arguments:

(<proc> <config> <spec> <positional args> ...)

The app spec should be a list of the form:

(<command> [<doc-string>] <clauses> ...)

where clauses can be any of:

(@ <opt-spec>)
option spec, described below
(begin: <begin-proc>)
procedure to run before main
(end: <end-proc>)
procedure to run after main
(types: (<type-name> <parser>) ...)
additional types that can be used in argument parsing
(<proc> args ...)
main procedure (args only for documentation)
<app-spec>
a subcommand described by the nested spec
(or <app-spec> ...)
an alternate list of subcommands

For subcommands the symbolic command name must match, though it is ignored for the initial spec (i.e. the application name is not checked). The begin and end procedures can be useful for loading and saving state common to all subcommands.

The opt-spec describes command-line options, and is a simple list with each opt of the form:

(<name> <type> [(<aliases> ...)] [<doc-string>])

where <name> is a symbol name, <aliases> is an optional list of strings (for long options) or characters (for short options) to serve as aliases in addition to the exact name. type can be any of:

boolean
boolean, associated value optional, allowing --noname to indicate #false
char
a single character
integer
an exact integer
real
any real number
number
any real or complex number
symbol
a symbol
string
a string
sexp
a sexp parsed with read
(list <type>)
a comma-delimited list of types

Note that the options specs are composed entirely of objects that can be read and written, thus for example optionally loaded from files, whereas the app specs include embedded procedure objects so are typically written with quasiquote.

Complete Example (from chibi) - stripped down ls(1):

(import (scheme base)
        (scheme process-context)
        (scheme write)
        (srfi 130)
        (chibi app)
        (chibi config)
        (chibi filesystem))

(define (ls cfg spec . files)
  (for-each
    (lambda (x)
      (for-each
        (lambda (file)
          (unless (and (string-prefix? "." file)
                       (not (conf-get cfg 'all)))
            (write-string file)
            (when (conf-get cfg 'long)
              (write-string 
)
              (write (file-modification-time file)))
            (newline)))
        (if (file-directory? x) (directory-files x) (list x))))
    files))

(run-application
  `(ls
    "list directory contents"
    (@
     (long boolean (#\l) "use a long listing format")
     (all boolean (#\a) "do not ignore entries starting with ."))
    (,ls files ...))
  (command-line))

Subcommand Skeleton Example:

(run-application
  `(zoo
    "Zookeeper Application"
    (@
     (animals (list symbol) "list of animals to act on (default all)")
     (lions boolean (#\l) "also apply the action to lions"))
    (or
     (feed "feed the animals" () (,feed animals ...))
     (wash "wash the animals" (@ (soap boolean)) (,wash animals ...))
     (help "print help" (,app-help-command))))
  (command-line)
  (conf-load (string-append (get-environment-variable "HOME") "/.zoo")))

The second and third arguments here are optional, provided to show the common pattern of allowing the same options to be specified either in a file and/or on the command-line. The above app can be run as:

Feed all animals, including lions:

zoo -l feed

Wash the elephants with soap:

zoo --animals=elephant wash --soap

Print help:

zoo help

The application procedures themselves are of the form:

(proc cfg spec args ...)

where cfg is a config object from (chibi config) holding the parsed option info, spec is the original app spec, and args are the remaining non-option command-line arguments.

To retrieve the options for the above example you can use:

  • (conf-get cfg 'animals)
  • (conf-get cfg 'lions)
  • (conf-get cfg '(command wash soap))

Notice that options for subcommands are nested under the (command <name>) prefix, so that you can use the same name for different subcommands without conflict. This also means the subcommand options are distinct from the top-level options, so when using subcommands users must always write the command line as:

app [<general options>] <subcommand> [<sub options>]

The ~/.zoo file could then hold an sexp of the form:

((animals (camel elephant rhinocerous))
 (command
  (wash
   (soap #t))))