Expand string patterns in Elisp
Scoping

In Emacs Lisp a nesting a defun inside another defun does not create a lexically scoped function. The symbol for the 'nested' function is still interned at in the current objarray. The behavior is unlike that of Scheme.

In Emacs Lisp let ((f (lambda (x) (body))))...(funcall f some-value) is the general structure for a 'local' function. 

Formatting

The formatting of the code within the call to pcase is difficult to read. When the condition and consequent cannot be listed on the same line, it is probably more common to place the consequent below the conditional.

Suggested Alternative Format

(defun expand-pattern (pattern)
  (mapcar (lambda (tokens)
            (mapconcat (lambda (token)
                         (format "%s" token))
                       (reverse tokens)
                       ""))
          (expat '(()) pattern)))

(defun expat (parents expr)
  "Utility function for expand pattern."
  (pcase expr
    ((or (pred stringp)
         (pred numberp))
     (mapcar (lambda (parent)
               (cons expr parent))
             parents))
    (`(and ,first)
     (expat parents first))
    (`(and ,first . ,rest)
     (expat (expat parents first)
            (cons 'and rest)))
    (`(or . ,args)
     (apply #'append 
            (mapcar (lambda (arg)
                      (expat parents arg))
                    args)))
    (_ (error "unknown %S" expr))))


Other Remarks

Because expat throws an error, it might make sense to have expand-pattern invoke expat within a try...catch block. Alternatively, expat could return nil when no match is found. If nil is a possible return value from processing the pattern, then a "Lispy" thing to do is to return two values, the first being the result of processing the expression and the second being either nil or t to indicate an error or no error.