Mercurial > emacs
comparison lisp/emacs-lisp/smie.el @ 110802:e544f6cc2447
SMIE: Reliably distinguish openers/closers in smie-prec2-levels
* lisp/emacs-lisp/smie.el (smie-bnf-classify): New function.
(smie-bnf-precedence-table): Use it to remember the closers/openers.
(smie-merge-prec2s): Handle those new entries.
(smie-prec2-levels): Only set precedence to nil for actual
openers/closers.
* lisp/progmodes/octave-mod.el (octave-smie-op-levels): Remove dummy entry
that is now unnecessary.
author | Stefan Monnier <monnier@iro.umontreal.ca> |
---|---|
date | Thu, 07 Oct 2010 13:27:19 +0200 |
parents | 27917ce150c1 |
children | a9904c1962db |
comparison
equal
deleted
inserted
replaced
110801:37b955157790 | 110802:e544f6cc2447 |
---|---|
136 (if (null (cdr tables)) | 136 (if (null (cdr tables)) |
137 (car tables) | 137 (car tables) |
138 (let ((prec2 (make-hash-table :test 'equal))) | 138 (let ((prec2 (make-hash-table :test 'equal))) |
139 (dolist (table tables) | 139 (dolist (table tables) |
140 (maphash (lambda (k v) | 140 (maphash (lambda (k v) |
141 (smie-set-prec2tab prec2 (car k) (cdr k) v)) | 141 (if (consp k) |
142 (smie-set-prec2tab prec2 (car k) (cdr k) v) | |
143 (if (and (gethash k prec2) | |
144 (not (equal (gethash k prec2) v))) | |
145 (error "Conflicting values for %s property" k) | |
146 (puthash k v prec2)))) | |
142 table)) | 147 table)) |
143 prec2))) | 148 prec2))) |
144 | 149 |
145 (defun smie-bnf-precedence-table (bnf &rest precs) | 150 (defun smie-bnf-precedence-table (bnf &rest precs) |
146 (let ((nts (mapcar 'car bnf)) ;Non-terminals | 151 (let ((nts (mapcar 'car bnf)) ;Non-terminals |
223 (if (and (cddr rhs) (not (member (car (cddr rhs)) nts))) | 228 (if (and (cddr rhs) (not (member (car (cddr rhs)) nts))) |
224 (smie-set-prec2tab prec2 (car rhs) (car (cddr rhs)) | 229 (smie-set-prec2tab prec2 (car rhs) (car (cddr rhs)) |
225 '= override))) | 230 '= override))) |
226 (t (smie-set-prec2tab prec2 (car rhs) (cadr rhs) '= override))) | 231 (t (smie-set-prec2tab prec2 (car rhs) (cadr rhs) '= override))) |
227 (setq rhs (cdr rhs))))) | 232 (setq rhs (cdr rhs))))) |
233 ;; Keep track of which tokens are openers/closer, so they can get a nil | |
234 ;; precedence in smie-prec2-levels. | |
235 (puthash :smie-open/close-alist (smie-bnf-classify bnf) prec2) | |
228 prec2)) | 236 prec2)) |
229 | 237 |
230 ;; (defun smie-prec2-closer-alist (prec2 include-inners) | 238 ;; (defun smie-prec2-closer-alist (prec2 include-inners) |
231 ;; "Build a closer-alist from a PREC2 table. | 239 ;; "Build a closer-alist from a PREC2 table. |
232 ;; The return value is in the same form as `smie-closer-alist'. | 240 ;; The return value is in the same form as `smie-closer-alist'. |
305 (dolist (term (reverse (cdr rhs))) | 313 (dolist (term (reverse (cdr rhs))) |
306 (unless (member term nts) | 314 (unless (member term nts) |
307 (pushnew (cons (car rhs) term) alist :test #'equal))))))) | 315 (pushnew (cons (car rhs) term) alist :test #'equal))))))) |
308 (nreverse alist))) | 316 (nreverse alist))) |
309 | 317 |
318 (defun smie-bnf-classify (bnf) | |
319 "Return a table classifying terminals. | |
320 Each terminal can either be an `opener', a `closer', or neither." | |
321 (let ((table (make-hash-table :test #'equal)) | |
322 (alist '())) | |
323 (dolist (category bnf) | |
324 (puthash (car category) 'neither table) ;Remove non-terminals. | |
325 (dolist (rhs (cdr category)) | |
326 (if (null (cdr rhs)) | |
327 (puthash (pop rhs) 'neither table) | |
328 (let ((first (pop rhs))) | |
329 (puthash first | |
330 (if (memq (gethash first table) '(nil opener)) | |
331 'opener 'neither) | |
332 table)) | |
333 (while (cdr rhs) | |
334 (puthash (pop rhs) 'neither table)) ;Remove internals. | |
335 (let ((last (pop rhs))) | |
336 (puthash last | |
337 (if (memq (gethash last table) '(nil closer)) | |
338 'closer 'neither) | |
339 table))))) | |
340 (maphash (lambda (tok v) | |
341 (when (memq v '(closer opener)) | |
342 (push (cons tok v) alist))) | |
343 table) | |
344 alist)) | |
310 | 345 |
311 (defun smie-debug--prec2-cycle (csts) | 346 (defun smie-debug--prec2-cycle (csts) |
312 "Return a cycle in CSTS, assuming there's one. | 347 "Return a cycle in CSTS, assuming there's one. |
313 CSTS is a list of pairs representing arcs in a graph." | 348 CSTS is a list of pairs representing arcs in a graph." |
314 ;; A PATH is of the form (START . REST) where REST is a reverse | 349 ;; A PATH is of the form (START . REST) where REST is a reverse |
343 " < "))) | 378 " < "))) |
344 | 379 |
345 (defun smie-prec2-levels (prec2) | 380 (defun smie-prec2-levels (prec2) |
346 ;; FIXME: Rather than only return an alist of precedence levels, we should | 381 ;; FIXME: Rather than only return an alist of precedence levels, we should |
347 ;; also extract other useful data from it: | 382 ;; also extract other useful data from it: |
348 ;; - matching sets of block openers&closers (which can otherwise become | |
349 ;; collapsed into a single equivalence class in smie-op-levels) for | |
350 ;; smie-close-block as well as to detect mismatches in smie-next-sexp | |
351 ;; or in blink-paren (as well as to do the blink-paren for inner | |
352 ;; keywords like the "in" of "let..in..end"). | |
353 ;; - better default indentation rules (i.e. non-zero indentation after inner | 383 ;; - better default indentation rules (i.e. non-zero indentation after inner |
354 ;; keywords like the "in" of "let..in..end") for smie-indent-after-keyword. | 384 ;; keywords like the "in" of "let..in..end") for smie-indent-after-keyword. |
355 ;; Of course, maybe those things would be even better handled in the | 385 ;; Of course, maybe those things would be even better handled in the |
356 ;; bnf->prec function. | 386 ;; bnf->prec function. |
357 "Take a 2D precedence table and turn it into an alist of precedence levels. | 387 "Take a 2D precedence table and turn it into an alist of precedence levels. |
367 tmp x y) | 397 tmp x y) |
368 ;; From `prec2' we construct a list of constraints between | 398 ;; From `prec2' we construct a list of constraints between |
369 ;; variables (aka "precedence levels"). These can be either | 399 ;; variables (aka "precedence levels"). These can be either |
370 ;; equality constraints (in `eqs') or `<' constraints (in `csts'). | 400 ;; equality constraints (in `eqs') or `<' constraints (in `csts'). |
371 (maphash (lambda (k v) | 401 (maphash (lambda (k v) |
372 (if (setq tmp (assoc (car k) table)) | 402 (when (consp k) |
373 (setq x (cddr tmp)) | 403 (if (setq tmp (assoc (car k) table)) |
374 (setq x (cons nil nil)) | 404 (setq x (cddr tmp)) |
375 (push (cons (car k) (cons nil x)) table)) | 405 (setq x (cons nil nil)) |
376 (if (setq tmp (assoc (cdr k) table)) | 406 (push (cons (car k) (cons nil x)) table)) |
377 (setq y (cdr tmp)) | 407 (if (setq tmp (assoc (cdr k) table)) |
378 (setq y (cons nil (cons nil nil))) | 408 (setq y (cdr tmp)) |
379 (push (cons (cdr k) y) table)) | 409 (setq y (cons nil (cons nil nil))) |
380 (ecase v | 410 (push (cons (cdr k) y) table)) |
381 (= (push (cons x y) eqs)) | 411 (ecase v |
382 (< (push (cons x y) csts)) | 412 (= (push (cons x y) eqs)) |
383 (> (push (cons y x) csts)))) | 413 (< (push (cons x y) csts)) |
414 (> (push (cons y x) csts))))) | |
384 prec2) | 415 prec2) |
385 ;; First process the equality constraints. | 416 ;; First process the equality constraints. |
386 (let ((eqs eqs)) | 417 (let ((eqs eqs)) |
387 (while eqs | 418 (while eqs |
388 (let ((from (caar eqs)) | 419 (let ((from (caar eqs)) |
430 (dolist (eq (nreverse eqs)) | 461 (dolist (eq (nreverse eqs)) |
431 (assert (or (null (caar eq)) (eq (car eq) (cdr eq)))) | 462 (assert (or (null (caar eq)) (eq (car eq) (cdr eq)))) |
432 (setcar (car eq) (cadr eq))) | 463 (setcar (car eq) (cadr eq))) |
433 ;; Finally, fill in the remaining vars (which only appeared on the | 464 ;; Finally, fill in the remaining vars (which only appeared on the |
434 ;; right side of the < constraints). | 465 ;; right side of the < constraints). |
435 (dolist (x table) | 466 (let ((classification-table (gethash :smie-open/close-alist prec2))) |
436 ;; When both sides are nil, it means this operator binds very | 467 (dolist (x table) |
437 ;; very tight, but it's still just an operator, so we give it | 468 ;; When both sides are nil, it means this operator binds very |
438 ;; the highest precedence. | 469 ;; very tight, but it's still just an operator, so we give it |
439 ;; OTOH if only one side is nil, it usually means it's like an | 470 ;; the highest precedence. |
440 ;; open-paren, which is very important for indentation purposes, | 471 ;; OTOH if only one side is nil, it usually means it's like an |
441 ;; so we keep it nil, to make it easier to recognize. | 472 ;; open-paren, which is very important for indentation purposes, |
442 (unless (or (nth 1 x) (nth 2 x)) | 473 ;; so we keep it nil if so, to make it easier to recognize. |
443 (setf (nth 1 x) i) | 474 (unless (or (nth 1 x) |
444 (setf (nth 2 x) i)))) | 475 (eq 'opener (cdr (assoc (car x) classification-table)))) |
476 (setf (nth 1 x) i) | |
477 (incf i)) ;See other (incf i) above. | |
478 (unless (or (nth 2 x) | |
479 (eq 'closer (cdr (assoc (car x) classification-table)))) | |
480 (setf (nth 2 x) i) | |
481 (incf i))))) ;See other (incf i) above. | |
445 table)) | 482 table)) |
446 | 483 |
447 ;;; Parsing using a precedence level table. | 484 ;;; Parsing using a precedence level table. |
448 | 485 |
449 (defvar smie-op-levels 'unset | 486 (defvar smie-op-levels 'unset |