Mercurial > emacs
comparison lisp/emacs-lisp/smie.el @ 108649:c9e786f54683
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.
author | Stefan Monnier <monnier@iro.umontreal.ca> |
---|---|
date | Tue, 18 May 2010 12:03:51 -0400 |
parents | 66f3c521df14 |
children | 79ce86edba9f |
comparison
equal
deleted
inserted
replaced
108648:6d142dd06f97 | 108649:c9e786f54683 |
---|---|
250 (let ((rhvs (mapcar 'cdr csts)) | 250 (let ((rhvs (mapcar 'cdr csts)) |
251 (progress nil)) | 251 (progress nil)) |
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))) |
263 (setcar (car eq) (cadr eq))) | 275 (setcar (car eq) (cadr eq))) |
264 ;; Finally, fill in the remaining vars (which only appeared on the | 276 ;; Finally, fill in the remaining vars (which only appeared on the |
276 (defvar smie-op-levels 'unset | 288 (defvar smie-op-levels 'unset |
277 "List of token parsing info. | 289 "List of token parsing info. |
278 Each element is of the form (TOKEN LEFT-LEVEL RIGHT-LEVEL). | 290 Each element is of the form (TOKEN LEFT-LEVEL RIGHT-LEVEL). |
279 Parsing is done using an operator precedence parser.") | 291 Parsing 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) |
284 (progn (if (zerop (skip-syntax-backward ".")) | 299 (progn (if (zerop (skip-syntax-backward ".")) |
285 (skip-syntax-backward "w_'")) | 300 (skip-syntax-backward "w_'")) |
289 ;; FIXME: This may be an OK default but probably needs a hook. | 304 ;; FIXME: This may be an OK default but probably needs a hook. |
290 (buffer-substring (point) | 305 (buffer-substring (point) |
291 (progn (if (zerop (skip-syntax-forward ".")) | 306 (progn (if (zerop (skip-syntax-forward ".")) |
292 (skip-syntax-forward "w_'")) | 307 (skip-syntax-forward "w_'")) |
293 (point)))) | 308 (point)))) |
309 | |
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) | |
327 "Skip over one sexp. | |
328 NEXT-TOKEN is a function of no argument that moves forward by one | |
329 token (after skipping comments if needed) and returns it. | |
330 NEXT-SEXP is a lower-level function to skip one sexp. | |
331 OP-FORW is the accessor to the forward level of the level data. | |
332 OP-BACK is the accessor to the backward level of the level data. | |
333 HALFSEXP if non-nil, means skip over a partial sexp if needed. I.e. if the | |
334 first token we see is an operator, skip over its left-hand-side argument. | |
335 Possible return values: | |
336 (FORW-LEVEL POS TOKEN): we couldn't skip TOKEN because its back-level | |
337 is too high. FORW-LEVEL is the forw-level of TOKEN, | |
338 POS is its start position in the buffer. | |
339 (t POS TOKEN): same thing when we bump on the wrong side of a paren. | |
340 (nil POS TOKEN): we skipped over a paren-like pair. | |
341 nil: we skipped over an identifier, matched parentheses, ..." | |
342 (catch 'return | |
343 (let ((levels ())) | |
344 (while | |
345 (let* ((pos (point)) | |
346 (token (funcall next-token)) | |
347 (toklevels (cdr (assoc token smie-op-levels)))) | |
348 | |
349 (cond | |
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))) | |
362 (cond | |
363 ((null levels) | |
364 (if (and halfsexp (funcall op-forw toklevels)) | |
365 (push (funcall op-forw toklevels) levels) | |
366 (throw 'return | |
367 (prog1 (list (or (car toklevels) t) (point) token) | |
368 (goto-char pos))))) | |
369 (t | |
370 (if (and levels (= (funcall op-back toklevels) (car levels))) | |
371 (setq levels (cdr levels))) | |
372 (cond | |
373 ((null levels) | |
374 (cond | |
375 ((null (funcall op-forw toklevels)) | |
376 (throw 'return (list nil (point) token))) | |
377 ((smie-associative-p toklevels) | |
378 (throw 'return | |
379 (prog1 (list (or (car toklevels) t) (point) token) | |
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)))) | |
387 (t | |
388 (if (funcall op-forw toklevels) | |
389 (push (funcall op-forw toklevels) levels)))))))) | |
390 levels) | |
391 (setq halfsexp nil))))) | |
294 | 392 |
295 (defun smie-backward-sexp (&optional halfsexp) | 393 (defun smie-backward-sexp (&optional halfsexp) |
296 "Skip over one sexp. | 394 "Skip over one sexp. |
297 HALFSEXP if non-nil, means skip over a partial sexp if needed. I.e. if the | 395 HALFSEXP if non-nil, means skip over a partial sexp if needed. I.e. if the |
298 first token we see is an operator, skip over its left-hand-side argument. | 396 first token we see is an operator, skip over its left-hand-side argument. |
302 POS is its start position in the buffer. | 400 POS is its start position in the buffer. |
303 (t POS TOKEN): same thing but for an open-paren or the beginning of buffer. | 401 (t POS TOKEN): same thing but for an open-paren or the beginning of buffer. |
304 (nil POS TOKEN): we skipped over a paren-like pair. | 402 (nil POS TOKEN): we skipped over a paren-like pair. |
305 nil: we skipped over an identifier, matched parentheses, ..." | 403 nil: we skipped over an identifier, matched parentheses, ..." |
306 (if (bobp) (list t (point)) | 404 (if (bobp) (list t (point)) |
307 (catch 'return | 405 (smie-next-sexp |
308 (let ((levels ())) | 406 (lambda () (forward-comment (- (point-max))) (smie-backward-token)) |
309 (while | 407 (indirect-function 'backward-sexp) |
310 (let* ((pos (point)) | 408 (indirect-function 'smie-op-left) |
311 (token (progn (forward-comment (- (point-max))) | 409 (indirect-function 'smie-op-right) |
312 (smie-backward-token))) | 410 halfsexp))) |
313 (toklevels (cdr (assoc token smie-op-levels)))) | |
314 | |
315 (cond | |
316 ((null toklevels) | |
317 (if (equal token "") | |
318 (condition-case err | |
319 (progn (goto-char pos) (backward-sexp 1) nil) | |
320 (scan-error (throw 'return (list t (caddr err))))))) | |
321 ((null (nth 1 toklevels)) | |
322 ;; A token like a paren-close. | |
323 (assert (nth 0 toklevels)) ;Otherwise, why mention it? | |
324 (push (nth 0 toklevels) levels)) | |
325 (t | |
326 (while (and levels (< (nth 1 toklevels) (car levels))) | |
327 (setq levels (cdr levels))) | |
328 (cond | |
329 ((null levels) | |
330 (if (and halfsexp (nth 0 toklevels)) | |
331 (push (nth 0 toklevels) levels) | |
332 (throw 'return | |
333 (prog1 (list (or (car toklevels) t) (point) token) | |
334 (goto-char pos))))) | |
335 (t | |
336 (while (and levels (= (nth 1 toklevels) (car levels))) | |
337 (setq levels (cdr levels))) | |
338 (cond | |
339 ((null levels) | |
340 (cond | |
341 ((null (nth 0 toklevels)) | |
342 (throw 'return (list nil (point) token))) | |
343 ((eq (nth 0 toklevels) (nth 1 toklevels)) | |
344 (throw 'return | |
345 (prog1 (list (or (car toklevels) t) (point) token) | |
346 (goto-char pos)))) | |
347 (t (debug)))) ;Not sure yet what to do here. | |
348 (t | |
349 (if (nth 0 toklevels) | |
350 (push (nth 0 toklevels) levels)))))))) | |
351 levels) | |
352 (setq halfsexp nil)))))) | |
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) |
356 "Skip over one sexp. | 414 "Skip over one sexp. |
357 HALFSEXP if non-nil, means skip over a partial sexp if needed. I.e. if the | 415 HALFSEXP if non-nil, means skip over a partial sexp if needed. I.e. if the |
362 POS is its end position in the buffer. | 420 POS is its end position in the buffer. |
363 (t POS TOKEN): same thing but for an open-paren or the beginning of buffer. | 421 (t POS TOKEN): same thing but for an open-paren or the beginning of buffer. |
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." |
416 (interactive "p") | 434 (interactive "p") |
417 (if (< n 0) | 435 (if (< n 0) |