
| Current Path : /proc/thread-self/root/usr/share/common-lisp/source/kmrcl/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : //proc/thread-self/root/usr/share/common-lisp/source/kmrcl/macros.lisp |
;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
;;;; *************************************************************************
;;;; FILE IDENTIFICATION
;;;;
;;;; Name: gentils.lisp
;;;; Purpose: Main general utility functions for KMRCL package
;;;; Programmer: Kevin M. Rosenberg
;;;; Date Started: Apr 2000
;;;;
;;;; This file, part of KMRCL, is Copyright (c) 2002 by Kevin M. Rosenberg
;;;;
;;;; KMRCL users are granted the rights to distribute and use this software
;;;; as governed by the terms of the Lisp Lesser GNU Public License
;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
;;;; *************************************************************************
(in-package #:kmrcl)
(defmacro let-when ((var test-form) &body body)
`(let ((,var ,test-form))
(when ,var ,@body)))
(defmacro let-if ((var test-form) if-true &optional if-false)
`(let ((,var ,test-form))
(if ,var ,if-true ,if-false)))
;; Anaphoric macros
(defmacro aif (test then &optional else)
`(let ((it ,test))
(if it ,then ,else)))
(defmacro awhen (test-form &body body)
`(aif ,test-form
(progn ,@body)))
(defmacro awhile (expr &body body)
`(do ((it ,expr ,expr))
((not it))
,@body))
(defmacro aand (&rest args)
(cond ((null args) t)
((null (cdr args)) (car args))
(t `(aif ,(car args) (aand ,@(cdr args))))))
(defmacro acond (&rest clauses)
(if (null clauses)
nil
(let ((cl1 (car clauses))
(sym (gensym)))
`(let ((,sym ,(car cl1)))
(if ,sym
(let ((it ,sym)) ,@(cdr cl1))
(acond ,@(cdr clauses)))))))
(defmacro alambda (parms &body body)
`(labels ((self ,parms ,@body))
#'self))
(defmacro aif2 (test &optional then else)
(let ((win (gensym)))
`(multiple-value-bind (it ,win) ,test
(if (or it ,win) ,then ,else))))
(defmacro awhen2 (test &body body)
`(aif2 ,test
(progn ,@body)))
(defmacro awhile2 (test &body body)
(let ((flag (gensym)))
`(let ((,flag t))
(while ,flag
(aif2 ,test
(progn ,@body)
(setq ,flag nil))))))
(defmacro acond2 (&rest clauses)
(if (null clauses)
nil
(let ((cl1 (car clauses))
(val (gensym))
(win (gensym)))
`(multiple-value-bind (,val ,win) ,(car cl1)
(if (or ,val ,win)
(let ((it ,val)) ,@(cdr cl1))
(acond2 ,@(cdr clauses)))))))
(defmacro mac (form &key (stream *standard-output*) (full nil) (width 80)
(downcase t)
&environment env)
(multiple-value-bind (expanded expanded-p)
(funcall (if full #'macroexpand #'macroexpand-1) form env)
(write expanded
:stream stream
:pretty t
:right-margin width
:case (if downcase :downcase :upcase)
:length nil
:level nil
:circle nil
:gensym nil)
(fresh-line stream)
expanded-p))
(defmacro print-form-and-results (form)
(let ((r (gensym "RES-")))
`(let ((r ,form))
(format t "~&~A --> ~S~%" ',form r)
r)))
;;; Loop macros
(defmacro until (test &body body)
`(do ()
(,test)
,@body))
(defmacro while (test &body body)
`(do ()
((not ,test))
,@body))
(defmacro for ((var start stop) &body body)
(let ((gstop (gensym "STOP-")))
`(do ((,var ,start (1+ ,var))
(,gstop ,stop))
((> ,var ,gstop))
,@body)))
(defmacro with-each-stream-line ((var stream) &body body)
(let ((eof (gensym "EOF-"))
(eof-value (gensym "EOF-VALUE-"))
(strm (gensym "STREAM-")))
`(let ((,strm ,stream)
(,eof ',eof-value))
(do ((,var (read-line ,strm nil ,eof) (read-line ,strm nil ,eof)))
((eql ,var ,eof))
,@body))))
(defmacro with-each-file-line ((var file) &body body)
(let ((stream (gensym)))
`(with-open-file (,stream ,file :direction :input)
(with-each-stream-line (,var ,stream)
,@body))))
(defmacro in (obj &rest choices)
(let ((insym (gensym)))
`(let ((,insym ,obj))
(or ,@(mapcar #'(lambda (c) `(eql ,insym ,c))
choices)))))
(defmacro mean (&rest args)
`(/ (+ ,@args) ,(length args)))
(defmacro with-gensyms (syms &body body)
`(let ,(mapcar #'(lambda (s) `(,s (gensym ,(format nil "~A-" s))))
syms)
,@body))
(defmacro time-seconds (&body body)
(let ((t1 (gensym)))
`(let ((,t1 (get-internal-real-time)))
(values
(progn ,@body)
(coerce (/ (- (get-internal-real-time) ,t1)
internal-time-units-per-second)
'double-float)))))
(defmacro time-iterations (n &body body)
(let ((i (gensym))
(count (gensym)))
`(progn
(let ((,count ,n))
(format t "~&Test with ~d iterations: ~W" ,count (quote ,body))
(let ((t1 (get-internal-real-time)))
(dotimes (,i ,count)
,@body)
(let* ((t2 (get-internal-real-time))
(secs (coerce (/ (- t2 t1)
internal-time-units-per-second)
'double-float)))
(format t "~&Total time: ")
(print-seconds secs)
(format t ", time per iteration: ")
(print-seconds (coerce (/ secs ,n) 'double-float))))))))
(defmacro mv-bind (vars form &body body)
`(multiple-value-bind ,vars ,form
,@body))
;; From USENET
(defmacro deflex (var val &optional (doc nil docp))
"Defines a top level (global) lexical VAR with initial value VAL,
which is assigned unconditionally as with DEFPARAMETER. If a DOC
string is provided, it is attached to both the name |VAR| and the
name *STORAGE-FOR-DEFLEX-VAR-|VAR|* as a documentation string of
kind 'VARIABLE. The new VAR will have lexical scope and thus may
be shadowed by LET bindings without affecting its global value."
(let* ((s0 (load-time-value (symbol-name '#:*storage-for-deflex-var-)))
(s1 (symbol-name var))
(p1 (symbol-package var))
(s2 (load-time-value (symbol-name '#:*)))
(backing-var (intern (concatenate 'string s0 s1 s2) p1)))
`(progn
(defparameter ,backing-var ,val ,@(when docp `(,doc)))
,@(when docp
`((setf (documentation ',var 'variable) ,doc)))
(define-symbol-macro ,var ,backing-var))))
(defmacro def-cached-vector (name element-type)
(let ((get-name (concat-symbol "get-" name "-vector"))
(release-name (concat-symbol "release-" name "-vector"))
(table-name (concat-symbol "*cached-" name "-table*"))
(lock-name (concat-symbol "*cached-" name "-lock*")))
`(eval-when (:compile-toplevel :load-toplevel :execute)
(defvar ,table-name (make-hash-table :test 'equal))
(defvar ,lock-name (kmrcl::make-lock ,name))
(defun ,get-name (size)
(kmrcl::with-lock-held (,lock-name)
(let ((buffers (gethash (cons size ,element-type) ,table-name)))
(if buffers
(let ((buffer (pop buffers)))
(setf (gethash (cons size ,element-type) ,table-name) buffers)
buffer)
(make-array size :element-type ,element-type)))))
(defun ,release-name (buffer)
(kmrcl::with-lock-held (,lock-name)
(let ((buffers (gethash (cons (array-total-size buffer)
,element-type)
,table-name)))
(setf (gethash (cons (array-total-size buffer)
,element-type) ,table-name)
(cons buffer buffers))))))))
(defmacro def-cached-instance (name)
(let* ((new-name (concat-symbol "new-" name "-instance"))
(release-name (concat-symbol "release-" name "-instance"))
(cache-name (concat-symbol "*cached-" name "-instance-table*"))
(lock-name (concat-symbol "*cached-" name "-instance-lock*")))
`(eval-when (:compile-toplevel :load-toplevel :execute)
(defvar ,cache-name nil)
(defvar ,lock-name (kmrcl::make-lock ',name))
(defun ,new-name ()
(kmrcl::with-lock-held (,lock-name)
(if ,cache-name
(pop ,cache-name)
(make-instance ',name))))
(defun ,release-name (instance)
(kmrcl::with-lock-held (,lock-name)
(push instance ,cache-name))))))
(defmacro with-ignore-errors (&rest forms)
`(progn
,@(mapcar
(lambda (x) (list 'ignore-errors x))
forms)))
(defmacro ppmx (form)
"Pretty prints the macro expansion of FORM."
`(let* ((exp1 (macroexpand-1 ',form))
(exp (macroexpand exp1))
(*print-circle* nil))
(cond ((equal exp exp1)
(format t "~&Macro expansion:")
(pprint exp))
(t (format t "~&First step of expansion:")
(pprint exp1)
(format t "~%~%Final expansion:")
(pprint exp)))
(format t "~%~%")
(values)))
(defmacro defconstant* (sym value &optional doc)
"Ensure VALUE is evaluated only once."
`(defconstant ,sym (if (boundp ',sym)
(symbol-value ',sym)
,value)
,@(when doc (list doc))))
(defmacro defvar-unbound (sym &optional (doc ""))
"defvar with a documentation string."
`(progn
(defvar ,sym)
(setf (documentation ',sym 'variable) ,doc)))