Function: smie-bnf--closer-alist
smie-bnf--closer-alist is a byte-compiled function defined in
smie.el.gz.
Signature
(smie-bnf--closer-alist BNF &optional NO-INNERS)
Documentation
Build a closer-alist from a BNF table.
The return value is in the same form as smie-closer-alist.
NO-INNERS if non-nil means that inner keywords will be excluded
from the table, e.g. the table will not include things like ("if" . "else").
Source Code
;; Defined in /usr/src/emacs/lisp/emacs-lisp/smie.el.gz
;; (defun smie-prec2-closer-alist (prec2 include-inners)
;; "Build a closer-alist from a PREC2 table.
;; The return value is in the same form as `smie-closer-alist'.
;; INCLUDE-INNERS if non-nil means that inner keywords will be included
;; in the table, e.g. the table will include things like (\"if\" . \"else\")."
;; (let* ((non-openers '())
;; (non-closers '())
;; ;; For each keyword, this gives the matching openers, if any.
;; (openers (make-hash-table :test 'equal))
;; (closers '())
;; (done nil))
;; ;; First, find the non-openers and non-closers.
;; (maphash (lambda (k v)
;; (unless (or (eq v '<) (member (cdr k) non-openers))
;; (push (cdr k) non-openers))
;; (unless (or (eq v '>) (member (car k) non-closers))
;; (push (car k) non-closers)))
;; prec2)
;; ;; Then find the openers and closers.
;; (maphash (lambda (k _)
;; (unless (member (car k) non-openers)
;; (puthash (car k) (list (car k)) openers))
;; (unless (or (member (cdr k) non-closers)
;; (member (cdr k) closers))
;; (push (cdr k) closers)))
;; prec2)
;; ;; Then collect the matching elements.
;; (while (not done)
;; (setq done t)
;; (maphash (lambda (k v)
;; (when (eq v '=)
;; (let ((aopeners (gethash (car k) openers))
;; (dopeners (gethash (cdr k) openers))
;; (new nil))
;; (dolist (o aopeners)
;; (unless (member o dopeners)
;; (setq new t)
;; (push o dopeners)))
;; (when new
;; (setq done nil)
;; (puthash (cdr k) dopeners openers)))))
;; prec2))
;; ;; Finally, dump the resulting table.
;; (let ((alist '()))
;; (maphash (lambda (k v)
;; (when (or include-inners (member k closers))
;; (dolist (opener v)
;; (unless (equal opener k)
;; (push (cons opener k) alist)))))
;; openers)
;; alist)))
(defun smie-bnf--closer-alist (bnf &optional no-inners)
;; We can also build this closer-alist table from a prec2 table,
;; but it takes more work, and the order is unpredictable, which
;; is a problem for smie-close-block.
;; More convenient would be to build it from a levels table since we
;; always have this table (contrary to the BNF), but it has all the
;; disadvantages of the prec2 case plus the disadvantage that the levels
;; table has lost some info which would result in extra invalid pairs.
"Build a closer-alist from a BNF table.
The return value is in the same form as `smie-closer-alist'.
NO-INNERS if non-nil means that inner keywords will be excluded
from the table, e.g. the table will not include things like (\"if\" . \"else\")."
(let ((nts (mapcar #'car bnf)) ;non terminals.
(alist '()))
(dolist (nt bnf)
(dolist (rhs (cdr nt))
(unless (or (< (length rhs) 2) (member (car rhs) nts))
(if no-inners
(let ((last (car (last rhs))))
(unless (member last nts)
(cl-pushnew (cons (car rhs) last) alist :test #'equal)))
;; Reverse so that the "real" closer gets there first,
;; which is important for smie-close-block.
(dolist (term (reverse (cdr rhs)))
(unless (member term nts)
(cl-pushnew (cons (car rhs) term) alist :test #'equal)))))))
(nreverse alist)))