comparison lisp/specifier.el @ 290:c9fe270a4101 r21-0b43

Import from CVS: tag r21-0b43
author cvs
date Mon, 13 Aug 2007 10:36:47 +0200
parents e11d67e05968
children 70ad99077275
comparison
equal deleted inserted replaced
289:6e6992ccc4b6 290:c9fe270a4101
401 specifier 401 specifier
402 (canonicalize-spec-list nval (specifier-type specifier)) 402 (canonicalize-spec-list nval (specifier-type specifier))
403 how-to-add)))) 403 how-to-add))))
404 value) 404 value)
405 405
406 ;; Note: you cannot replace the following macro with `letf' because
407 ;; `specifier-instance' does not have a setf method defined. (I tried
408 ;; to use `set-specifier' as the setf method for `specifier-instance',
409 ;; but it doesn't work for `letf' because set-specifier to the old
410 ;; value cannot be used to "undo" a previous set-specifier, as letf
411 ;; expects.)
412 ;;
413 ;; This macro might perhaps be made simpler, with an addition to
414 ;; `remove-specifier'. Example (simplified for clarity):
415 ;;
416 ;; (defmacro let-specifier (specifier value domain &rest body)
417 ;; `(unwind-protect
418 ;; (progn
419 ;; (add-spec-to-specifier ,specifier ,value ,domain nil 'prepend)
420 ;; ,@body)
421 ;; (remove-specifier ,specifier ,domain)))
422 ;;
423 ;; So, the idea is to unconditionally prepend a specification for
424 ;; DOMAIN, and unconditionally remove it. This does not work because
425 ;; `remove-specifier' removes *all* the specifications of DOMAIN,
426 ;; nuking the old ones, in the process. (for this purpose, it might
427 ;; make sense for `remove-specifier' to have a HOW-TO-REMOVE
428 ;; argument.)
429 ;;
430 ;; The following version remembers the old speclist and returns it
431 ;; later. It's probably less error-prone anyway.
432
433 (defmacro let-specifier (specifier-list &rest body) 406 (defmacro let-specifier (specifier-list &rest body)
434 "(let-specifier SPECIFIER-LIST BODY): bind specifiers and evaluate BODY. 407 "Add specifier specs, evaluate forms in BODY and restore the specifiers.
408 \(let-specifier SPECIFIER-LIST BODY...)
409
410 Each element of SPECIFIER-LIST should look like this:
411 \(SPECIFIER VALUE &optional LOCALE TAG-SET HOW-TO-ADD).
412
413 SPECIFIER is the specifier to be temporarily modified. VALUE is the
414 instantiator to be temporarily added to SPECIFIER in LOCALE. LOCALE,
415 TAG-SET and HOW-TO-ADD have the same meaning as in
416 `add-spec-to-specifier'.
417
418 The code resulting from macro expansion will add specifications to
419 specifiers using `add-spec-to-specifier'. After BODY is finished, the
420 temporary specifications are removed and old spec-lists are restored.
421
422 LOCALE, TAG-SET and HOW-TO-ADD may be omitted, and default to nil.
435 The value of the last form in BODY is returned. 423 The value of the last form in BODY is returned.
436 Each element of SPECIFIER-LIST should be a list of 424
437 \(SPECIFIER VALUE DOMAIN). VALUE and DOMAIN may be omitted, and default 425 NOTE: If you want the specifier's instance to change in all
438 to nil. The elements of SPECIFIER-LIST are evaluated sequentially. 426 circumstances, use (selected-window) as the LOCALE. If LOCALE is nil
439 427 or omitted, it defaults to `global'.
440 For meaning of DOMAIN, see `specifier-instance'." 428
441 ;; Error-checking 429 Example:
442 (dolist (listel specifier-list) 430 (let-specifier ((modeline-shadow-thickness 0 (selected-window)))
443 (or (and (consp listel) 431 (sit-for 1))"
444 (<= (length listel) 3)) 432 (check-argument-type 'listp specifier-list)
445 (signal 'error (list "Should be a 3-element list" listel)))) 433 (flet ((gensym-frob (x name)
446 ;; Set up fresh symbols to avoid name clashes. 434 (if (or (atom x) (eq (car x) 'quote))
447 (let* ((specvarlist (mapcar #'(lambda (ignored) (gensym "specifier-")) 435 (list x)
448 specifier-list)) 436 (list (gensym name) x))))
449 (valvarlist (mapcar #'(lambda (ignored) (gensym "value-")) 437 ;; VARLIST is a list of
450 specifier-list)) 438 ;; ((SPECIFIERSYM SPECIFIER) (VALUE) (LOCALESYM LOCALE)
451 (domvarlist (mapcar #'(lambda (ignored) (gensym "domain-")) 439 ;; (TAG-SET) (HOW-TO-ADD))
452 specifier-list)) 440 ;; If any of these is an atom, then a separate symbol is
453 (oldvarlist (mapcar #'(lambda (ignored) (gensym "old-")) 441 ;; unnecessary, the CAR will contain the atom and CDR will be nil.
454 specifier-list))) 442 (let* ((varlist (mapcar #'(lambda (listel)
455 ;; Bind the appropriate variables. 443 (or (and (consp listel)
456 `(let* (,@(mapcar* (lambda (symbol listel) 444 (<= (length listel) 5)
457 (list symbol (nth 0 listel))) 445 (> (length listel) 1))
458 specvarlist specifier-list) 446 (signal 'error
459 ,@(mapcar* (lambda (symbol listel) 447 (list
460 (list symbol (nth 1 listel))) 448 "should be a list of 2-5 elements"
461 valvarlist specifier-list) 449 listel)))
462 ,@(mapcar* (lambda (symbol listel) 450 ;; VALUE, TAG-SET and HOW-TO-ADD are
463 (list symbol (nth 2 listel))) 451 ;; referenced only once, so we needn't
464 domvarlist specifier-list) 452 ;; frob them with gensym.
465 ,@(mapcar* (lambda (symbol specifier domain) 453 (list (gensym-frob (nth 0 listel) "specifier-")
466 (list symbol `(specifier-spec-list 454 (list (nth 1 listel))
467 ,specifier ,domain))) 455 (gensym-frob (nth 2 listel) "locale-")
468 oldvarlist specvarlist domvarlist)) 456 (list (nth 3 listel))
469 (unwind-protect 457 (list (nth 4 listel))))
470 (progn 458 specifier-list))
471 ,@(mapcar* (lambda (specifier value domain) 459 ;; OLDVALLIST is a list of (OLDVALSYM OLDVALFORM)
472 `(add-spec-to-specifier 460 (oldvallist (mapcar #'(lambda (varel)
473 ,specifier ,value ,domain 461 (list (gensym "old-")
474 nil 'prepend)) 462 `(specifier-spec-list
475 specvarlist valvarlist domvarlist) 463 ,(car (nth 0 varel))
476 ,@body) 464 ,(car (nth 2 varel)))))
477 ,@(apply 465 varlist)))
478 #'nconc 466 ;; Bind the appropriate variables.
479 ;; Reverse the unwinding order for marginal safety gain. 467 `(let* (,@(mapcan #'(lambda (varel)
480 (nreverse 468 (delq nil (mapcar
481 (mapcar* 469 #'(lambda (varcons)
482 (lambda (specifier domain oldvalue) 470 (and (cdr varcons) varcons))
483 `((remove-specifier ,specifier ,domain) 471 varel)))
484 (add-spec-list-to-specifier ,specifier ,oldvalue))) 472 varlist)
485 specvarlist domvarlist oldvarlist))))))) 473 ,@oldvallist)
486 474 (unwind-protect
487 ;; (cl-prettyexpand '(let-specifier ((modeline-shadow-thickness 0 (selected-window)) (fubar 0 baz)) (sit-for 1))) 475 (progn
476 ,@(mapcar #'(lambda (varel)
477 `(add-spec-to-specifier
478 ,(car (nth 0 varel)) ,(car (nth 1 varel))
479 ,(car (nth 2 varel)) ,(car (nth 3 varel))
480 ,(car (nth 4 varel))))
481 varlist)
482 ,@body)
483 ;; Reverse the unwinding order, so that using the same
484 ;; specifier multiple times works.
485 ,@(apply #'nconc (nreverse (mapcar*
486 #'(lambda (oldval varel)
487 `((remove-specifier
488 ,(car (nth 0 varel))
489 ,(car (nth 2 varel)))
490 (add-spec-list-to-specifier
491 ,(car (nth 0 varel))
492 ,(car oldval))))
493 oldvallist varlist))))))))
494
495 ;; Evaluate this for testing:
496 ; (cl-prettyexpand '(let-specifier ((modeline-shadow-thickness 0 (selected-window) 'x) (fubar (value) baz)) (sit-for 1)))
488 497
489 (define-specifier-tag 'win 'device-on-window-system-p) 498 (define-specifier-tag 'win 'device-on-window-system-p)
490 499
491 ;; Add tags for device types that don't have support compiled 500 ;; Add tags for device types that don't have support compiled
492 ;; into the binary that we're about to dump. This will prevent 501 ;; into the binary that we're about to dump. This will prevent