changeset 111622:df422e3ae879

* emacs-lisp/smie.el (smie-bnf-classify): Signal errors for tokens that are both openers (resp. closers) and something else. (smie-grammar): Loosen definition of valid values. (smie-next-sexp, smie-down-list, smie-blink-matching-open) (smie-indent--parent, smie-rule-parent, smie-indent-keyword) (smie-indent-after-keyword): Adjust users. (smie-indent-keyword): Don't indent empty lines.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Wed, 17 Nov 2010 14:59:16 -0500
parents 753ce65aa0fc
children 0f82907d5a26
files lisp/ChangeLog lisp/emacs-lisp/smie.el
diffstat 2 files changed, 63 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Wed Nov 17 09:34:28 2010 -0500
+++ b/lisp/ChangeLog	Wed Nov 17 14:59:16 2010 -0500
@@ -1,5 +1,13 @@
 2010-11-17  Stefan Monnier  <monnier@iro.umontreal.ca>
 
+	* emacs-lisp/smie.el (smie-bnf-classify): Signal errors for tokens
+	that are both openers (resp. closers) and something else.
+	(smie-grammar): Loosen definition of valid values.
+	(smie-next-sexp, smie-down-list, smie-blink-matching-open)
+	(smie-indent--parent, smie-rule-parent, smie-indent-keyword)
+	(smie-indent-after-keyword): Adjust users.
+	(smie-indent-keyword): Don't indent empty lines.
+
 	* vc-hg.el (vc-hg-program): New var.
 	Suggested by Norman Gray <norman@astro.gla.ac.uk>.
 	(vc-hg-state, vc-hg-working-revision, vc-hg-command): Use it.
