aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2010-05-18 12:03:51 -0400
committerStefan Monnier2010-05-18 12:03:51 -0400
commit472e7ec1e16f2f487e0e788f77fc9f3009b204b4 (patch)
tree1198f0a3dc703e40369284da1098a79a84859afd
parent1fc0ce04bc651fe8adbe822515e4ea7a4e904249 (diff)
downloademacs-472e7ec1e16f2f487e0e788f77fc9f3009b204b4.tar.gz
emacs-472e7ec1e16f2f487e0e788f77fc9f3009b204b4.zip
Fix handling of non-associative equal levels.
* emacs-lisp/smie.el (smie-prec2-levels): Choose distinct levels even when it's not needed. (smie-op-left, smie-op-right): New functions. (smie-next-sexp): New function, extracted from smie-backward-sexp. Better handle equal levels to distinguish the associative case from the "multi-keyword construct" case. (smie-backward-sexp, smie-forward-sexp): Use it.
-rw-r--r--lisp/ChangeLog11
-rw-r--r--lisp/emacs-lisp/smie.el200
2 files changed, 120 insertions, 91 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 3cf8b43a796..91265a15bbf 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,14 @@
12010-05-18 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 Fix handling of non-associative equal levels.
4 * emacs-lisp/smie.el (smie-prec2-levels): Choose distinct levels even
5 when it's not needed.
6 (smie-op-left, smie-op-right): New functions.
7 (smie-next-sexp): New function, extracted from smie-backward-sexp.
8 Better handle equal levels to distinguish the associative case from
9 the "multi-keyword construct" case.
10 (smie-backward-sexp, smie-forward-sexp): Use it.
11
12010-05-18 Juanma Barranquero <lekktu@gmail.com> 122010-05-18 Juanma Barranquero <lekktu@gmail.com>
2 13
3 * progmodes/prolog.el (smie-indent-basic): Declare for byte-compiler. 14 * progmodes/prolog.el (smie-indent-basic): Declare for byte-compiler.
diff --git a/lisp/emacs-lisp/smie.el b/lisp/emacs-lisp/smie.el
index 27ddeb762af..0e7b0dc19ca 100644
--- a/lisp/emacs-lisp/smie.el
+++ b/lisp/emacs-lisp/smie.el
@@ -252,11 +252,23 @@ PREC2 is a table as returned by `smie-precs-precedence-table' or
252 (dolist (cst csts) 252 (dolist (cst csts)
253 (unless (memq (car cst) rhvs) 253 (unless (memq (car cst) rhvs)
254 (setq progress t) 254 (setq progress t)
255 ;; We could give each var in a given iteration the same value,
256 ;; but we can also give them arbitrarily different values.
257 ;; Basically, these are vars between which there is no
258 ;; constraint (neither equality nor inequality), so
259 ;; anything will do.
260 ;; We give them arbitrary values, which means that we
261 ;; replace the "no constraint" case with either > or <
262 ;; but not =. The reason we do that is so as to try and
263 ;; distinguish associative operators (which will have
264 ;; left = right).
265 (unless (caar cst)
255 (setcar (car cst) i) 266 (setcar (car cst) i)
267 (incf i))
256 (setq csts (delq cst csts)))) 268 (setq csts (delq cst csts))))
257 (unless progress 269 (unless progress
258 (error "Can't resolve the precedence table to precedence levels"))) 270 (error "Can't resolve the precedence table to precedence levels")))
259 (incf i)) 271 (incf i 10))
260 ;; Propagate equalities back to their source. 272 ;; Propagate equalities back to their source.
261 (dolist (eq (nreverse eqs)) 273 (dolist (eq (nreverse eqs))
262 (assert (null (caar eq))) 274 (assert (null (caar eq)))
@@ -278,6 +290,9 @@ PREC2 is a table as returned by `smie-precs-precedence-table' or
278Each element is of the form (TOKEN LEFT-LEVEL RIGHT-LEVEL). 290Each element is of the form (TOKEN LEFT-LEVEL RIGHT-LEVEL).
279Parsing is done using an operator precedence parser.") 291Parsing is done using an operator precedence parser.")
280 292
293(defalias 'smie-op-left 'car)
294(defalias 'smie-op-right 'cadr)
295
281(defun smie-backward-token () 296(defun smie-backward-token ()
282 ;; FIXME: This may be an OK default but probably needs a hook. 297 ;; FIXME: This may be an OK default but probably needs a hook.
283 (buffer-substring (point) 298 (buffer-substring (point)
@@ -292,64 +307,107 @@ Parsing is done using an operator precedence parser.")
292 (skip-syntax-forward "w_'")) 307 (skip-syntax-forward "w_'"))
293 (point)))) 308 (point))))
294 309
295(defun smie-backward-sexp (&optional halfsexp) 310(defun smie-associative-p (toklevels)
311 ;; in "a + b + c" we want to stop at each +, but in
312 ;; "if a then b else c" we don't want to stop at each keyword.
313 ;; To distinguish the two cases, we made smie-prec2-levels choose
314 ;; different levels for each part of "if a then b else c", so that
315 ;; by checking if the left-level is equal to the right level, we can
316 ;; figure out that it's an associative operator.
317 ;; This is not 100% foolproof, tho, since a grammar like
318 ;; (exp ("A" exp "C") ("A" exp "B" exp "C"))
319 ;; will cause "B" to have equal left and right levels, even though
320 ;; it is not an associative operator.
321 ;; A better check would be the check the actual previous operator
322 ;; against this one to see if it's the same, but we'd have to change
323 ;; `levels' to keep a stack of operators rather than only levels.
324 (eq (smie-op-left toklevels) (smie-op-right toklevels)))
325
326(defun smie-next-sexp (next-token next-sexp op-forw op-back halfsexp)
296 "Skip over one sexp. 327 "Skip over one sexp.
328NEXT-TOKEN is a function of no argument that moves forward by one
329token (after skipping comments if needed) and returns it.
330NEXT-SEXP is a lower-level function to skip one sexp.
331OP-FORW is the accessor to the forward level of the level data.
332OP-BACK is the accessor to the backward level of the level data.
297HALFSEXP if non-nil, means skip over a partial sexp if needed. I.e. if the 333HALFSEXP if non-nil, means skip over a partial sexp if needed. I.e. if the
298first token we see is an operator, skip over its left-hand-side argument. 334first token we see is an operator, skip over its left-hand-side argument.
299Possible return values: 335Possible return values:
300 (LEFT-LEVEL POS TOKEN): we couldn't skip TOKEN because its right-level 336 (FORW-LEVEL POS TOKEN): we couldn't skip TOKEN because its back-level
301 is too high. LEFT-LEVEL is the left-level of TOKEN, 337 is too high. FORW-LEVEL is the forw-level of TOKEN,
302 POS is its start position in the buffer. 338 POS is its start position in the buffer.
303 (t POS TOKEN): same thing but for an open-paren or the beginning of buffer. 339 (t POS TOKEN): same thing when we bump on the wrong side of a paren.
304 (nil POS TOKEN): we skipped over a paren-like pair. 340 (nil POS TOKEN): we skipped over a paren-like pair.
305 nil: we skipped over an identifier, matched parentheses, ..." 341 nil: we skipped over an identifier, matched parentheses, ..."
306 (if (bobp) (list t (point)) 342 (catch 'return
307 (catch 'return 343 (let ((levels ()))
308 (let ((levels ())) 344 (while
309 (while 345 (let* ((pos (point))
310 (let* ((pos (point)) 346 (token (funcall next-token))
311 (token (progn (forward-comment (- (point-max))) 347 (toklevels (cdr (assoc token smie-op-levels))))
312 (smie-backward-token))) 348
313 (toklevels (cdr (assoc token smie-op-levels)))) 349 (cond
314 350 ((null toklevels)
351 (if (equal token "")
352 (condition-case err
353 (progn (goto-char pos) (funcall next-sexp 1) nil)
354 (scan-error (throw 'return (list t (caddr err)))))))
355 ((null (funcall op-back toklevels))
356 ;; A token like a paren-close.
357 (assert (funcall op-forw toklevels)) ;Otherwise, why mention it?
358 (push (funcall op-forw toklevels) levels))
359 (t
360 (while (and levels (< (funcall op-back toklevels) (car levels)))
361 (setq levels (cdr levels)))
315 (cond 362 (cond
316 ((null toklevels) 363 ((null levels)
317 (if (equal token "") 364 (if (and halfsexp (funcall op-forw toklevels))
318 (condition-case err 365 (push (funcall op-forw toklevels) levels)
319 (progn (goto-char pos) (backward-sexp 1) nil) 366 (throw 'return
320 (scan-error (throw 'return (list t (caddr err))))))) 367 (prog1 (list (or (car toklevels) t) (point) token)
321 ((null (nth 1 toklevels)) 368 (goto-char pos)))))
322 ;; A token like a paren-close.
323 (assert (nth 0 toklevels)) ;Otherwise, why mention it?
324 (push (nth 0 toklevels) levels))
325 (t 369 (t
326 (while (and levels (< (nth 1 toklevels) (car levels))) 370 (if (and levels (= (funcall op-back toklevels) (car levels)))
327 (setq levels (cdr levels))) 371 (setq levels (cdr levels)))
328 (cond 372 (cond
329 ((null levels) 373 ((null levels)
330 (if (and halfsexp (nth 0 toklevels)) 374 (cond
331 (push (nth 0 toklevels) levels) 375 ((null (funcall op-forw toklevels))
376 (throw 'return (list nil (point) token)))
377 ((smie-associative-p toklevels)
332 (throw 'return 378 (throw 'return
333 (prog1 (list (or (car toklevels) t) (point) token) 379 (prog1 (list (or (car toklevels) t) (point) token)
334 (goto-char pos))))) 380 (goto-char pos))))
381 ;; We just found a match to the previously pending operator
382 ;; but this new operator is still part of a larger RHS.
383 ;; E.g. we're now looking at the "then" in
384 ;; "if a then b else c". So we have to keep parsing the
385 ;; rest of the construct.
386 (t (push (funcall op-forw toklevels) levels))))
335 (t 387 (t
336 (while (and levels (= (nth 1 toklevels) (car levels))) 388 (if (funcall op-forw toklevels)
337 (setq levels (cdr levels))) 389 (push (funcall op-forw toklevels) levels))))))))
338 (cond 390 levels)
339 ((null levels) 391 (setq halfsexp nil)))))
340 (cond 392
341 ((null (nth 0 toklevels)) 393(defun smie-backward-sexp (&optional halfsexp)
342 (throw 'return (list nil (point) token))) 394 "Skip over one sexp.
343 ((eq (nth 0 toklevels) (nth 1 toklevels)) 395HALFSEXP if non-nil, means skip over a partial sexp if needed. I.e. if the
344 (throw 'return 396first token we see is an operator, skip over its left-hand-side argument.
345 (prog1 (list (or (car toklevels) t) (point) token) 397Possible return values:
346 (goto-char pos)))) 398 (LEFT-LEVEL POS TOKEN): we couldn't skip TOKEN because its right-level
347 (t (debug)))) ;Not sure yet what to do here. 399 is too high. LEFT-LEVEL is the left-level of TOKEN,
348 (t 400 POS is its start position in the buffer.
349 (if (nth 0 toklevels) 401 (t POS TOKEN): same thing but for an open-paren or the beginning of buffer.
350 (push (nth 0 toklevels) levels)))))))) 402 (nil POS TOKEN): we skipped over a paren-like pair.
351 levels) 403 nil: we skipped over an identifier, matched parentheses, ..."
352 (setq halfsexp nil)))))) 404 (if (bobp) (list t (point))
405 (smie-next-sexp
406 (lambda () (forward-comment (- (point-max))) (smie-backward-token))
407 (indirect-function 'backward-sexp)
408 (indirect-function 'smie-op-left)
409 (indirect-function 'smie-op-right)
410 halfsexp)))
353 411
354;; Mirror image, not used for indentation. 412;; Mirror image, not used for indentation.
355(defun smie-forward-sexp (&optional halfsexp) 413(defun smie-forward-sexp (&optional halfsexp)
@@ -364,52 +422,12 @@ Possible return values:
364 (nil POS TOKEN): we skipped over a paren-like pair. 422 (nil POS TOKEN): we skipped over a paren-like pair.
365 nil: we skipped over an identifier, matched parentheses, ..." 423 nil: we skipped over an identifier, matched parentheses, ..."
366 (if (eobp) (list t (point)) 424 (if (eobp) (list t (point))
367 (catch 'return 425 (smie-next-sexp
368 (let ((levels ())) 426 (lambda () (forward-comment (point-max)) (smie-forward-token))
369 (while 427 (indirect-function 'forward-sexp)
370 (let* ((pos (point)) 428 (indirect-function 'smie-op-right)
371 (token (progn (forward-comment (point-max)) 429 (indirect-function 'smie-op-left)
372 (smie-forward-token))) 430 halfsexp)))
373 (toklevels (cdr (assoc token smie-op-levels))))
374
375 (cond
376 ((null toklevels)
377 (if (equal token "")
378 (condition-case err
379 (progn (goto-char pos) (forward-sexp 1) nil)
380 (scan-error (throw 'return (list t (caddr err)))))))
381 ((null (nth 0 toklevels))
382 ;; A token like a paren-close.
383 (assert (nth 1 toklevels)) ;Otherwise, why mention it?
384 (push (nth 1 toklevels) levels))
385 (t
386 (while (and levels (< (nth 0 toklevels) (car levels)))
387 (setq levels (cdr levels)))
388 (cond
389 ((null levels)
390 (if (and halfsexp (nth 1 toklevels))
391 (push (nth 1 toklevels) levels)
392 (throw 'return
393 (prog1 (list (or (nth 1 toklevels) t) (point) token)
394 (goto-char pos)))))
395 (t
396 (while (and levels (= (nth 0 toklevels) (car levels)))
397 (setq levels (cdr levels)))
398 (cond
399 ((null levels)
400 (cond
401 ((null (nth 1 toklevels))
402 (throw 'return (list nil (point) token)))
403 ((eq (nth 1 toklevels) (nth 0 toklevels))
404 (throw 'return
405 (prog1 (list (or (nth 1 toklevels) t) (point) token)
406 (goto-char pos))))
407 (t (debug)))) ;Not sure yet what to do here.
408 (t
409 (if (nth 1 toklevels)
410 (push (nth 1 toklevels) levels))))))))
411 levels)
412 (setq halfsexp nil))))))
413 431
414(defun smie-backward-sexp-command (&optional n) 432(defun smie-backward-sexp-command (&optional n)
415 "Move backward through N logical elements." 433 "Move backward through N logical elements."