Releasing Romulan 1.0.0 (first release)

I am hereby releasing the first version (1.0.0) of Romulan. The source is available on Gitlab and on Github. The primary site is, for the moment, Gitlab.

Romulan is a declarative interface for the Common Lisp command line parser clingon.

Romulan is currently work in progress. Defining an application with subcommands already works (see example below). For more planned development (= features that do not exist yet, but will some time in the future), see the section Future development in the README.


As already mentioned above only subcommand interfaces (also called git or svn style) can currently be defined in Romulan. Those are interfaces where the command line looks like:

$ ./example-script-2 --user=Jim hello
Hello, Jim!
$ .//example-script-2 shout go for it
GO FOR IT, rak1912!

The words hello and shout are called subcommands here.

You will find the following Romulan example in file example-script-2 in the Romulan source, a slightly larger example in example-script.

In Romulan an interface with subcommands is declared with commandline-subcommand-interface. The declaration needs to contain at least a usage string (that is later used to construct help text) and the definition of global options:

(commandline-subcommand-interface romulan-test "shout some words or say hello"

  :usage   "[-v] [-u <user>] <command> [options ...]"
  :options (:user (:description "user to greet"
                   :short-name #\u
                   :env-vars ("USER"))))

Invoking the command without any options will display a help text that describes usage and global options from the definition above and lists the subcommands (all this courtesy of clingon, Romulan just adds defaults and reformats the definitions in a slightly different format for clingon).

$ ./example-script-2 
  romulan-test - shout some words or say hello

  romulan-test [-v] [-u <user>] <command> [options ...]

      --help          display usage information and exit
      --version       display version and exit
  -u, --user <VALUE>  user to greet [env: $USER]

  shout  shouts back anything you write
  hello  just says hello

Subcommands are defined with define-subcommands and look mostly like function definitions with additional arguments that specify usage, one-line help text and the subcommand options.

(define-subcommand hello (&key user)
    (:description "just says hello")

  (format t "Hello, ~A!~%" user))

(define-subcommand shout (words &key user)

    (:description "shouts back anything you write"
     :usage       "[options ...] [arguments ...]"
     :varargs     t)

  (format t "~{~A~^ ~}, ~A!~%" (mapcar #'string-upcase words) user))

All subcommands are indeed procedures. The &key parameters of the lambda list correspond to options which will be automatically bound when invoking the subcommand. The positional parameters will be bound from the positional arguments of the POSIX argument vector.

When all sub-commands are defined, end-subcommand-interface must be invoked. This actually builds the interfaces declared with commandline-subcommand-interface.


The command line interface thus defined is bound as a procedure to the symbol given to commandline-subcommand-interface and can be invoked as a procedure to process the command-line arguments and invoke the subcommands.


The subcommands are themselves procedures and can simply be called from Lisp (e.g. for testing) like any procedure:

(hello :user "Jim)
Hello, Jim!


Due to legal pitfalls in Europe there is no comment section in this blog at the moment (sorry), but you can discuss this article or comment on its content ⮕ here on Mastodon.