--- a/lisp/emacs-lisp/smie.el	Wed Nov 17 09:34:28 2010 -0500
+++ b/lisp/emacs-lisp/smie.el	Wed Nov 17 14:59:16 2010 -0500
@@ -353,6 +353,7 @@
   "Return a table classifying terminals.
 Each terminal can either be an `opener', a `closer', or neither."
   (let ((table (make-hash-table :test #'equal))
+        (nts (mapcar #'car bnf))
         (alist '()))
     (dolist (category bnf)
       (puthash (car category) 'neither table) ;Remove non-terminals.
@@ -362,14 +363,22 @@
           (let ((first (pop rhs)))
             (puthash first
                      (if (memq (gethash first table) '(nil opener))
-                         'opener 'neither)
+                         'opener
+                       (unless (member first nts)
+                         (error "SMIE: token %s is both opener and non-opener"
+                                first))
+                       'neither)
                      table))
           (while (cdr rhs)
             (puthash (pop rhs) 'neither table)) ;Remove internals.
           (let ((last (pop rhs)))
             (puthash last
                      (if (memq (gethash last table) '(nil closer))
-                         'closer 'neither)
+                         'closer
+                       (unless (member last nts)
+                         (error "SMIE: token %s is both closer and non-closer"
+                                last))
+                       'neither)
                      table)))))
     (maphash (lambda (tok v)
                (when (memq v '(closer opener))
@@ -544,9 +553,9 @@
 This list is normally built by `smie-prec2->grammar'.
 Each element is of the form (TOKEN LEFT-LEVEL RIGHT-LEVEL).
 Parsing is done using an operator precedence parser.
-LEFT-LEVEL and RIGHT-LEVEL can be either numbers or nil, where nil
+LEFT-LEVEL and RIGHT-LEVEL can be either numbers or a list, where a list
 means that this operator does not bind on the corresponding side,
-i.e. a LEFT-LEVEL of nil means this is a token that behaves somewhat like
+e.g. a LEFT-LEVEL of nil means this is a token that behaves somewhat like
 an open-paren, whereas a RIGHT-LEVEL of nil would correspond to something
 like a close-paren.")
 
@@ -630,9 +639,10 @@
                 (if (eq pos (point))
                     ;; We did not move, so let's abort the loop.
                     (throw 'return (list t (point))))))
-             ((null (funcall op-back toklevels))
+             ((not (numberp (funcall op-back toklevels)))
               ;; A token like a paren-close.
-              (assert (funcall op-forw toklevels)) ;Otherwise, why mention it?
+              (assert (numberp     ; Otherwise, why mention it in smie-grammar.
+                       (funcall op-forw toklevels)))
               (push toklevels levels))
              (t
               (while (and levels (< (funcall op-back toklevels)
@@ -640,7 +650,7 @@
                 (setq levels (cdr levels)))
               (cond
                ((null levels)
-                (if (and halfsexp (funcall op-forw toklevels))
+                (if (and halfsexp (numberp (funcall op-forw toklevels)))
                     (push toklevels levels)
                   (throw 'return
                          (prog1 (list (or (car toklevels) t) (point) token)
@@ -656,11 +666,11 @@
                    ;; Keep looking as long as we haven't matched the
                    ;; topmost operator.
                    (levels
-                    (if (funcall op-forw toklevels)
+                    (if (numberp (funcall op-forw toklevels))
                         (push toklevels levels)))
                    ;; We matched the topmost operator.  If the new operator
                    ;; is the last in the corresponding BNF rule, we're done.
-                   ((null (funcall op-forw toklevels))
+                   ((not (numberp (funcall op-forw toklevels)))
                     ;; It is the last element, let's stop here.
                     (throw 'return (list nil (point) token)))
                    ;; If the new operator is not the last in the BNF rule,
@@ -765,7 +775,7 @@
                 ;; intervention, e.g. for Octave's use of `until'
                 ;; as a pseudo-closer of `do'.
                 (closer)
-                ((or (equal levels '(nil)) (nth 1 (car levels)))
+                ((or (equal levels '(nil)) (numberp (nth 1 (car levels))))
                  (error "Doesn't look like a block"))
                 (t
                  ;; Now that smie-setup automatically sets smie-closer-alist
@@ -776,7 +786,7 @@
                        (when (and (eq (nth 2 level) (nth 1 other))
                                   (not (memq other seen)))
                          (push other seen)
-                         (if (nth 2 other)
+                         (if (numberp (nth 2 other))
                              (push other levels)
                            (push (car other) found))))))
                  (cond
@@ -817,8 +827,8 @@
                   (progn (goto-char start) (down-list inc) nil)
                 (forward-sexp inc)
                 (/= (point) pos)))
-             ((and levels (null (nth (+ 1 offset) levels))) nil)
-             ((and levels (null (nth (- 2 offset) levels)))
+             ((and levels (not (numberp (nth (+ 1 offset) levels)))) nil)
+             ((and levels (not (numberp (nth (- 2 offset) levels))))
               (let ((end (point)))
                 (goto-char start)
                 (signal 'scan-error
@@ -903,7 +913,7 @@
                          (not (memq (char-before)
                                     smie-blink-matching-triggers)))
                      (or smie-blink-matching-inners
-                         (null (nth 2 (assoc token smie-grammar)))))
+                         (not (numberp (nth 2 (assoc token smie-grammar))))))
             ;; The major mode might set blink-matching-check-function
             ;; buffer-locally so that interactive calls to
             ;; blink-matching-open work right, but let's not presume
@@ -979,7 +989,7 @@
       (save-excursion
         (let* ((pos (point))
                (tok (funcall smie-forward-token-function)))
-          (unless (cadr (assoc tok smie-grammar))
+          (unless (numberp (cadr (assoc tok smie-grammar)))
             (goto-char pos))
           (setq smie--parent
                 (smie-backward-sexp 'halfsexp))))))
@@ -1026,7 +1036,7 @@
              ;; rules-function, so it gives it a chance to tweak
              ;; indentation (e.g. by forcing indentation relative to
              ;; its own parent, as in fn a => fn b => fn c =>).
-             (if (or (null (car smie--parent)) (smie-indent--hanging-p))
+             (if (or (listp (car smie--parent)) (smie-indent--hanging-p))
                  (smie-indent-virtual) (current-column))))))
 
 (defvar smie-rule-separator-outdent 2)
@@ -1222,26 +1232,30 @@
     (let* ((pos (point))
            (toklevels (smie-indent-forward-token))
            (token (pop toklevels)))
-      (if (null (car toklevels))
-          (save-excursion
-            (goto-char pos)
-            ;; Different cases:
-            ;; - smie-indent--bolp: "indent according to others".
-            ;; - common hanging: "indent according to others".
-            ;; - SML-let hanging: "indent like parent".
-            ;; - if-after-else: "indent-like parent".
-            ;; - middle-of-line: "trust current position".
-            (cond
-             ((null (cdr toklevels)) nil) ;Not a keyword.
-             ((smie-indent--rule :before token))
-             ((smie-indent--bolp)       ;I.e. non-virtual indent.
-              ;; For an open-paren-like thingy at BOL, always indent only
-              ;; based on other rules (typically smie-indent-after-keyword).
-              nil)
-             (t
-              ;; By default use point unless we're hanging.
-              (unless (smie-indent--hanging-p) (current-column)))))
-
+      (cond
+       ((< pos (line-beginning-position))
+        ;; The token we just read is actually not on the line where we started.
+        nil)
+       ((not (numberp (car toklevels)))
+        (save-excursion
+          (goto-char pos)
+          ;; Different cases:
+          ;; - smie-indent--bolp: "indent according to others".
+          ;; - common hanging: "indent according to others".
+          ;; - SML-let hanging: "indent like parent".
+          ;; - if-after-else: "indent-like parent".
+          ;; - middle-of-line: "trust current position".
+          (cond
+           ((null (cdr toklevels)) nil) ;Not a keyword.
+           ((smie-indent--rule :before token))
+           ((smie-indent--bolp)         ;I.e. non-virtual indent.
+            ;; For an open-paren-like thingy at BOL, always indent only
+            ;; based on other rules (typically smie-indent-after-keyword).
+            nil)
+           (t
+            ;; By default use point unless we're hanging.
+            (unless (smie-indent--hanging-p) (current-column))))))
+       (t
         ;; FIXME: This still looks too much like black magic!!
         (let* ((parent (smie-backward-sexp 'halfsexp)))
           ;; Different behaviors:
@@ -1321,7 +1335,7 @@
               ;; So we use a heuristic here, which is that we only use virtual
               ;; if the parent is tightly linked to the child token (they're
               ;; part of the same BNF rule).
-              (if (car parent) (current-column) (smie-indent-virtual))))))))))
+              (if (car parent) (current-column) (smie-indent-virtual)))))))))))
 
 (defun smie-indent-comment ()
   "Compute indentation of a comment."
@@ -1389,11 +1403,11 @@
        ;; The default indentation after a keyword/operator is
        ;; 0 for infix, t for prefix, and use another rule
        ;; for postfix.
-       ((null (nth 2 toklevel)) nil)        ;A closer.
-       ((or (null (nth 1 toklevel))         ;An opener.
-            (rassoc tok smie-closer-alist)) ;An inner.
+       ((not (numberp (nth 2 toklevel))) nil)                   ;A closer.
+       ((or (not (numberp (nth 1 toklevel)))                    ;An opener.
+            (rassoc tok smie-closer-alist))                     ;An inner.
         (+ (smie-indent-virtual) (smie-indent--offset 'basic))) ;
-       (t (smie-indent-virtual))))))    ;An infix.
+       (t (smie-indent-virtual))))))                            ;An infix.
 
 (defun smie-indent-exps ()
   ;; Indentation of sequences of simple expressions without