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)))))