Command Line Subcommands in Lisp

Here’s the translation of the Go code to Lisp, formatted in Markdown suitable for Hugo:

Our first example demonstrates how to create command-line subcommands with their own set of flags. This is similar to how tools like git have different subcommands (e.g., git commit, git push) with their own specific options.

(defun main ()
  ;; We declare subcommands using make-command-table and define
  ;; new flags specific for each subcommand.
  (let ((foo-cmd (make-command-table :name "foo"
                                     :options '(("enable" :type boolean :initial-value nil)
                                                ("name" :type string :initial-value ""))))
        (bar-cmd (make-command-table :name "bar"
                                     :options '(("level" :type integer :initial-value 0)))))

    ;; The subcommand is expected as the first argument to the program.
    (when (< (length (uiop:command-line-arguments)) 1)
      (format t "expected 'foo' or 'bar' subcommands~%")
      (uiop:quit 1))

    ;; Check which subcommand is invoked.
    (let ((subcommand (first (uiop:command-line-arguments))))
      (cond
        ((string= subcommand "foo")
         (multiple-value-bind (options free-args)
             (command-line-arguments:process-command-line-options foo-cmd (rest (uiop:command-line-arguments)))
           (format t "subcommand 'foo'~%")
           (format t "  enable: ~a~%" (getf options :enable))
           (format t "  name: ~a~%" (getf options :name))
           (format t "  tail: ~a~%" free-args)))
        
        ((string= subcommand "bar")
         (multiple-value-bind (options free-args)
             (command-line-arguments:process-command-line-options bar-cmd (rest (uiop:command-line-arguments)))
           (format t "subcommand 'bar'~%")
           (format t "  level: ~a~%" (getf options :level))
           (format t "  tail: ~a~%" free-args)))
        
        (t
         (format t "expected 'foo' or 'bar' subcommands~%")
         (uiop:quit 1))))))

To run the program, you would typically save this code in a file (e.g., command-line-subcommands.lisp) and use your Lisp implementation to compile and run it. Here are some example invocations:

First, invoke the foo subcommand:

$ sbcl --script command-line-subcommands.lisp foo --enable --name=joe a1 a2
subcommand 'foo'
  enable: T
  name: joe
  tail: (a1 a2)

Now try bar:

$ sbcl --script command-line-subcommands.lisp bar --level 8 a1
subcommand 'bar'
  level: 8
  tail: (a1)

But bar won’t accept foo’s flags:

$ sbcl --script command-line-subcommands.lisp bar --enable a1
expected 'foo' or 'bar' subcommands

This Lisp implementation uses a hypothetical make-command-table function and the command-line-arguments library to parse command-line options. The exact implementation might vary depending on the specific Lisp dialect and libraries you’re using.

In Lisp, we don’t have a direct equivalent of Go’s flag package, so we’ve simulated similar functionality using a custom approach. The cond statement is used instead of a switch to handle different subcommands.

Next, we’ll look at environment variables, another common way to parameterize programs.