branch: elpa/llama commit 67a43eb4f0502d92227b9513eb247355aa5e598e Author: Jonas Bernoulli <jo...@bernoul.li> Commit: Jonas Bernoulli <jo...@bernoul.li>
Support explicit unused arguments - Support trailing unused (mandatory and/or optional) arguments. - Support unused optional arguments, in between (used and/or unused) mandatory arguments and used optional arguments. (In a later step we will start preferring optional arguments, for unused arguments that are not explicitly named.) --- llama.el | 72 ++++++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/llama.el b/llama.el index 33dff5b2e3..a1fc5fd086 100644 --- a/llama.el +++ b/llama.el @@ -105,26 +105,24 @@ omitting the whitespace between it and the following symbol. It also looks a bit like #\\='function." (unless (symbolp fn) (signal 'wrong-type-argument (list 'symbolp fn))) - `(lambda ,(llama--arguments body) - (,fn ,@body))) - -(defun llama--arguments (data) - (let ((args (make-vector 10 nil)) - (optional nil) - (pos 0)) - (llama--collect data args) - (apply #'nconc + (let* ((args (make-vector 10 nil)) + (body (llama--collect body args)) + (optional nil) + (pos 0)) + `(lambda + (,@(apply + #'nconc (mapcar (lambda (symbol) (setq pos (1+ pos)) (cond ((not symbol) (list (intern (format "_%c%s" (if optional ?& ?%) pos)))) - ((eq (aref (symbol-name symbol) 0) ?%) + ((string-match-p "\\`_?%" (symbol-name symbol)) (when optional (error "`%s' cannot follow optional arguments" symbol)) (list symbol)) - ((eq symbol '&*) + ((memq symbol '(&* _&*)) (list '&rest symbol)) (optional (list symbol)) @@ -136,15 +134,19 @@ It also looks a bit like #\\='function." (push symbol symbols))) symbols) (let ((rest (aref args 0))) - (and rest (list rest)))))))) + (and rest (list rest))))))) + (,fn ,@body)))) + +(defconst llama--unused-argument (make-symbol "llama--unused-argument")) (defun llama--collect (expr args) (cond ((symbolp expr) (let ((name (symbol-name expr))) (save-match-data - (when (string-match "\\`[%&]\\([1-9*]\\)?\\'" name) - (let* ((pos (match-string 1 name)) + (cond + ((string-match "\\`\\(_\\)?[%&]\\([1-9*]\\)?\\'" name) + (let* ((pos (match-string 2 name)) (pos (cond ((equal pos "*") 0) ((not pos) 1) ((string-to-number pos))))) @@ -152,17 +154,30 @@ It also looks a bit like #\\='function." (aref args 1) (not (equal expr (aref args 1)))) (error "`%s' and `%s' are mutually exclusive" expr (aref args 1))) - (aset args pos expr)))))) - ((memq (car-safe expr) '(## quote))) + (aset args pos expr)) + (if (match-string 1 name) + llama--unused-argument + expr)) + (expr))))) + ((memq (car-safe expr) '(## quote)) + expr) + ((and (listp expr) (ignore-errors (length expr))) + (mapcan (lambda (elt) + (let ((symbol (llama--collect elt args))) + (and (not (eq symbol llama--unused-argument)) + (list symbol)))) + expr)) ((listp expr) (while (consp (cdr expr)) (llama--collect (car expr) args) (setq expr (cdr expr))) (when expr (llama--collect (car expr) args) - (llama--collect (cdr expr) args))) + (llama--collect (cdr expr) args)) + expr) ((vectorp expr) - (mapc (lambda (elt) (llama--collect elt args)) expr)))) + (vconcat (mapcar (lambda (elt) (llama--collect elt args)) expr))) + (expr))) ;;; Advices @@ -231,10 +246,27 @@ It also looks a bit like #\\='function." (defface llama-optional-argument '((t :inherit font-lock-type-face)) "Face used for optional arguments `&1' through `&9', `&' and `&*'.") +(defface llama-erased-argument + `((((supports :box t)) + :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) + :style nil + :color "red")) + (((supports :underline t)) + :underline "red") + (t + :inherit font-lock-warning-face)) + "Face used for erased arguments `_%1'...`_%9', `_&1'...`_&9' and `_&*'. +This face is used in addition to one of llama's other argument faces. +Unlike implicit unused arguments (which do not appear in the function +body), these arguments are erased from the function body during macro +expansion, and the looks of this face should hint at that.") + (defvar llama-font-lock-keywords '(("(\\(##\\)" 1 'llama-macro) - ("\\_<\\(?:%[1-9]?\\)\\_>" 0 'llama-mandatory-argument) - ("\\_<\\(?:&[1-9*]?\\)\\_>" 0 'llama-optional-argument))) + ("\\_<\\(?:_?%[1-9]?\\)\\_>" 0 'llama-mandatory-argument) + ("\\_<\\(?:_?&[1-9*]?\\)\\_>" 0 'llama-optional-argument) + ("\\_<\\(?:_\\(?:%[1-9]?\\|&[1-9*]?\\)\\)\\_>" + 0 'llama-erased-argument prepend))) (defvar llama-fontify-mode-lighter nil)