An Emacs Lisp macro to parse arguments in shell scripts

My Emacs system sometimes creeps out into the shell. This usually happens when I want to provide an additional entry point to information I maintain using Emacs.

This means that in my ~/bin I have a few scripts, written in Emacs Lisp, that are meant to be run from the command line, outside of an Emacs client.

With that, comes the need to parse parameters.

After writing the same code a couple of times, and since back then I could not find an obvious other choice to do the same thing, I decided it was a good use case for a macro, which I called with-positional-args.

Here how it looks like in user-space. This is part of a script I use to search into annotated PDFs in a directory:

#!/usr/local/bin/emacs --script

;; preparing load-path and requiring a couple
;; of features, including the one that implements
;; with-positional-args

(defun pdf-search (pattern path)
  ;; the details are irrelevant in this context
  )

;; Here we go!
(with-positional-args ((pattern :mandatory "You must provide a PATTERN")
                       (path :default "."))
  (pdf-search pattern path))

And here the implementation:

;; Caveman args list parsing
(defun arg-resolver (arg-properties idx)
  (pcase (car arg-properties)
    (:default
     `(or (nth ,idx command-line-args)
              ,(cadr arg-properties)))
    (:mandatory
     `(or (nth ,idx command-line-args)
          (error (or ,(cadr arg-properties)
                     "Undefined error"))))
    (_ (nth idx command-line-args))))

(defmacro with-positional-args (arglist &rest body)
  "Bind command-line arguments as per ARGLIST, then evaluate BODY.

Each element of ARGLIST has the form: (VAR) for optional argument, (VAR
:default VALUE) for specifying a default value when missing, (VAR
:mandatory [MSG]) for required arguments with optional error message
MSG."
  (declare (indent 1))
  `(let ,(cl-loop for (arg-name . arg-properties) in arglist
                  for idx from 3
                  collect `(,arg-name ,(arg-resolver
                                        arg-properties idx)))
     ,@body))