diff options
| author | Stefan Monnier | 2010-05-18 12:03:51 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2010-05-18 12:03:51 -0400 |
| commit | 472e7ec1e16f2f487e0e788f77fc9f3009b204b4 (patch) | |
| tree | 1198f0a3dc703e40369284da1098a79a84859afd | |
| parent | 1fc0ce04bc651fe8adbe822515e4ea7a4e904249 (diff) | |
| download | emacs-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/ChangeLog | 11 | ||||
| -rw-r--r-- | lisp/emacs-lisp/smie.el | 200 |
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 @@ | |||
| 1 | 2010-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 | |||
| 1 | 2010-05-18 Juanma Barranquero <lekktu@gmail.com> | 12 | 2010-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 | |||
| 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) |
| @@ -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. |
| 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. | ||
| 297 | HALFSEXP if non-nil, means skip over a partial sexp if needed. I.e. if the | 333 | 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. | 334 | first token we see is an operator, skip over its left-hand-side argument. |
| 299 | Possible return values: | 335 | Possible 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)) | 395 | HALFSEXP if non-nil, means skip over a partial sexp if needed. I.e. if the |
| 344 | (throw 'return | 396 | first token we see is an operator, skip over its left-hand-side argument. |
| 345 | (prog1 (list (or (car toklevels) t) (point) token) | 397 | Possible 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." |