Function: smie-next-sexp

smie-next-sexp is a byte-compiled function defined in smie.el.gz.

Signature

(smie-next-sexp NEXT-TOKEN NEXT-SEXP OP-FORW OP-BACK HALFSEXP)

Documentation

Skip over one sexp.

NEXT-TOKEN is a function of no argument that moves forward by one token (after skipping comments if needed) and returns it. NEXT-SEXP is a lower-level function to skip one sexp. OP-FORW is the accessor to the forward level of the level data. OP-BACK is the accessor to the backward level of the level data. HALFSEXP if non-nil, means skip over a partial sexp if needed. I.e. if the first token we see is an operator, skip over its left-hand-side argument. HALFSEXP can also be a token, in which case it means to parse as if we had just successfully passed this token. Possible return values:
  (FORW-LEVEL POS TOKEN): we couldn't skip TOKEN because its back-level
    is too high. FORW-LEVEL is the forw-level of TOKEN,
    POS is its start position in the buffer.
  (t POS TOKEN): same thing when we bump on the wrong side of a paren.
    Instead of t, the car can also be some other non-nil non-number value.
  (nil POS TOKEN): we skipped over a paren-like pair.
  nil: we skipped over an identifier, matched parentheses, ...

Source Code

;; Defined in /usr/src/emacs/lisp/emacs-lisp/smie.el.gz
(defun smie-next-sexp (next-token next-sexp op-forw op-back halfsexp)
  "Skip over one sexp.
NEXT-TOKEN is a function of no argument that moves forward by one
token (after skipping comments if needed) and returns it.
NEXT-SEXP is a lower-level function to skip one sexp.
OP-FORW is the accessor to the forward level of the level data.
OP-BACK is the accessor to the backward level of the level data.
HALFSEXP if non-nil, means skip over a partial sexp if needed.  I.e. if the
first token we see is an operator, skip over its left-hand-side argument.
HALFSEXP can also be a token, in which case it means to parse as if
we had just successfully passed this token.
Possible return values:
  (FORW-LEVEL POS TOKEN): we couldn't skip TOKEN because its back-level
    is too high.  FORW-LEVEL is the forw-level of TOKEN,
    POS is its start position in the buffer.
  (t POS TOKEN): same thing when we bump on the wrong side of a paren.
    Instead of t, the `car' can also be some other non-nil non-number value.
  (nil POS TOKEN): we skipped over a paren-like pair.
  nil: we skipped over an identifier, matched parentheses, ..."
  (catch 'return
    (let ((levels
           (if (stringp halfsexp)
               (prog1 (list (or (cdr (assoc halfsexp smie-grammar))
                                (when (string-match "\\`\\s(\\|\\s)\\(\\)\\'"
                                                    halfsexp)
                                  (if (match-end 1) '(0 nil) '(nil 0)))
                                (error "Unknown token: %S" halfsexp)))
                 (setq halfsexp nil)))))
      (while
          (let* ((pos (point))
                 (token (funcall next-token))
                 (toklevels (cdr (assoc token smie-grammar))))
            (cond
             ((null toklevels)
              (when (zerop (length token))
                (condition-case err
                    (progn (funcall next-sexp 1) nil)
                  (scan-error
                   (let* ((epos1 (nth 2 err))
                          (epos (if (<= (point) epos1) (nth 3 err) epos1)))
                     (goto-char pos)
                     (throw 'return
                            (list t epos
                                  (unless (= (point) epos)
                                    (buffer-substring-no-properties
                                     epos
                                     (+ epos (if (< (point) epos) -1 1)))))))))
                (if (eq pos (point))
                    ;; We did not move, so let's abort the loop.
                    (throw 'return (list t (point))))))
             ((not (numberp (funcall op-back toklevels)))
              ;; A token like a paren-close.
              (cl-assert (numberp  ; Otherwise, why mention it in smie-grammar.
                          (funcall op-forw toklevels)))
              (push toklevels levels))
             (t
              (while (and levels (< (funcall op-back toklevels)
                                    (funcall op-forw (car levels))))
                (setq levels (cdr levels)))
              (cond
               ((null levels)
                (if (and halfsexp (numberp (funcall op-forw toklevels)))
                    (push toklevels levels)
                  (throw 'return
                         (prog1 (list (or (funcall op-forw toklevels) t)
                                      (point) token)
                           (goto-char pos)))))
               (t
                (let ((lastlevels levels))
                  (if (and levels (= (funcall op-back toklevels)
                                     (funcall op-forw (car levels))))
                      (setq levels (cdr levels)))
                  ;; We may have found a match for the previously pending
                  ;; operator.  Is this the end?
                  (cond
                   ;; Keep looking as long as we haven't matched the
                   ;; topmost operator.
                   (levels
                    (cond
                     ((numberp (funcall op-forw toklevels))
                      (push toklevels levels))
                     ;; FIXME: For some languages, we can express the grammar
                     ;; OK, but next-sexp doesn't stop where we'd want it to.
                     ;; E.g. in SML, we'd want to stop right in front of
                     ;; "local" if we're scanning (both forward and backward)
                     ;; from a "val/fun/..." at the same level.
                     ;; Same for Pascal/Modula2's "procedure" w.r.t
                     ;; "type/var/const".
                     ;;
                     ;; ((and (functionp (cadr (funcall op-forw toklevels)))
                     ;;       (funcall (cadr (funcall op-forw toklevels))
                     ;;                levels))
                     ;;  (setq levels nil))
                     ))
                   ;; We matched the topmost operator.  If the new operator
                   ;; is the last in the corresponding BNF rule, we're done.
                   ((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,
                   ;; and is not associative, it's one of the inner operators
                   ;; (like the "in" in "let .. in .. end"), so keep looking.
                   ((not (smie--associative-p toklevels))
                    (push toklevels levels))
                   ;; The new operator is associative.  Two cases:
                   ;; - it's really just an associative operator (like + or ;)
                   ;;   in which case we should have stopped right before.
                   ((and lastlevels
                         (smie--associative-p (car lastlevels)))
                    (throw 'return
                           (prog1 (list (or (funcall op-forw toklevels) t)
                                        (point) token)
                             (goto-char pos))))
                   ;; - it's an associative operator within a larger construct
                   ;;   (e.g. an "elsif"), so we should just ignore it and keep
                   ;;   looking for the closing element.
                   (t (setq levels lastlevels))))))))
            levels)
        (setq halfsexp nil)))))